Browse Source

Remove Wait list screen #3293

pull/3428/head
Benoit Marty 1 week ago
parent
commit
76bc70afa0
  1. 24
      features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt
  2. 14
      features/login/impl/src/main/kotlin/io/element/android/features/login/impl/error/WaitListError.kt
  3. 10
      features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordNode.kt
  4. 16
      features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt
  5. 14
      features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListEvents.kt
  6. 52
      features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListNode.kt
  7. 87
      features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListPresenter.kt
  8. 19
      features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListState.kt
  9. 35
      features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListStateProvider.kt
  10. 143
      features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListView.kt
  11. 114
      features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListPresenterTest.kt
  12. 1
      samples/minimal/src/main/kotlin/io/element/android/samples/minimal/LoginScreen.kt
  13. 3
      screenshots/de/features.login.impl.screens.waitlistscreen_WaitListView_Day_0_de.png
  14. 3
      screenshots/de/features.login.impl.screens.waitlistscreen_WaitListView_Day_1_de.png
  15. 3
      screenshots/de/features.login.impl.screens.waitlistscreen_WaitListView_Day_2_de.png
  16. 3
      screenshots/de/features.login.impl.screens.waitlistscreen_WaitListView_Day_3_de.png
  17. 3
      screenshots/de/features.login.impl.screens.waitlistscreen_WaitListView_Day_4_de.png
  18. 3
      tests/uitests/src/test/snapshots/images/features.login.impl.screens.waitlistscreen_WaitListView_Day_0_en.png
  19. 3
      tests/uitests/src/test/snapshots/images/features.login.impl.screens.waitlistscreen_WaitListView_Day_1_en.png
  20. 3
      tests/uitests/src/test/snapshots/images/features.login.impl.screens.waitlistscreen_WaitListView_Day_2_en.png
  21. 3
      tests/uitests/src/test/snapshots/images/features.login.impl.screens.waitlistscreen_WaitListView_Day_3_en.png
  22. 3
      tests/uitests/src/test/snapshots/images/features.login.impl.screens.waitlistscreen_WaitListView_Day_4_en.png
  23. 3
      tests/uitests/src/test/snapshots/images/features.login.impl.screens.waitlistscreen_WaitListView_Night_0_en.png
  24. 3
      tests/uitests/src/test/snapshots/images/features.login.impl.screens.waitlistscreen_WaitListView_Night_1_en.png
  25. 3
      tests/uitests/src/test/snapshots/images/features.login.impl.screens.waitlistscreen_WaitListView_Night_2_en.png
  26. 3
      tests/uitests/src/test/snapshots/images/features.login.impl.screens.waitlistscreen_WaitListView_Night_3_en.png
  27. 3
      tests/uitests/src/test/snapshots/images/features.login.impl.screens.waitlistscreen_WaitListView_Night_4_en.png

24
features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt

@ -19,7 +19,6 @@ import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.Plugin
import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.operation.newRoot
import com.bumble.appyx.navmodel.backstack.operation.push import com.bumble.appyx.navmodel.backstack.operation.push
import com.bumble.appyx.navmodel.backstack.operation.singleTop import com.bumble.appyx.navmodel.backstack.operation.singleTop
import dagger.assisted.Assisted import dagger.assisted.Assisted
@ -31,10 +30,8 @@ import io.element.android.features.login.impl.accountprovider.AccountProviderDat
import io.element.android.features.login.impl.qrcode.QrCodeLoginFlowNode import io.element.android.features.login.impl.qrcode.QrCodeLoginFlowNode
import io.element.android.features.login.impl.screens.changeaccountprovider.ChangeAccountProviderNode import io.element.android.features.login.impl.screens.changeaccountprovider.ChangeAccountProviderNode
import io.element.android.features.login.impl.screens.confirmaccountprovider.ConfirmAccountProviderNode import io.element.android.features.login.impl.screens.confirmaccountprovider.ConfirmAccountProviderNode
import io.element.android.features.login.impl.screens.loginpassword.LoginFormState
import io.element.android.features.login.impl.screens.loginpassword.LoginPasswordNode import io.element.android.features.login.impl.screens.loginpassword.LoginPasswordNode
import io.element.android.features.login.impl.screens.searchaccountprovider.SearchAccountProviderNode import io.element.android.features.login.impl.screens.searchaccountprovider.SearchAccountProviderNode
import io.element.android.features.login.impl.screens.waitlistscreen.WaitListNode
import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.NodeInputs
@ -112,9 +109,6 @@ class LoginFlowNode @AssistedInject constructor(
@Parcelize @Parcelize
data object LoginPassword : NavTarget data object LoginPassword : NavTarget
@Parcelize
data class WaitList(val loginFormState: LoginFormState) : NavTarget
@Parcelize @Parcelize
data class OidcView(val oidcDetails: OidcDetails) : NavTarget data class OidcView(val oidcDetails: OidcDetails) : NavTarget
} }
@ -181,27 +175,11 @@ class LoginFlowNode @AssistedInject constructor(
createNode<SearchAccountProviderNode>(buildContext, plugins = listOf(callback)) createNode<SearchAccountProviderNode>(buildContext, plugins = listOf(callback))
} }
NavTarget.LoginPassword -> { NavTarget.LoginPassword -> {
val callback = object : LoginPasswordNode.Callback { createNode<LoginPasswordNode>(buildContext)
override fun onWaitListError(loginFormState: LoginFormState) {
backstack.newRoot(NavTarget.WaitList(loginFormState))
}
}
createNode<LoginPasswordNode>(buildContext, plugins = listOf(callback))
} }
is NavTarget.OidcView -> { is NavTarget.OidcView -> {
oidcEntryPoint.createFallbackWebViewNode(this, buildContext, navTarget.oidcDetails.url) oidcEntryPoint.createFallbackWebViewNode(this, buildContext, navTarget.oidcDetails.url)
} }
is NavTarget.WaitList -> {
val inputs = WaitListNode.Inputs(
loginFormState = navTarget.loginFormState,
)
val callback = object : WaitListNode.Callback {
override fun onCancelClick() {
navigateUp()
}
}
createNode<WaitListNode>(buildContext, plugins = listOf(callback, inputs))
}
} }
} }

14
features/login/impl/src/main/kotlin/io/element/android/features/login/impl/error/WaitListError.kt

@ -1,14 +0,0 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only
* Please see LICENSE in the repository root for full details.
*/
package io.element.android.features.login.impl.error
import io.element.android.libraries.core.bool.orFalse
fun Throwable.isWaitListError(): Boolean {
return message?.contains("IO_ELEMENT_X_WAIT_LIST").orFalse()
}

10
features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordNode.kt

@ -12,7 +12,6 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.Plugin
import com.bumble.appyx.core.plugin.plugins
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode import io.element.android.anvilannotations.ContributesNode
@ -24,14 +23,6 @@ class LoginPasswordNode @AssistedInject constructor(
@Assisted plugins: List<Plugin>, @Assisted plugins: List<Plugin>,
private val presenter: LoginPasswordPresenter, private val presenter: LoginPasswordPresenter,
) : Node(buildContext, plugins = plugins) { ) : Node(buildContext, plugins = plugins) {
interface Callback : Plugin {
fun onWaitListError(loginFormState: LoginFormState)
}
private fun onWaitListError(loginFormState: LoginFormState) {
plugins<Callback>().forEach { it.onWaitListError(loginFormState) }
}
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
val state = presenter.present() val state = presenter.present()
@ -39,7 +30,6 @@ class LoginPasswordNode @AssistedInject constructor(
state = state, state = state,
modifier = modifier, modifier = modifier,
onBackClick = ::navigateUp, onBackClick = ::navigateUp,
onWaitListError = ::onWaitListError,
) )
} }
} }

16
features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt

@ -44,7 +44,6 @@ import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.features.login.impl.R import io.element.android.features.login.impl.R
import io.element.android.features.login.impl.error.isWaitListError
import io.element.android.features.login.impl.error.loginError import io.element.android.features.login.impl.error.loginError
import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule
@ -72,7 +71,6 @@ import io.element.android.libraries.ui.strings.CommonStrings
fun LoginPasswordView( fun LoginPasswordView(
state: LoginPasswordState, state: LoginPasswordState,
onBackClick: () -> Unit, onBackClick: () -> Unit,
onWaitListError: (LoginFormState) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val isLoading by remember(state.loginAction) { val isLoading by remember(state.loginAction) {
@ -149,16 +147,9 @@ fun LoginPasswordView(
} }
if (state.loginAction is AsyncData.Failure) { if (state.loginAction is AsyncData.Failure) {
when { LoginErrorDialog(error = state.loginAction.error, onDismiss = {
state.loginAction.error.isWaitListError() -> { state.eventSink(LoginPasswordEvents.ClearError)
onWaitListError(state.formState) })
}
else -> {
LoginErrorDialog(error = state.loginAction.error, onDismiss = {
state.eventSink(LoginPasswordEvents.ClearError)
})
}
}
} }
} }
} }
@ -302,6 +293,5 @@ internal fun LoginPasswordViewPreview(@PreviewParameter(LoginPasswordStateProvid
LoginPasswordView( LoginPasswordView(
state = state, state = state,
onBackClick = {}, onBackClick = {},
onWaitListError = {},
) )
} }

14
features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListEvents.kt

@ -1,14 +0,0 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only
* Please see LICENSE in the repository root for full details.
*/
package io.element.android.features.login.impl.screens.waitlistscreen
sealed interface WaitListEvents {
data object AttemptLogin : WaitListEvents
data object ClearError : WaitListEvents
data object Continue : WaitListEvents
}

52
features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListNode.kt

@ -1,52 +0,0 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only
* Please see LICENSE in the repository root for full details.
*/
package io.element.android.features.login.impl.screens.waitlistscreen
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin
import com.bumble.appyx.core.plugin.plugins
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode
import io.element.android.features.login.impl.screens.loginpassword.LoginFormState
import io.element.android.libraries.architecture.NodeInputs
import io.element.android.libraries.architecture.inputs
import io.element.android.libraries.di.AppScope
@ContributesNode(AppScope::class)
class WaitListNode @AssistedInject constructor(
@Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>,
presenterFactory: WaitListPresenter.Factory,
) : Node(buildContext, plugins = plugins) {
data class Inputs(val loginFormState: LoginFormState) : NodeInputs
private val inputs: Inputs = inputs()
private val presenter = presenterFactory.create(inputs.loginFormState)
interface Callback : Plugin {
fun onCancelClick()
}
private fun onCancelClick() {
plugins<Callback>().forEach { it.onCancelClick() }
}
@Composable
override fun View(modifier: Modifier) {
val state = presenter.present()
WaitListView(
state = state,
onCancelClick = ::onCancelClick,
modifier = modifier
)
}
}

87
features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListPresenter.kt

@ -1,87 +0,0 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only
* Please see LICENSE in the repository root for full details.
*/
package io.element.android.features.login.impl.screens.waitlistscreen
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
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.AsyncData
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
import io.element.android.libraries.matrix.api.core.SessionId
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import timber.log.Timber
class WaitListPresenter @AssistedInject constructor(
@Assisted private val formState: LoginFormState,
private val buildMeta: BuildMeta,
private val authenticationService: MatrixAuthenticationService,
private val defaultLoginUserStory: DefaultLoginUserStory,
) : Presenter<WaitListState> {
@AssistedFactory
interface Factory {
fun create(loginFormState: LoginFormState): WaitListPresenter
}
@Composable
override fun present(): WaitListState {
val coroutineScope = rememberCoroutineScope()
val homeserverUrl = remember {
authenticationService.getHomeserverDetails().value?.url ?: "server"
}
val loginAction: MutableState<AsyncData<SessionId>> = remember {
mutableStateOf(AsyncData.Uninitialized)
}
val attemptNumber = remember { mutableIntStateOf(0) }
fun handleEvents(event: WaitListEvents) {
when (event) {
WaitListEvents.AttemptLogin -> {
// Do not attempt to login on first resume of the View.
attemptNumber.intValue++
if (attemptNumber.intValue > 1) {
coroutineScope.loginAttempt(formState, loginAction)
}
}
WaitListEvents.ClearError -> loginAction.value = AsyncData.Uninitialized
WaitListEvents.Continue -> defaultLoginUserStory.setLoginFlowIsDone(true)
}
}
return WaitListState(
appName = buildMeta.applicationName,
serverName = homeserverUrl,
loginAction = loginAction.value,
eventSink = ::handleEvents
)
}
private fun CoroutineScope.loginAttempt(formState: LoginFormState, loggedInState: MutableState<AsyncData<SessionId>>) = launch {
Timber.w("Attempt to login...")
loggedInState.value = AsyncData.Loading()
authenticationService.login(formState.login.trim(), formState.password)
.onSuccess { sessionId ->
loggedInState.value = AsyncData.Success(sessionId)
}
.onFailure { failure ->
loggedInState.value = AsyncData.Failure(failure)
}
}
}

19
features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListState.kt

@ -1,19 +0,0 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only
* Please see LICENSE in the repository root for full details.
*/
package io.element.android.features.login.impl.screens.waitlistscreen
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.matrix.api.core.SessionId
// Do not use default value, so no member get forgotten in the presenters.
data class WaitListState(
val appName: String,
val serverName: String,
val loginAction: AsyncData<SessionId>,
val eventSink: (WaitListEvents) -> Unit
)

35
features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListStateProvider.kt

@ -1,35 +0,0 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only
* Please see LICENSE in the repository root for full details.
*/
package io.element.android.features.login.impl.screens.waitlistscreen
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.matrix.api.core.SessionId
open class WaitListStateProvider : PreviewParameterProvider<WaitListState> {
override val values: Sequence<WaitListState>
get() = sequenceOf(
aWaitListState(loginAction = AsyncData.Uninitialized),
aWaitListState(loginAction = AsyncData.Loading()),
aWaitListState(loginAction = AsyncData.Failure(Throwable("error"))),
aWaitListState(loginAction = AsyncData.Failure(Throwable(message = "IO_ELEMENT_X_WAIT_LIST"))),
aWaitListState(loginAction = AsyncData.Success(SessionId("@alice:element.io"))),
// Add other state here
)
}
fun aWaitListState(
appName: String = "Element X",
serverName: String = "server.org",
loginAction: AsyncData<SessionId> = AsyncData.Uninitialized,
) = WaitListState(
appName = appName,
serverName = serverName,
loginAction = loginAction,
eventSink = {}
)

143
features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListView.kt

@ -1,143 +0,0 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only
* Please see LICENSE in the repository root for full details.
*/
package io.element.android.features.login.impl.screens.waitlistscreen
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.LocalContentColor
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import androidx.lifecycle.Lifecycle
import io.element.android.compound.theme.ElementTheme
import io.element.android.features.login.impl.R
import io.element.android.features.login.impl.error.isWaitListError
import io.element.android.features.login.impl.error.loginError
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.designsystem.atomic.pages.SunsetPage
import io.element.android.libraries.designsystem.components.dialogs.RetryDialog
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.TextButton
import io.element.android.libraries.designsystem.utils.OnLifecycleEvent
import io.element.android.libraries.ui.strings.CommonStrings
// Ref: https://www.figma.com/file/0MMNu7cTOzLOlWb7ctTkv3/Element-X?type=design&node-id=6761-148425
// Only the first screen can be displayed, since once logged in, this Node will be remove by the RootNode.
@Composable
fun WaitListView(
state: WaitListState,
onCancelClick: () -> Unit,
modifier: Modifier = Modifier,
) {
OnLifecycleEvent { _, event ->
when (event) {
Lifecycle.Event.ON_RESUME -> state.eventSink.invoke(WaitListEvents.AttemptLogin)
else -> Unit
}
}
WaitListContent(state, onCancelClick, modifier)
}
@Composable
private fun WaitListError(state: WaitListState) {
// Display a dialog for error other than the waitlist error
state.loginAction.errorOrNull()?.let { error ->
if (error.isWaitListError().not()) {
RetryDialog(
content = stringResource(id = loginError(error)),
onRetry = {
state.eventSink.invoke(WaitListEvents.AttemptLogin)
},
onDismiss = {
state.eventSink.invoke(WaitListEvents.ClearError)
}
)
}
}
}
@Composable
private fun WaitListContent(
state: WaitListState,
onCancelClick: () -> Unit,
modifier: Modifier = Modifier,
) {
Box(
modifier = modifier.fillMaxSize(),
) {
val title = stringResource(
when (state.loginAction) {
is AsyncData.Success -> R.string.screen_waitlist_title_success
else -> R.string.screen_waitlist_title
}
)
val subtitle = when (state.loginAction) {
is AsyncData.Success -> stringResource(
id = R.string.screen_waitlist_message_success,
state.appName,
)
else -> stringResource(
id = R.string.screen_waitlist_message,
state.appName,
state.serverName,
)
}
SunsetPage(
isLoading = state.loginAction.isLoading(),
title = title,
subtitle = subtitle,
) {
OverallContent(state, onCancelClick)
}
WaitListError(state)
}
}
@Composable
private fun OverallContent(
state: WaitListState,
onCancelClick: () -> Unit,
) {
Box(modifier = Modifier.fillMaxSize()) {
if (state.loginAction !is AsyncData.Success) {
CompositionLocalProvider(LocalContentColor provides ElementTheme.colors.textOnSolidPrimary) {
TextButton(
text = stringResource(CommonStrings.action_cancel),
onClick = onCancelClick,
)
}
}
if (state.loginAction is AsyncData.Success) {
Button(
text = stringResource(id = CommonStrings.action_continue),
onClick = { state.eventSink.invoke(WaitListEvents.Continue) },
modifier = Modifier
.fillMaxWidth()
.align(Alignment.BottomCenter)
.padding(bottom = 8.dp),
)
}
}
}
@PreviewsDayNight
@Composable
internal fun WaitListViewPreview(@PreviewParameter(WaitListStateProvider::class) state: WaitListState) = ElementPreview {
WaitListView(
state = state,
onCancelClick = {},
)
}

114
features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListPresenterTest.kt

@ -1,114 +0,0 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only
* Please see LICENSE in the repository root for full details.
*/
package io.element.android.features.login.impl.screens.waitlistscreen
import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
import io.element.android.features.login.impl.DefaultLoginUserStory
import io.element.android.features.login.impl.screens.loginpassword.LoginFormState
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.test.A_HOMESERVER
import io.element.android.libraries.matrix.test.A_HOMESERVER_URL
import io.element.android.libraries.matrix.test.A_THROWABLE
import io.element.android.libraries.matrix.test.A_USER_ID
import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService
import io.element.android.libraries.matrix.test.core.aBuildMeta
import io.element.android.tests.testutils.WarmUpRule
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
class WaitListPresenterTest {
@get:Rule
val warmUpRule = WarmUpRule()
@Test
fun `present - initial state`() = runTest {
val authenticationService = FakeMatrixAuthenticationService().apply {
givenHomeserver(A_HOMESERVER)
}
val loginUserStory = DefaultLoginUserStory()
val presenter = WaitListPresenter(
LoginFormState.Default,
aBuildMeta(applicationName = "Application Name"),
authenticationService,
loginUserStory,
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
assertThat(initialState.appName).isEqualTo("Application Name")
assertThat(initialState.serverName).isEqualTo(A_HOMESERVER_URL)
assertThat(initialState.loginAction).isEqualTo(AsyncData.Uninitialized)
}
}
@Test
fun `present - attempt login with error`() = runTest {
val authenticationService = FakeMatrixAuthenticationService().apply {
givenLoginError(A_THROWABLE)
}
val loginUserStory = DefaultLoginUserStory()
val presenter = WaitListPresenter(
LoginFormState.Default,
aBuildMeta(),
authenticationService,
loginUserStory,
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
// First usage of AttemptLogin, nothing should happen
initialState.eventSink.invoke(WaitListEvents.AttemptLogin)
expectNoEvents()
initialState.eventSink.invoke(WaitListEvents.AttemptLogin)
val submitState = awaitItem()
assertThat(submitState.loginAction).isInstanceOf(AsyncData.Loading::class.java)
val errorState = awaitItem()
assertThat(errorState.loginAction).isEqualTo(AsyncData.Failure<SessionId>(A_THROWABLE))
// Assert the error can be cleared
errorState.eventSink(WaitListEvents.ClearError)
val clearedState = awaitItem()
assertThat(clearedState.loginAction).isEqualTo(AsyncData.Uninitialized)
}
}
@Test
fun `present - attempt login with success`() = runTest {
val authenticationService = FakeMatrixAuthenticationService()
val loginUserStory = DefaultLoginUserStory().apply { setLoginFlowIsDone(false) }
val presenter = WaitListPresenter(
LoginFormState.Default,
aBuildMeta(),
authenticationService,
loginUserStory,
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
assertThat(loginUserStory.loginFlowIsDone.value).isFalse()
val initialState = awaitItem()
// First usage of AttemptLogin, nothing should happen
initialState.eventSink.invoke(WaitListEvents.AttemptLogin)
expectNoEvents()
initialState.eventSink.invoke(WaitListEvents.AttemptLogin)
val submitState = awaitItem()
assertThat(submitState.loginAction).isInstanceOf(AsyncData.Loading::class.java)
val successState = awaitItem()
assertThat(successState.loginAction).isEqualTo(AsyncData.Success(A_USER_ID))
assertThat(loginUserStory.loginFlowIsDone.value).isFalse()
successState.eventSink.invoke(WaitListEvents.Continue)
assertThat(loginUserStory.loginFlowIsDone.value).isTrue()
}
}
}

1
samples/minimal/src/main/kotlin/io/element/android/samples/minimal/LoginScreen.kt

@ -38,7 +38,6 @@ class LoginScreen(private val authenticationService: MatrixAuthenticationService
state = state, state = state,
modifier = modifier, modifier = modifier,
onBackClick = {}, onBackClick = {},
onWaitListError = {},
) )
} }
} }

3
screenshots/de/features.login.impl.screens.waitlistscreen_WaitListView_Day_0_de.png

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b2c858bc7e2beecc0d4c92df0b4ac61e1e3a975a072e0e75cfa1da6aaa32142c
size 140926

3
screenshots/de/features.login.impl.screens.waitlistscreen_WaitListView_Day_1_de.png

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8524bdd37ac6eb784d82041b572d9cf3bb69cf3de318ba1d8abc45e9a239dad1
size 141726

3
screenshots/de/features.login.impl.screens.waitlistscreen_WaitListView_Day_2_de.png

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a4fd2455de9a763e582e4977e36686c714dfb380737fc6193b4eab9ef8b64de9
size 58641

3
screenshots/de/features.login.impl.screens.waitlistscreen_WaitListView_Day_3_de.png

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b2c858bc7e2beecc0d4c92df0b4ac61e1e3a975a072e0e75cfa1da6aaa32142c
size 140926

3
screenshots/de/features.login.impl.screens.waitlistscreen_WaitListView_Day_4_de.png

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f7880a67dd818c44b519be634a4a33a4a0efc842f9ad5e4879f16acb7c8b1f5f
size 122622

3
tests/uitests/src/test/snapshots/images/features.login.impl.screens.waitlistscreen_WaitListView_Day_0_en.png

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fa9162336cb7d46a2517f4b4149a9f8389e69e410e3896cb6b973fc16a940adf
size 138058

3
tests/uitests/src/test/snapshots/images/features.login.impl.screens.waitlistscreen_WaitListView_Day_1_en.png

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ef852707489db29300b4bf8259a5d71d3c82ce26bcea3ddb00d037b1ba379423
size 138901

3
tests/uitests/src/test/snapshots/images/features.login.impl.screens.waitlistscreen_WaitListView_Day_2_en.png

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5946208785bcc7eff5e76048cd467e9bde6f4ff5874ef2dd2562d5bbb0844f93
size 59177

3
tests/uitests/src/test/snapshots/images/features.login.impl.screens.waitlistscreen_WaitListView_Day_3_en.png

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fa9162336cb7d46a2517f4b4149a9f8389e69e410e3896cb6b973fc16a940adf
size 138058

3
tests/uitests/src/test/snapshots/images/features.login.impl.screens.waitlistscreen_WaitListView_Day_4_en.png

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3b4fe8418f426d9953189908053407550ad6d83d45a548253d75507321f686c9
size 120721

3
tests/uitests/src/test/snapshots/images/features.login.impl.screens.waitlistscreen_WaitListView_Night_0_en.png

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3d81ad9f5a5983653d9266a2500b2c71dc7ca231d52a62d1c1f1b091849ede32
size 165501

3
tests/uitests/src/test/snapshots/images/features.login.impl.screens.waitlistscreen_WaitListView_Night_1_en.png

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1cb4f2cbdc2b7fca643a9debff50206d4aa9321894a86149d6c815fd4db7b593
size 166084

3
tests/uitests/src/test/snapshots/images/features.login.impl.screens.waitlistscreen_WaitListView_Night_2_en.png

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3b942ce79745a26aa2d4227c35aaad442b35c2f768915e3d9fabf45a04cd1d82
size 61267

3
tests/uitests/src/test/snapshots/images/features.login.impl.screens.waitlistscreen_WaitListView_Night_3_en.png

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3d81ad9f5a5983653d9266a2500b2c71dc7ca231d52a62d1c1f1b091849ede32
size 165501

3
tests/uitests/src/test/snapshots/images/features.login.impl.screens.waitlistscreen_WaitListView_Night_4_en.png

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:aa47e394a5af6cad06496a5d36fa81b2e7d76dd9eaec45c3914f1ffce45f9f91
size 146617
Loading…
Cancel
Save