Rename nodes and organize by package into screens subpackage for clarity
This commit is contained in:
parent
06297bb792
commit
dc064069ba
52 changed files with 450 additions and 476 deletions
|
|
@ -32,14 +32,14 @@ import com.bumble.appyx.navmodel.backstack.operation.singleTop
|
||||||
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
|
||||||
import io.element.android.features.login.impl.accountprovider.AccountProviderNode
|
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
|
||||||
import io.element.android.features.login.impl.changeaccountprovider.ChangeAccountProviderNode
|
|
||||||
import io.element.android.features.login.impl.changeaccountprovider.form.ChangeAccountProviderFormNode
|
|
||||||
import io.element.android.features.login.impl.datasource.AccountProviderDataSource
|
|
||||||
import io.element.android.features.login.impl.loginpassword.LoginPasswordNode
|
|
||||||
import io.element.android.features.login.impl.oidc.CustomTabAvailabilityChecker
|
import io.element.android.features.login.impl.oidc.CustomTabAvailabilityChecker
|
||||||
import io.element.android.features.login.impl.oidc.customtab.CustomTabHandler
|
import io.element.android.features.login.impl.oidc.customtab.CustomTabHandler
|
||||||
import io.element.android.features.login.impl.oidc.webview.OidcNode
|
import io.element.android.features.login.impl.oidc.webview.OidcNode
|
||||||
|
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.loginpassword.LoginPasswordNode
|
||||||
|
import io.element.android.features.login.impl.screens.searchaccountprovider.SearchAccountProviderNode
|
||||||
import io.element.android.libraries.architecture.BackstackNode
|
import io.element.android.libraries.architecture.BackstackNode
|
||||||
import io.element.android.libraries.architecture.NodeInputs
|
import io.element.android.libraries.architecture.NodeInputs
|
||||||
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler
|
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler
|
||||||
|
|
@ -59,7 +59,7 @@ class LoginFlowNode @AssistedInject constructor(
|
||||||
private val accountProviderDataSource: AccountProviderDataSource,
|
private val accountProviderDataSource: AccountProviderDataSource,
|
||||||
) : BackstackNode<LoginFlowNode.NavTarget>(
|
) : BackstackNode<LoginFlowNode.NavTarget>(
|
||||||
backstack = BackStack(
|
backstack = BackStack(
|
||||||
initialElement = NavTarget.AccountProvider, // NavTarget.Root,
|
initialElement = NavTarget.ConfirmAccountProvider,
|
||||||
savedStateMap = buildContext.savedStateMap,
|
savedStateMap = buildContext.savedStateMap,
|
||||||
),
|
),
|
||||||
buildContext = buildContext,
|
buildContext = buildContext,
|
||||||
|
|
@ -76,16 +76,16 @@ class LoginFlowNode @AssistedInject constructor(
|
||||||
|
|
||||||
sealed interface NavTarget : Parcelable {
|
sealed interface NavTarget : Parcelable {
|
||||||
@Parcelize
|
@Parcelize
|
||||||
object AccountProvider : NavTarget
|
object ConfirmAccountProvider : NavTarget
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
object ChangeAccountProvider : NavTarget
|
object ChangeAccountProvider : NavTarget
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
object ChangeAccountProviderForm : NavTarget
|
object SearchAccountProvider : NavTarget
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
object LoginPasswordForm : NavTarget
|
object LoginPassword : NavTarget
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class OidcView(val oidcDetails: OidcDetails) : NavTarget
|
data class OidcView(val oidcDetails: OidcDetails) : NavTarget
|
||||||
|
|
@ -93,15 +93,11 @@ class LoginFlowNode @AssistedInject constructor(
|
||||||
|
|
||||||
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
||||||
return when (navTarget) {
|
return when (navTarget) {
|
||||||
is NavTarget.OidcView -> {
|
NavTarget.ConfirmAccountProvider -> {
|
||||||
val input = OidcNode.Inputs(navTarget.oidcDetails)
|
val inputs = ConfirmAccountProviderNode.Inputs(
|
||||||
createNode<OidcNode>(buildContext, plugins = listOf(input))
|
|
||||||
}
|
|
||||||
NavTarget.AccountProvider -> {
|
|
||||||
val inputs = AccountProviderNode.Inputs(
|
|
||||||
isAccountCreation = inputs.isAccountCreation
|
isAccountCreation = inputs.isAccountCreation
|
||||||
)
|
)
|
||||||
val callback = object : AccountProviderNode.Callback {
|
val callback = object : ConfirmAccountProviderNode.Callback {
|
||||||
override fun onOidcDetails(oidcDetails: OidcDetails) {
|
override fun onOidcDetails(oidcDetails: OidcDetails) {
|
||||||
if (customTabAvailabilityChecker.supportCustomTab()) {
|
if (customTabAvailabilityChecker.supportCustomTab()) {
|
||||||
// In this case open a Chrome Custom tab
|
// In this case open a Chrome Custom tab
|
||||||
|
|
@ -113,42 +109,46 @@ class LoginFlowNode @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLoginPasswordNeeded() {
|
override fun onLoginPasswordNeeded() {
|
||||||
backstack.push(NavTarget.LoginPasswordForm)
|
backstack.push(NavTarget.LoginPassword)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onChangeAccountProvider() {
|
override fun onChangeAccountProvider() {
|
||||||
backstack.push(NavTarget.ChangeAccountProvider)
|
backstack.push(NavTarget.ChangeAccountProvider)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
createNode<AccountProviderNode>(buildContext, plugins = listOf(inputs, callback))
|
createNode<ConfirmAccountProviderNode>(buildContext, plugins = listOf(inputs, callback))
|
||||||
}
|
}
|
||||||
NavTarget.ChangeAccountProvider -> {
|
NavTarget.ChangeAccountProvider -> {
|
||||||
val callback = object : ChangeAccountProviderNode.Callback {
|
val callback = object : ChangeAccountProviderNode.Callback {
|
||||||
override fun onDone() {
|
override fun onDone() {
|
||||||
// Go back to the Account Provider screen
|
// Go back to the Account Provider screen
|
||||||
backstack.singleTop(NavTarget.AccountProvider)
|
backstack.singleTop(NavTarget.ConfirmAccountProvider)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOtherClicked() {
|
override fun onOtherClicked() {
|
||||||
backstack.push(NavTarget.ChangeAccountProviderForm)
|
backstack.push(NavTarget.SearchAccountProvider)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createNode<ChangeAccountProviderNode>(buildContext, plugins = listOf(callback))
|
createNode<ChangeAccountProviderNode>(buildContext, plugins = listOf(callback))
|
||||||
}
|
}
|
||||||
NavTarget.ChangeAccountProviderForm -> {
|
NavTarget.SearchAccountProvider -> {
|
||||||
val callback = object : ChangeAccountProviderFormNode.Callback {
|
val callback = object : SearchAccountProviderNode.Callback {
|
||||||
override fun onDone() {
|
override fun onDone() {
|
||||||
// Go back to the Account Provider screen
|
// Go back to the Account Provider screen
|
||||||
backstack.singleTop(NavTarget.AccountProvider)
|
backstack.singleTop(NavTarget.ConfirmAccountProvider)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createNode<ChangeAccountProviderFormNode>(buildContext, plugins = listOf(callback))
|
createNode<SearchAccountProviderNode>(buildContext, plugins = listOf(callback))
|
||||||
}
|
}
|
||||||
NavTarget.LoginPasswordForm -> {
|
NavTarget.LoginPassword -> {
|
||||||
createNode<LoginPasswordNode>(buildContext, plugins = listOf())
|
createNode<LoginPasswordNode>(buildContext, plugins = listOf())
|
||||||
}
|
}
|
||||||
|
is NavTarget.OidcView -> {
|
||||||
|
val input = OidcNode.Inputs(navTarget.oidcDetails)
|
||||||
|
createNode<OidcNode>(buildContext, plugins = listOf(input))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.accountprovider.item
|
package io.element.android.features.login.impl.accountprovider
|
||||||
|
|
||||||
data class AccountProvider constructor(
|
data class AccountProvider constructor(
|
||||||
val title: String,
|
val title: String,
|
||||||
|
|
@ -14,9 +14,8 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.datasource
|
package io.element.android.features.login.impl.accountprovider
|
||||||
|
|
||||||
import io.element.android.features.login.impl.accountprovider.item.AccountProvider
|
|
||||||
import io.element.android.features.login.impl.util.defaultAccountProvider
|
import io.element.android.features.login.impl.util.defaultAccountProvider
|
||||||
import io.element.android.libraries.di.AppScope
|
import io.element.android.libraries.di.AppScope
|
||||||
import io.element.android.libraries.di.SingleIn
|
import io.element.android.libraries.di.SingleIn
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.accountprovider.item
|
package io.element.android.features.login.impl.accountprovider
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
|
|
||||||
|
|
@ -16,146 +16,117 @@
|
||||||
|
|
||||||
package io.element.android.features.login.impl.accountprovider
|
package io.element.android.features.login.impl.accountprovider
|
||||||
|
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.heightIn
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.AccountCircle
|
import androidx.compose.material.icons.filled.Search
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.derivedStateOf
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.element.android.features.login.impl.R
|
import io.element.android.features.login.impl.R
|
||||||
import io.element.android.features.login.impl.error.ChangeServerError
|
import io.element.android.libraries.designsystem.ElementTextStyles
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtom
|
||||||
import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule
|
import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtomSize
|
||||||
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
|
|
||||||
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
|
|
||||||
import io.element.android.libraries.designsystem.components.button.ButtonWithProgress
|
|
||||||
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
|
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
||||||
|
import io.element.android.libraries.designsystem.theme.components.Divider
|
||||||
|
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||||
import io.element.android.libraries.designsystem.theme.components.Text
|
import io.element.android.libraries.designsystem.theme.components.Text
|
||||||
import io.element.android.libraries.designsystem.theme.components.TextButton
|
|
||||||
import io.element.android.libraries.matrix.api.auth.OidcDetails
|
|
||||||
import io.element.android.libraries.testtags.TestTags
|
|
||||||
import io.element.android.libraries.testtags.testTag
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://www.figma.com/file/o9p34zmiuEpZRyvZXJZAYL/FTUE?type=design&node-id=604-60817
|
||||||
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun AccountProviderView(
|
fun AccountProviderView(
|
||||||
state: AccountProviderState,
|
item: AccountProvider,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
onOidcDetails: (OidcDetails) -> Unit = {},
|
onClick: () -> Unit,
|
||||||
onLoginPasswordNeeded: () -> Unit = {},
|
|
||||||
onLearnMoreClicked: () -> Unit = {},
|
|
||||||
onChange: () -> Unit = {},
|
|
||||||
) {
|
) {
|
||||||
val isLoading by remember(state.loginFlow) {
|
Column(modifier = modifier
|
||||||
derivedStateOf {
|
.fillMaxWidth()
|
||||||
state.loginFlow is Async.Loading
|
.clickable { onClick() }) {
|
||||||
}
|
Divider()
|
||||||
}
|
Column(
|
||||||
val eventSink = state.eventSink
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
HeaderFooterPage(
|
.padding(vertical = 4.dp, horizontal = 16.dp)
|
||||||
modifier = modifier,
|
) {
|
||||||
header = {
|
Row(
|
||||||
IconTitleSubtitleMolecule(
|
modifier = Modifier
|
||||||
modifier = Modifier.padding(top = 60.dp),
|
.fillMaxWidth()
|
||||||
iconImageVector = Icons.Filled.AccountCircle,
|
.heightIn(min = 44.dp),
|
||||||
title = stringResource(
|
verticalAlignment = Alignment.CenterVertically
|
||||||
id = if (state.isAccountCreation) {
|
) {
|
||||||
R.string.screen_account_provider_signup_title
|
if (item.isMatrixOrg) {
|
||||||
} else {
|
RoundedIconAtom(
|
||||||
R.string.screen_account_provider_signin_title
|
size = RoundedIconAtomSize.Medium,
|
||||||
},
|
resourceId = R.drawable.ic_matrix,
|
||||||
state.accountProvider.title
|
tint = Color.Unspecified,
|
||||||
),
|
)
|
||||||
subTitle = stringResource(
|
} else {
|
||||||
id = if (state.isAccountCreation) {
|
RoundedIconAtom(
|
||||||
R.string.screen_account_provider_signup_subtitle
|
size = RoundedIconAtomSize.Medium,
|
||||||
} else {
|
imageVector = Icons.Filled.Search,
|
||||||
// Use same value for now.
|
tint = MaterialTheme.colorScheme.primary,
|
||||||
R.string.screen_account_provider_signup_subtitle
|
)
|
||||||
},
|
}
|
||||||
)
|
Text(
|
||||||
)
|
|
||||||
},
|
|
||||||
footer = {
|
|
||||||
ButtonColumnMolecule {
|
|
||||||
ButtonWithProgress(
|
|
||||||
text = stringResource(id = R.string.screen_account_provider_continue),
|
|
||||||
showProgress = isLoading,
|
|
||||||
onClick = { eventSink.invoke(AccountProviderEvents.Continue) },
|
|
||||||
enabled = state.submitEnabled,
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.padding(start = 16.dp)
|
||||||
.testTag(TestTags.loginContinue)
|
.weight(1f),
|
||||||
|
text = item.title,
|
||||||
|
style = ElementTextStyles.Regular.headline.copy(textAlign = TextAlign.Start),
|
||||||
|
color = MaterialTheme.colorScheme.primary,
|
||||||
)
|
)
|
||||||
TextButton(
|
if (item.isPublic) {
|
||||||
onClick = {
|
Icon(
|
||||||
onChange()
|
modifier = Modifier
|
||||||
},
|
.padding(start = 10.dp)
|
||||||
enabled = true,
|
.size(16.dp),
|
||||||
|
resourceId = R.drawable.ic_public,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = Color.Unspecified,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (item.subtitle != null) {
|
||||||
|
Text(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.padding(start = 46.dp, bottom = 12.dp, end = 26.dp),
|
||||||
.testTag(TestTags.loginChangeServer)
|
text = item.subtitle,
|
||||||
) {
|
style = ElementTextStyles.Regular.subheadline.copy(textAlign = TextAlign.Start),
|
||||||
Text(text = stringResource(id = R.string.screen_account_provider_change))
|
color = MaterialTheme.colorScheme.secondary,
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
) {
|
|
||||||
when (state.loginFlow) {
|
|
||||||
is Async.Failure -> {
|
|
||||||
when (val error = state.loginFlow.error) {
|
|
||||||
is ChangeServerError.InlineErrorMessage -> {
|
|
||||||
ErrorDialog(
|
|
||||||
content = error.message(),
|
|
||||||
onDismiss = {
|
|
||||||
eventSink.invoke(AccountProviderEvents.ClearError)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
is ChangeServerError.SlidingSyncAlert -> {
|
|
||||||
SlidingSyncNotSupportedDialog(onLearnMoreClicked = {
|
|
||||||
onLearnMoreClicked()
|
|
||||||
eventSink(AccountProviderEvents.ClearError)
|
|
||||||
}, onDismiss = {
|
|
||||||
eventSink(AccountProviderEvents.ClearError)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is Async.Loading -> Unit // The Continue button shows the loading state
|
|
||||||
is Async.Success -> {
|
|
||||||
when (val loginFlowState = state.loginFlow.state) {
|
|
||||||
is LoginFlow.OidcFlow -> onOidcDetails(loginFlowState.oidcDetails)
|
|
||||||
LoginFlow.PasswordLogin -> onLoginPasswordNeeded()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Async.Uninitialized -> Unit
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
fun AccountProviderViewLightPreview(@PreviewParameter(AccountProviderStateProvider::class) state: AccountProviderState) =
|
fun AccountProviderViewLightPreview(@PreviewParameter(AccountProviderProvider::class) item: AccountProvider) =
|
||||||
ElementPreviewLight { ContentToPreview(state) }
|
ElementPreviewLight { ContentToPreview(item) }
|
||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
fun AccountProviderViewDarkPreview(@PreviewParameter(AccountProviderStateProvider::class) state: AccountProviderState) =
|
fun AccountProviderViewDarkPreview(@PreviewParameter(AccountProviderProvider::class) item: AccountProvider) =
|
||||||
ElementPreviewDark { ContentToPreview(state) }
|
ElementPreviewDark { ContentToPreview(item) }
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ContentToPreview(state: AccountProviderState) {
|
private fun ContentToPreview(item: AccountProvider) {
|
||||||
AccountProviderView(
|
AccountProviderView(
|
||||||
state = state,
|
item = item,
|
||||||
|
onClick = { }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,132 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.accountprovider.item
|
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.foundation.layout.heightIn
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.layout.size
|
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.filled.Search
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import io.element.android.features.login.impl.R
|
|
||||||
import io.element.android.libraries.designsystem.ElementTextStyles
|
|
||||||
import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtom
|
|
||||||
import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtomSize
|
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
|
||||||
import io.element.android.libraries.designsystem.theme.components.Divider
|
|
||||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
|
||||||
import io.element.android.libraries.designsystem.theme.components.Text
|
|
||||||
|
|
||||||
/**
|
|
||||||
* https://www.figma.com/file/o9p34zmiuEpZRyvZXJZAYL/FTUE?type=design&node-id=604-60817
|
|
||||||
*/
|
|
||||||
@Composable
|
|
||||||
fun AccountProviderView(
|
|
||||||
item: AccountProvider,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
onClick: () -> Unit,
|
|
||||||
) {
|
|
||||||
Column(modifier = modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.clickable { onClick() }) {
|
|
||||||
Divider()
|
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(vertical = 4.dp, horizontal = 16.dp)
|
|
||||||
) {
|
|
||||||
Row(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.heightIn(min = 44.dp),
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
if (item.isMatrixOrg) {
|
|
||||||
RoundedIconAtom(
|
|
||||||
size = RoundedIconAtomSize.Medium,
|
|
||||||
resourceId = R.drawable.ic_matrix,
|
|
||||||
tint = Color.Unspecified,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
RoundedIconAtom(
|
|
||||||
size = RoundedIconAtomSize.Medium,
|
|
||||||
imageVector = Icons.Filled.Search,
|
|
||||||
tint = MaterialTheme.colorScheme.primary,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Text(
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(start = 16.dp)
|
|
||||||
.weight(1f),
|
|
||||||
text = item.title,
|
|
||||||
style = ElementTextStyles.Regular.headline.copy(textAlign = TextAlign.Start),
|
|
||||||
color = MaterialTheme.colorScheme.primary,
|
|
||||||
)
|
|
||||||
if (item.isPublic) {
|
|
||||||
Icon(
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(start = 10.dp)
|
|
||||||
.size(16.dp),
|
|
||||||
resourceId = R.drawable.ic_public,
|
|
||||||
contentDescription = null,
|
|
||||||
tint = Color.Unspecified,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (item.subtitle != null) {
|
|
||||||
Text(
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(start = 46.dp, bottom = 12.dp, end = 26.dp),
|
|
||||||
text = item.subtitle,
|
|
||||||
style = ElementTextStyles.Regular.subheadline.copy(textAlign = TextAlign.Start),
|
|
||||||
color = MaterialTheme.colorScheme.secondary,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Preview
|
|
||||||
@Composable
|
|
||||||
fun AccountProviderViewLightPreview(@PreviewParameter(AccountProviderProvider::class) item: AccountProvider) =
|
|
||||||
ElementPreviewLight { ContentToPreview(item) }
|
|
||||||
|
|
||||||
@Preview
|
|
||||||
@Composable
|
|
||||||
fun AccountProviderViewDarkPreview(@PreviewParameter(AccountProviderProvider::class) item: AccountProvider) =
|
|
||||||
ElementPreviewDark { ContentToPreview(item) }
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun ContentToPreview(item: AccountProvider) {
|
|
||||||
AccountProviderView(
|
|
||||||
item = item,
|
|
||||||
onClick = { }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.changeaccountprovider.form
|
|
||||||
|
|
||||||
import io.element.android.libraries.architecture.Async
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolve homeserver base on search terms.
|
|
||||||
*/
|
|
||||||
interface HomeserverResolver {
|
|
||||||
suspend fun resolve(userInput: String): Flow<Async<List<HomeserverData>>>
|
|
||||||
}
|
|
||||||
|
|
@ -14,9 +14,9 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.changeaccountprovider.common
|
package io.element.android.features.login.impl.changeserver
|
||||||
|
|
||||||
import io.element.android.features.login.impl.accountprovider.item.AccountProvider
|
import io.element.android.features.login.impl.accountprovider.AccountProvider
|
||||||
|
|
||||||
sealed interface ChangeServerEvents {
|
sealed interface ChangeServerEvents {
|
||||||
data class ChangeServer(val accountProvider: AccountProvider) : ChangeServerEvents
|
data class ChangeServer(val accountProvider: AccountProvider) : ChangeServerEvents
|
||||||
|
|
@ -14,15 +14,15 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.changeaccountprovider.common
|
package io.element.android.features.login.impl.changeserver
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import io.element.android.features.login.impl.accountprovider.item.AccountProvider
|
import io.element.android.features.login.impl.accountprovider.AccountProvider
|
||||||
import io.element.android.features.login.impl.datasource.AccountProviderDataSource
|
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
|
||||||
import io.element.android.features.login.impl.error.ChangeServerError
|
import io.element.android.features.login.impl.error.ChangeServerError
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.Async
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.changeaccountprovider.common
|
package io.element.android.features.login.impl.changeserver
|
||||||
|
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.Async
|
||||||
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.changeaccountprovider.common
|
package io.element.android.features.login.impl.changeserver
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.Async
|
||||||
|
|
@ -14,13 +14,13 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.changeaccountprovider.common
|
package io.element.android.features.login.impl.changeserver
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import io.element.android.features.login.impl.accountprovider.SlidingSyncNotSupportedDialog
|
import io.element.android.features.login.impl.dialogs.SlidingSyncNotSupportedDialog
|
||||||
import io.element.android.features.login.impl.error.ChangeServerError
|
import io.element.android.features.login.impl.error.ChangeServerError
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.Async
|
||||||
import io.element.android.libraries.designsystem.components.ProgressDialog
|
import io.element.android.libraries.designsystem.components.ProgressDialog
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.changeaccountprovider.form
|
package io.element.android.features.login.impl.changeserver.resolver
|
||||||
|
|
||||||
data class HomeserverData constructor(
|
data class HomeserverData constructor(
|
||||||
// The computed homeserver url, for which a wellknown file has been retrieved, or just a valid Url
|
// The computed homeserver url, for which a wellknown file has been retrieved, or just a valid Url
|
||||||
|
|
@ -14,17 +14,15 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.changeaccountprovider.form
|
package io.element.android.features.login.impl.changeserver.resolver
|
||||||
|
|
||||||
import com.squareup.anvil.annotations.ContributesBinding
|
import io.element.android.features.login.impl.changeserver.resolver.network.WellknownRequest
|
||||||
import io.element.android.features.login.impl.changeaccountprovider.form.network.WellknownRequest
|
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.Async
|
||||||
import io.element.android.libraries.core.bool.orFalse
|
import io.element.android.libraries.core.bool.orFalse
|
||||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||||
import io.element.android.libraries.core.data.tryOrNull
|
import io.element.android.libraries.core.data.tryOrNull
|
||||||
import io.element.android.libraries.core.uri.ensureProtocol
|
import io.element.android.libraries.core.uri.ensureProtocol
|
||||||
import io.element.android.libraries.core.uri.isValidUrl
|
import io.element.android.libraries.core.uri.isValidUrl
|
||||||
import io.element.android.libraries.di.AppScope
|
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.awaitAll
|
import kotlinx.coroutines.awaitAll
|
||||||
import kotlinx.coroutines.currentCoroutineContext
|
import kotlinx.coroutines.currentCoroutineContext
|
||||||
|
|
@ -38,13 +36,12 @@ import javax.inject.Inject
|
||||||
/**
|
/**
|
||||||
* Resolve homeserver base on search terms.
|
* Resolve homeserver base on search terms.
|
||||||
*/
|
*/
|
||||||
@ContributesBinding(AppScope::class)
|
class HomeserverResolver @Inject constructor(
|
||||||
class DefaultHomeserverResolver @Inject constructor(
|
|
||||||
private val dispatchers: CoroutineDispatchers,
|
private val dispatchers: CoroutineDispatchers,
|
||||||
private val wellknownRequest: WellknownRequest,
|
private val wellknownRequest: WellknownRequest,
|
||||||
) : HomeserverResolver {
|
) {
|
||||||
|
|
||||||
override suspend fun resolve(userInput: String): Flow<Async<List<HomeserverData>>> = flow {
|
suspend fun resolve(userInput: String): Flow<Async<List<HomeserverData>>> = flow {
|
||||||
val flowContext = currentCoroutineContext()
|
val flowContext = currentCoroutineContext()
|
||||||
emit(Async.Uninitialized)
|
emit(Async.Uninitialized)
|
||||||
// Debounce
|
// Debounce
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package io.element.android.features.login.impl.changeaccountprovider.form.network
|
package io.element.android.features.login.impl.changeserver.resolver.network
|
||||||
|
|
||||||
import com.squareup.anvil.annotations.ContributesBinding
|
import com.squareup.anvil.annotations.ContributesBinding
|
||||||
import io.element.android.libraries.di.AppScope
|
import io.element.android.libraries.di.AppScope
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.changeaccountprovider.form.network
|
package io.element.android.features.login.impl.changeserver.resolver.network
|
||||||
|
|
||||||
import io.element.android.libraries.core.bool.orFalse
|
import io.element.android.libraries.core.bool.orFalse
|
||||||
import kotlinx.serialization.SerialName
|
import kotlinx.serialization.SerialName
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.changeaccountprovider.form.network
|
package io.element.android.features.login.impl.changeserver.resolver.network
|
||||||
|
|
||||||
import kotlinx.serialization.SerialName
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.changeaccountprovider.form.network
|
package io.element.android.features.login.impl.changeserver.resolver.network
|
||||||
|
|
||||||
import kotlinx.serialization.SerialName
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.changeaccountprovider.form.network
|
package io.element.android.features.login.impl.changeserver.resolver.network
|
||||||
|
|
||||||
import retrofit2.http.GET
|
import retrofit2.http.GET
|
||||||
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package io.element.android.features.login.impl.changeaccountprovider.form.network
|
package io.element.android.features.login.impl.changeserver.resolver.network
|
||||||
|
|
||||||
interface WellknownRequest {
|
interface WellknownRequest {
|
||||||
/**
|
/**
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.accountprovider
|
package io.element.android.features.login.impl.dialogs
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.changeaccountprovider
|
package io.element.android.features.login.impl.screens.changeaccountprovider
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
|
@ -14,11 +14,11 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.changeaccountprovider
|
package io.element.android.features.login.impl.screens.changeaccountprovider
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import io.element.android.features.login.impl.accountprovider.item.AccountProvider
|
import io.element.android.features.login.impl.accountprovider.AccountProvider
|
||||||
import io.element.android.features.login.impl.changeaccountprovider.common.ChangeServerPresenter
|
import io.element.android.features.login.impl.changeserver.ChangeServerPresenter
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
@ -14,10 +14,10 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.changeaccountprovider
|
package io.element.android.features.login.impl.screens.changeaccountprovider
|
||||||
|
|
||||||
import io.element.android.features.login.impl.accountprovider.item.AccountProvider
|
import io.element.android.features.login.impl.accountprovider.AccountProvider
|
||||||
import io.element.android.features.login.impl.changeaccountprovider.common.ChangeServerState
|
import io.element.android.features.login.impl.changeserver.ChangeServerState
|
||||||
|
|
||||||
// Do not use default value, so no member get forgotten in the presenters.
|
// Do not use default value, so no member get forgotten in the presenters.
|
||||||
data class ChangeAccountProviderState constructor(
|
data class ChangeAccountProviderState constructor(
|
||||||
|
|
@ -14,11 +14,11 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.changeaccountprovider
|
package io.element.android.features.login.impl.screens.changeaccountprovider
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.features.login.impl.accountprovider.item.anAccountProvider
|
import io.element.android.features.login.impl.accountprovider.anAccountProvider
|
||||||
import io.element.android.features.login.impl.changeaccountprovider.common.aChangeServerState
|
import io.element.android.features.login.impl.changeserver.aChangeServerState
|
||||||
|
|
||||||
open class ChangeAccountProviderStateProvider : PreviewParameterProvider<ChangeAccountProviderState> {
|
open class ChangeAccountProviderStateProvider : PreviewParameterProvider<ChangeAccountProviderState> {
|
||||||
override val values: Sequence<ChangeAccountProviderState>
|
override val values: Sequence<ChangeAccountProviderState>
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
@file:OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)
|
@file:OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)
|
||||||
|
|
||||||
package io.element.android.features.login.impl.changeaccountprovider
|
package io.element.android.features.login.impl.screens.changeaccountprovider
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
|
@ -40,10 +40,10 @@ import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.element.android.features.login.impl.R
|
import io.element.android.features.login.impl.R
|
||||||
import io.element.android.features.login.impl.accountprovider.item.AccountProvider
|
import io.element.android.features.login.impl.accountprovider.AccountProvider
|
||||||
import io.element.android.features.login.impl.accountprovider.item.AccountProviderView
|
import io.element.android.features.login.impl.accountprovider.AccountProviderView
|
||||||
import io.element.android.features.login.impl.changeaccountprovider.common.ChangeServerEvents
|
import io.element.android.features.login.impl.changeserver.ChangeServerEvents
|
||||||
import io.element.android.features.login.impl.changeaccountprovider.common.ChangeServerView
|
import io.element.android.features.login.impl.changeserver.ChangeServerView
|
||||||
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
|
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
|
||||||
import io.element.android.libraries.designsystem.components.button.BackButton
|
import io.element.android.libraries.designsystem.components.button.BackButton
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
||||||
|
|
@ -14,9 +14,9 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.accountprovider
|
package io.element.android.features.login.impl.screens.confirmaccountprovider
|
||||||
|
|
||||||
sealed interface AccountProviderEvents {
|
sealed interface ConfirmAccountProviderEvents {
|
||||||
object Continue : AccountProviderEvents
|
object Continue : ConfirmAccountProviderEvents
|
||||||
object ClearError : AccountProviderEvents
|
object ClearError : ConfirmAccountProviderEvents
|
||||||
}
|
}
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.accountprovider
|
package io.element.android.features.login.impl.screens.confirmaccountprovider
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
|
@ -33,10 +33,10 @@ import io.element.android.libraries.di.AppScope
|
||||||
import io.element.android.libraries.matrix.api.auth.OidcDetails
|
import io.element.android.libraries.matrix.api.auth.OidcDetails
|
||||||
|
|
||||||
@ContributesNode(AppScope::class)
|
@ContributesNode(AppScope::class)
|
||||||
class AccountProviderNode @AssistedInject constructor(
|
class ConfirmAccountProviderNode @AssistedInject constructor(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
presenterFactory: AccountProviderPresenter.Factory,
|
presenterFactory: ConfirmAccountProviderPresenter.Factory,
|
||||||
) : Node(buildContext, plugins = plugins) {
|
) : Node(buildContext, plugins = plugins) {
|
||||||
|
|
||||||
data class Inputs(
|
data class Inputs(
|
||||||
|
|
@ -45,7 +45,7 @@ class AccountProviderNode @AssistedInject constructor(
|
||||||
|
|
||||||
private val inputs: Inputs = inputs()
|
private val inputs: Inputs = inputs()
|
||||||
private val presenter = presenterFactory.create(
|
private val presenter = presenterFactory.create(
|
||||||
AccountProviderPresenterParams(
|
ConfirmAccountProviderPresenter.Params(
|
||||||
isAccountCreation = inputs.isAccountCreation,
|
isAccountCreation = inputs.isAccountCreation,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
@ -72,7 +72,7 @@ class AccountProviderNode @AssistedInject constructor(
|
||||||
override fun View(modifier: Modifier) {
|
override fun View(modifier: Modifier) {
|
||||||
val state = presenter.present()
|
val state = presenter.present()
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
AccountProviderView(
|
ConfirmAccountProviderView(
|
||||||
state = state,
|
state = state,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
onOidcDetails = ::onOidcDetails,
|
onOidcDetails = ::onOidcDetails,
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.accountprovider
|
package io.element.android.features.login.impl.screens.confirmaccountprovider
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
|
|
@ -26,7 +26,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
import io.element.android.features.login.impl.datasource.AccountProviderDataSource
|
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
|
||||||
import io.element.android.features.login.impl.error.ChangeServerError
|
import io.element.android.features.login.impl.error.ChangeServerError
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.Async
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
|
|
@ -37,23 +37,23 @@ import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
data class AccountProviderPresenterParams(
|
class ConfirmAccountProviderPresenter @AssistedInject constructor(
|
||||||
val isAccountCreation: Boolean,
|
@Assisted private val params: Params,
|
||||||
)
|
|
||||||
|
|
||||||
class AccountProviderPresenter @AssistedInject constructor(
|
|
||||||
@Assisted private val params: AccountProviderPresenterParams,
|
|
||||||
private val accountProviderDataSource: AccountProviderDataSource,
|
private val accountProviderDataSource: AccountProviderDataSource,
|
||||||
private val authenticationService: MatrixAuthenticationService
|
private val authenticationService: MatrixAuthenticationService
|
||||||
) : Presenter<AccountProviderState> {
|
) : Presenter<ConfirmAccountProviderState> {
|
||||||
|
|
||||||
|
data class Params(
|
||||||
|
val isAccountCreation: Boolean,
|
||||||
|
)
|
||||||
|
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
interface Factory {
|
interface Factory {
|
||||||
fun create(params: AccountProviderPresenterParams): AccountProviderPresenter
|
fun create(params: Params): ConfirmAccountProviderPresenter
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun present(): AccountProviderState {
|
override fun present(): ConfirmAccountProviderState {
|
||||||
val accountProvider by accountProviderDataSource.flow().collectAsState()
|
val accountProvider by accountProviderDataSource.flow().collectAsState()
|
||||||
val localCoroutineScope = rememberCoroutineScope()
|
val localCoroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
|
@ -61,16 +61,16 @@ class AccountProviderPresenter @AssistedInject constructor(
|
||||||
mutableStateOf(Async.Uninitialized)
|
mutableStateOf(Async.Uninitialized)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun handleEvents(event: AccountProviderEvents) {
|
fun handleEvents(event: ConfirmAccountProviderEvents) {
|
||||||
when (event) {
|
when (event) {
|
||||||
AccountProviderEvents.Continue -> {
|
ConfirmAccountProviderEvents.Continue -> {
|
||||||
localCoroutineScope.submit(accountProvider.title, loginFlowAction)
|
localCoroutineScope.submit(accountProvider.title, loginFlowAction)
|
||||||
}
|
}
|
||||||
AccountProviderEvents.ClearError -> loginFlowAction.value = Async.Uninitialized
|
ConfirmAccountProviderEvents.ClearError -> loginFlowAction.value = Async.Uninitialized
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return AccountProviderState(
|
return ConfirmAccountProviderState(
|
||||||
accountProvider = accountProvider,
|
accountProvider = accountProvider,
|
||||||
isAccountCreation = params.isAccountCreation,
|
isAccountCreation = params.isAccountCreation,
|
||||||
loginFlow = loginFlowAction.value,
|
loginFlow = loginFlowAction.value,
|
||||||
|
|
@ -14,18 +14,18 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.accountprovider
|
package io.element.android.features.login.impl.screens.confirmaccountprovider
|
||||||
|
|
||||||
import io.element.android.features.login.impl.accountprovider.item.AccountProvider
|
import io.element.android.features.login.impl.accountprovider.AccountProvider
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.Async
|
||||||
import io.element.android.libraries.matrix.api.auth.OidcDetails
|
import io.element.android.libraries.matrix.api.auth.OidcDetails
|
||||||
|
|
||||||
// Do not use default value, so no member get forgotten in the presenters.
|
// Do not use default value, so no member get forgotten in the presenters.
|
||||||
data class AccountProviderState(
|
data class ConfirmAccountProviderState(
|
||||||
val accountProvider: AccountProvider,
|
val accountProvider: AccountProvider,
|
||||||
val isAccountCreation: Boolean,
|
val isAccountCreation: Boolean,
|
||||||
val loginFlow: Async<LoginFlow>,
|
val loginFlow: Async<LoginFlow>,
|
||||||
val eventSink: (AccountProviderEvents) -> Unit
|
val eventSink: (ConfirmAccountProviderEvents) -> Unit
|
||||||
) {
|
) {
|
||||||
val submitEnabled: Boolean get() = accountProvider.title.isNotEmpty() && (loginFlow is Async.Uninitialized || loginFlow is Async.Loading)
|
val submitEnabled: Boolean get() = accountProvider.title.isNotEmpty() && (loginFlow is Async.Uninitialized || loginFlow is Async.Loading)
|
||||||
}
|
}
|
||||||
|
|
@ -14,21 +14,21 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.accountprovider
|
package io.element.android.features.login.impl.screens.confirmaccountprovider
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.features.login.impl.accountprovider.item.anAccountProvider
|
import io.element.android.features.login.impl.accountprovider.anAccountProvider
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.Async
|
||||||
|
|
||||||
open class AccountProviderStateProvider : PreviewParameterProvider<AccountProviderState> {
|
open class ConfirmAccountProviderStateProvider : PreviewParameterProvider<ConfirmAccountProviderState> {
|
||||||
override val values: Sequence<AccountProviderState>
|
override val values: Sequence<ConfirmAccountProviderState>
|
||||||
get() = sequenceOf(
|
get() = sequenceOf(
|
||||||
anAccountProviderState(),
|
aConfirmAccountProviderState(),
|
||||||
// Add other state here
|
// Add other state here
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun anAccountProviderState() = AccountProviderState(
|
fun aConfirmAccountProviderState() = ConfirmAccountProviderState(
|
||||||
accountProvider = anAccountProvider(),
|
accountProvider = anAccountProvider(),
|
||||||
isAccountCreation = false,
|
isAccountCreation = false,
|
||||||
loginFlow = Async.Uninitialized,
|
loginFlow = Async.Uninitialized,
|
||||||
|
|
@ -0,0 +1,162 @@
|
||||||
|
/*
|
||||||
|
* 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.screens.confirmaccountprovider
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.AccountCircle
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.derivedStateOf
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import io.element.android.features.login.impl.R
|
||||||
|
import io.element.android.features.login.impl.dialogs.SlidingSyncNotSupportedDialog
|
||||||
|
import io.element.android.features.login.impl.error.ChangeServerError
|
||||||
|
import io.element.android.libraries.architecture.Async
|
||||||
|
import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule
|
||||||
|
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
|
||||||
|
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
|
||||||
|
import io.element.android.libraries.designsystem.components.button.ButtonWithProgress
|
||||||
|
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
|
||||||
|
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
||||||
|
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
||||||
|
import io.element.android.libraries.designsystem.theme.components.Text
|
||||||
|
import io.element.android.libraries.designsystem.theme.components.TextButton
|
||||||
|
import io.element.android.libraries.matrix.api.auth.OidcDetails
|
||||||
|
import io.element.android.libraries.testtags.TestTags
|
||||||
|
import io.element.android.libraries.testtags.testTag
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ConfirmAccountProviderView(
|
||||||
|
state: ConfirmAccountProviderState,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
onOidcDetails: (OidcDetails) -> Unit = {},
|
||||||
|
onLoginPasswordNeeded: () -> Unit = {},
|
||||||
|
onLearnMoreClicked: () -> Unit = {},
|
||||||
|
onChange: () -> Unit = {},
|
||||||
|
) {
|
||||||
|
val isLoading by remember(state.loginFlow) {
|
||||||
|
derivedStateOf {
|
||||||
|
state.loginFlow is Async.Loading
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val eventSink = state.eventSink
|
||||||
|
|
||||||
|
HeaderFooterPage(
|
||||||
|
modifier = modifier,
|
||||||
|
header = {
|
||||||
|
IconTitleSubtitleMolecule(
|
||||||
|
modifier = Modifier.padding(top = 60.dp),
|
||||||
|
iconImageVector = Icons.Filled.AccountCircle,
|
||||||
|
title = stringResource(
|
||||||
|
id = if (state.isAccountCreation) {
|
||||||
|
R.string.screen_account_provider_signup_title
|
||||||
|
} else {
|
||||||
|
R.string.screen_account_provider_signin_title
|
||||||
|
},
|
||||||
|
state.accountProvider.title
|
||||||
|
),
|
||||||
|
subTitle = stringResource(
|
||||||
|
id = if (state.isAccountCreation) {
|
||||||
|
R.string.screen_account_provider_signup_subtitle
|
||||||
|
} else {
|
||||||
|
// Use same value for now.
|
||||||
|
R.string.screen_account_provider_signup_subtitle
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
footer = {
|
||||||
|
ButtonColumnMolecule {
|
||||||
|
ButtonWithProgress(
|
||||||
|
text = stringResource(id = R.string.screen_account_provider_continue),
|
||||||
|
showProgress = isLoading,
|
||||||
|
onClick = { eventSink.invoke(ConfirmAccountProviderEvents.Continue) },
|
||||||
|
enabled = state.submitEnabled,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.testTag(TestTags.loginContinue)
|
||||||
|
)
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
onChange()
|
||||||
|
},
|
||||||
|
enabled = true,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.testTag(TestTags.loginChangeServer)
|
||||||
|
) {
|
||||||
|
Text(text = stringResource(id = R.string.screen_account_provider_change))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
when (state.loginFlow) {
|
||||||
|
is Async.Failure -> {
|
||||||
|
when (val error = state.loginFlow.error) {
|
||||||
|
is ChangeServerError.InlineErrorMessage -> {
|
||||||
|
ErrorDialog(
|
||||||
|
content = error.message(),
|
||||||
|
onDismiss = {
|
||||||
|
eventSink.invoke(ConfirmAccountProviderEvents.ClearError)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is ChangeServerError.SlidingSyncAlert -> {
|
||||||
|
SlidingSyncNotSupportedDialog(onLearnMoreClicked = {
|
||||||
|
onLearnMoreClicked()
|
||||||
|
eventSink(ConfirmAccountProviderEvents.ClearError)
|
||||||
|
}, onDismiss = {
|
||||||
|
eventSink(ConfirmAccountProviderEvents.ClearError)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is Async.Loading -> Unit // The Continue button shows the loading state
|
||||||
|
is Async.Success -> {
|
||||||
|
when (val loginFlowState = state.loginFlow.state) {
|
||||||
|
is LoginFlow.OidcFlow -> onOidcDetails(loginFlowState.oidcDetails)
|
||||||
|
LoginFlow.PasswordLogin -> onLoginPasswordNeeded()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Async.Uninitialized -> Unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
fun ConfirmAccountProviderViewLightPreview(@PreviewParameter(ConfirmAccountProviderStateProvider::class) state: ConfirmAccountProviderState) =
|
||||||
|
ElementPreviewLight { ContentToPreview(state) }
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
fun ConfirmAccountProviderViewDarkPreview(@PreviewParameter(ConfirmAccountProviderStateProvider::class) state: ConfirmAccountProviderState) =
|
||||||
|
ElementPreviewDark { ContentToPreview(state) }
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ContentToPreview(state: ConfirmAccountProviderState) {
|
||||||
|
ConfirmAccountProviderView(
|
||||||
|
state = state,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.loginpassword
|
package io.element.android.features.login.impl.screens.loginpassword
|
||||||
|
|
||||||
sealed interface LoginPasswordEvents {
|
sealed interface LoginPasswordEvents {
|
||||||
data class SetLogin(val login: String) : LoginPasswordEvents
|
data class SetLogin(val login: String) : LoginPasswordEvents
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.loginpassword
|
package io.element.android.features.login.impl.screens.loginpassword
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.loginpassword
|
package io.element.android.features.login.impl.screens.loginpassword
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
|
|
@ -24,7 +24,7 @@ import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import io.element.android.features.login.impl.datasource.AccountProviderDataSource
|
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.Async
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
|
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
|
||||||
|
|
@ -14,10 +14,10 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.loginpassword
|
package io.element.android.features.login.impl.screens.loginpassword
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import io.element.android.features.login.impl.accountprovider.item.AccountProvider
|
import io.element.android.features.login.impl.accountprovider.AccountProvider
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.Async
|
||||||
import io.element.android.libraries.matrix.api.core.SessionId
|
import io.element.android.libraries.matrix.api.core.SessionId
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
@ -14,10 +14,10 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.loginpassword
|
package io.element.android.features.login.impl.screens.loginpassword
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.features.login.impl.accountprovider.item.anAccountProvider
|
import io.element.android.features.login.impl.accountprovider.anAccountProvider
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.Async
|
||||||
|
|
||||||
open class LoginPasswordStateProvider : PreviewParameterProvider<LoginPasswordState> {
|
open class LoginPasswordStateProvider : PreviewParameterProvider<LoginPasswordState> {
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022 New Vector Ltd
|
* Copyright (c) 2023 New Vector Ltd
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.loginpassword
|
package io.element.android.features.login.impl.screens.loginpassword
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
|
@ -14,11 +14,11 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.changeaccountprovider.form
|
package io.element.android.features.login.impl.screens.searchaccountprovider
|
||||||
|
|
||||||
sealed interface ChangeAccountProviderFormEvents {
|
sealed interface SearchAccountProviderEvents {
|
||||||
/**
|
/**
|
||||||
* The user has typed something, expect to get a list of result in the state.
|
* The user has typed something, expect to get a list of result in the state.
|
||||||
*/
|
*/
|
||||||
data class UserInput(val input: String) : ChangeAccountProviderFormEvents
|
data class UserInput(val input: String) : SearchAccountProviderEvents
|
||||||
}
|
}
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.changeaccountprovider.form
|
package io.element.android.features.login.impl.screens.searchaccountprovider
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
|
@ -30,10 +30,10 @@ import io.element.android.features.login.impl.util.openLearnMorePage
|
||||||
import io.element.android.libraries.di.AppScope
|
import io.element.android.libraries.di.AppScope
|
||||||
|
|
||||||
@ContributesNode(AppScope::class)
|
@ContributesNode(AppScope::class)
|
||||||
class ChangeAccountProviderFormNode @AssistedInject constructor(
|
class SearchAccountProviderNode @AssistedInject constructor(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
private val presenter: ChangeAccountProviderFormPresenter,
|
private val presenter: SearchAccountProviderPresenter,
|
||||||
) : Node(buildContext, plugins = plugins) {
|
) : Node(buildContext, plugins = plugins) {
|
||||||
|
|
||||||
interface Callback : Plugin {
|
interface Callback : Plugin {
|
||||||
|
|
@ -48,7 +48,7 @@ class ChangeAccountProviderFormNode @AssistedInject constructor(
|
||||||
override fun View(modifier: Modifier) {
|
override fun View(modifier: Modifier) {
|
||||||
val state = presenter.present()
|
val state = presenter.present()
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
ChangeAccountProviderFormView(
|
SearchAccountProviderView(
|
||||||
state = state,
|
state = state,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
onBackPressed = ::navigateUp,
|
onBackPressed = ::navigateUp,
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.changeaccountprovider.form
|
package io.element.android.features.login.impl.screens.searchaccountprovider
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
|
@ -23,18 +23,20 @@ import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import io.element.android.features.login.impl.changeaccountprovider.common.ChangeServerPresenter
|
import io.element.android.features.login.impl.changeserver.ChangeServerPresenter
|
||||||
|
import io.element.android.features.login.impl.changeserver.resolver.HomeserverData
|
||||||
|
import io.element.android.features.login.impl.changeserver.resolver.HomeserverResolver
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.Async
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class ChangeAccountProviderFormPresenter @Inject constructor(
|
class SearchAccountProviderPresenter @Inject constructor(
|
||||||
private val homeserverResolver: HomeserverResolver,
|
private val homeserverResolver: HomeserverResolver,
|
||||||
private val changeServerPresenter: ChangeServerPresenter,
|
private val changeServerPresenter: ChangeServerPresenter,
|
||||||
) : Presenter<ChangeAccountProviderFormState> {
|
) : Presenter<SearchAccountProviderState> {
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun present(): ChangeAccountProviderFormState {
|
override fun present(): SearchAccountProviderState {
|
||||||
var userInput by rememberSaveable {
|
var userInput by rememberSaveable {
|
||||||
mutableStateOf("")
|
mutableStateOf("")
|
||||||
}
|
}
|
||||||
|
|
@ -50,15 +52,15 @@ class ChangeAccountProviderFormPresenter @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun handleEvents(event: ChangeAccountProviderFormEvents) {
|
fun handleEvents(event: SearchAccountProviderEvents) {
|
||||||
when (event) {
|
when (event) {
|
||||||
is ChangeAccountProviderFormEvents.UserInput -> {
|
is SearchAccountProviderEvents.UserInput -> {
|
||||||
userInput = event.input
|
userInput = event.input
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ChangeAccountProviderFormState(
|
return SearchAccountProviderState(
|
||||||
userInput = userInput,
|
userInput = userInput,
|
||||||
userInputResult = data,
|
userInputResult = data,
|
||||||
changeServerState = changeServerState,
|
changeServerState = changeServerState,
|
||||||
|
|
@ -14,15 +14,16 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.changeaccountprovider.form
|
package io.element.android.features.login.impl.screens.searchaccountprovider
|
||||||
|
|
||||||
import io.element.android.features.login.impl.changeaccountprovider.common.ChangeServerState
|
import io.element.android.features.login.impl.changeserver.ChangeServerState
|
||||||
|
import io.element.android.features.login.impl.changeserver.resolver.HomeserverData
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.Async
|
||||||
|
|
||||||
// Do not use default value, so no member get forgotten in the presenters.
|
// Do not use default value, so no member get forgotten in the presenters.
|
||||||
data class ChangeAccountProviderFormState(
|
data class SearchAccountProviderState(
|
||||||
val userInput: String,
|
val userInput: String,
|
||||||
val userInputResult: Async<List<HomeserverData>>,
|
val userInputResult: Async<List<HomeserverData>>,
|
||||||
val changeServerState: ChangeServerState,
|
val changeServerState: ChangeServerState,
|
||||||
val eventSink: (ChangeAccountProviderFormEvents) -> Unit
|
val eventSink: (SearchAccountProviderEvents) -> Unit
|
||||||
)
|
)
|
||||||
|
|
@ -14,25 +14,26 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.changeaccountprovider.form
|
package io.element.android.features.login.impl.screens.searchaccountprovider
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.features.login.impl.changeaccountprovider.common.aChangeServerState
|
import io.element.android.features.login.impl.changeserver.aChangeServerState
|
||||||
|
import io.element.android.features.login.impl.changeserver.resolver.HomeserverData
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.Async
|
||||||
|
|
||||||
open class ChangeAccountProviderFormStateProvider : PreviewParameterProvider<ChangeAccountProviderFormState> {
|
open class SearchAccountProviderStateProvider : PreviewParameterProvider<SearchAccountProviderState> {
|
||||||
override val values: Sequence<ChangeAccountProviderFormState>
|
override val values: Sequence<SearchAccountProviderState>
|
||||||
get() = sequenceOf(
|
get() = sequenceOf(
|
||||||
aChangeAccountProviderFormState(),
|
aSearchAccountProviderState(),
|
||||||
aChangeAccountProviderFormState(userInputResult = Async.Success(aHomeserverDataList())),
|
aSearchAccountProviderState(userInputResult = Async.Success(aHomeserverDataList())),
|
||||||
// Add other state here
|
// Add other state here
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun aChangeAccountProviderFormState(
|
fun aSearchAccountProviderState(
|
||||||
userInput: String = "",
|
userInput: String = "",
|
||||||
userInputResult: Async<List<HomeserverData>> = Async.Uninitialized,
|
userInputResult: Async<List<HomeserverData>> = Async.Uninitialized,
|
||||||
) = ChangeAccountProviderFormState(
|
) = SearchAccountProviderState(
|
||||||
userInput = userInput,
|
userInput = userInput,
|
||||||
userInputResult = userInputResult,
|
userInputResult = userInputResult,
|
||||||
changeServerState = aChangeServerState(),
|
changeServerState = aChangeServerState(),
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
@file:OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)
|
@file:OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)
|
||||||
|
|
||||||
package io.element.android.features.login.impl.changeaccountprovider.form
|
package io.element.android.features.login.impl.screens.searchaccountprovider
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
|
@ -50,10 +50,11 @@ import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.element.android.features.login.impl.R
|
import io.element.android.features.login.impl.R
|
||||||
import io.element.android.features.login.impl.accountprovider.item.AccountProvider
|
import io.element.android.features.login.impl.accountprovider.AccountProvider
|
||||||
import io.element.android.features.login.impl.accountprovider.item.AccountProviderView
|
import io.element.android.features.login.impl.accountprovider.AccountProviderView
|
||||||
import io.element.android.features.login.impl.changeaccountprovider.common.ChangeServerEvents
|
import io.element.android.features.login.impl.changeserver.ChangeServerEvents
|
||||||
import io.element.android.features.login.impl.changeaccountprovider.common.ChangeServerView
|
import io.element.android.features.login.impl.changeserver.ChangeServerView
|
||||||
|
import io.element.android.features.login.impl.changeserver.resolver.HomeserverData
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.Async
|
||||||
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
|
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
|
||||||
import io.element.android.libraries.designsystem.components.button.BackButton
|
import io.element.android.libraries.designsystem.components.button.BackButton
|
||||||
|
|
@ -72,8 +73,8 @@ import io.element.android.libraries.testtags.testTag
|
||||||
* https://www.figma.com/file/o9p34zmiuEpZRyvZXJZAYL/FTUE?type=design&node-id=611-61435
|
* https://www.figma.com/file/o9p34zmiuEpZRyvZXJZAYL/FTUE?type=design&node-id=611-61435
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun ChangeAccountProviderFormView(
|
fun SearchAccountProviderView(
|
||||||
state: ChangeAccountProviderFormState,
|
state: SearchAccountProviderState,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
onBackPressed: () -> Unit = {},
|
onBackPressed: () -> Unit = {},
|
||||||
onLearnMoreClicked: () -> Unit = {},
|
onLearnMoreClicked: () -> Unit = {},
|
||||||
|
|
@ -123,7 +124,7 @@ fun ChangeAccountProviderFormView(
|
||||||
.testTag(TestTags.changeServerServer),
|
.testTag(TestTags.changeServerServer),
|
||||||
onValueChange = {
|
onValueChange = {
|
||||||
userInputState = it
|
userInputState = it
|
||||||
eventSink(ChangeAccountProviderFormEvents.UserInput(it))
|
eventSink(SearchAccountProviderEvents.UserInput(it))
|
||||||
},
|
},
|
||||||
keyboardOptions = KeyboardOptions(
|
keyboardOptions = KeyboardOptions(
|
||||||
keyboardType = KeyboardType.Uri,
|
keyboardType = KeyboardType.Uri,
|
||||||
|
|
@ -135,7 +136,7 @@ fun ChangeAccountProviderFormView(
|
||||||
{
|
{
|
||||||
IconButton(onClick = {
|
IconButton(onClick = {
|
||||||
userInputState = ""
|
userInputState = ""
|
||||||
eventSink(ChangeAccountProviderFormEvents.UserInput(""))
|
eventSink(SearchAccountProviderEvents.UserInput(""))
|
||||||
}) {
|
}) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Filled.Close,
|
imageVector = Icons.Filled.Close,
|
||||||
|
|
@ -202,17 +203,17 @@ private fun HomeserverData.toAccountProvider(): AccountProvider {
|
||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
fun ChangeAccountProviderFormViewLightPreview(@PreviewParameter(ChangeAccountProviderFormStateProvider::class) state: ChangeAccountProviderFormState) =
|
fun SearchAccountProviderViewLightPreview(@PreviewParameter(SearchAccountProviderStateProvider::class) state: SearchAccountProviderState) =
|
||||||
ElementPreviewLight { ContentToPreview(state) }
|
ElementPreviewLight { ContentToPreview(state) }
|
||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
fun ChangeAccountProviderFormViewDarkPreview(@PreviewParameter(ChangeAccountProviderFormStateProvider::class) state: ChangeAccountProviderFormState) =
|
fun SearchAccountProviderViewDarkPreview(@PreviewParameter(SearchAccountProviderStateProvider::class) state: SearchAccountProviderState) =
|
||||||
ElementPreviewDark { ContentToPreview(state) }
|
ElementPreviewDark { ContentToPreview(state) }
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ContentToPreview(state: ChangeAccountProviderFormState) {
|
private fun ContentToPreview(state: SearchAccountProviderState) {
|
||||||
ChangeAccountProviderFormView(
|
SearchAccountProviderView(
|
||||||
state = state,
|
state = state,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
package io.element.android.features.login.impl.util
|
package io.element.android.features.login.impl.util
|
||||||
|
|
||||||
import io.element.android.features.login.impl.accountprovider.item.AccountProvider
|
import io.element.android.features.login.impl.accountprovider.AccountProvider
|
||||||
|
|
||||||
object LoginConstants {
|
object LoginConstants {
|
||||||
const val MATRIX_ORG_URL = "matrix.org"
|
const val MATRIX_ORG_URL = "matrix.org"
|
||||||
|
|
|
||||||
|
|
@ -14,14 +14,14 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.changeaccountprovider.common
|
package io.element.android.features.login.impl.changeserver
|
||||||
|
|
||||||
import app.cash.molecule.RecompositionClock
|
import app.cash.molecule.RecompositionClock
|
||||||
import app.cash.molecule.moleculeFlow
|
import app.cash.molecule.moleculeFlow
|
||||||
import app.cash.turbine.test
|
import app.cash.turbine.test
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.login.impl.accountprovider.item.AccountProvider
|
import io.element.android.features.login.impl.accountprovider.AccountProvider
|
||||||
import io.element.android.features.login.impl.datasource.AccountProviderDataSource
|
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.Async
|
||||||
import io.element.android.libraries.matrix.test.A_HOMESERVER
|
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_HOMESERVER_URL
|
||||||
|
|
@ -14,10 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.changeaccountprovider.form
|
package io.element.android.features.login.impl.changeserver.resolver.network
|
||||||
|
|
||||||
import io.element.android.features.login.impl.changeaccountprovider.form.network.WellKnown
|
|
||||||
import io.element.android.features.login.impl.changeaccountprovider.form.network.WellknownRequest
|
|
||||||
|
|
||||||
class FakeWellknownRequest : WellknownRequest {
|
class FakeWellknownRequest : WellknownRequest {
|
||||||
private var resultMap: Map<String, WellKnown> = emptyMap()
|
private var resultMap: Map<String, WellKnown> = emptyMap()
|
||||||
|
|
@ -14,15 +14,15 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.changeaccountprovider
|
package io.element.android.features.login.impl.screens.changeaccountprovider
|
||||||
|
|
||||||
import app.cash.molecule.RecompositionClock
|
import app.cash.molecule.RecompositionClock
|
||||||
import app.cash.molecule.moleculeFlow
|
import app.cash.molecule.moleculeFlow
|
||||||
import app.cash.turbine.test
|
import app.cash.turbine.test
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.login.impl.accountprovider.item.AccountProvider
|
import io.element.android.features.login.impl.accountprovider.AccountProvider
|
||||||
import io.element.android.features.login.impl.changeaccountprovider.common.ChangeServerPresenter
|
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
|
||||||
import io.element.android.features.login.impl.datasource.AccountProviderDataSource
|
import io.element.android.features.login.impl.changeserver.ChangeServerPresenter
|
||||||
import io.element.android.libraries.matrix.test.auth.FakeAuthenticationService
|
import io.element.android.libraries.matrix.test.auth.FakeAuthenticationService
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
@ -14,13 +14,13 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.accountprovider
|
package io.element.android.features.login.impl.screens.confirmaccountprovider
|
||||||
|
|
||||||
import app.cash.molecule.RecompositionClock
|
import app.cash.molecule.RecompositionClock
|
||||||
import app.cash.molecule.moleculeFlow
|
import app.cash.molecule.moleculeFlow
|
||||||
import app.cash.turbine.test
|
import app.cash.turbine.test
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.login.impl.datasource.AccountProviderDataSource
|
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
|
||||||
import io.element.android.features.login.impl.util.defaultAccountProvider
|
import io.element.android.features.login.impl.util.defaultAccountProvider
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.Async
|
||||||
import io.element.android.libraries.matrix.test.A_HOMESERVER
|
import io.element.android.libraries.matrix.test.A_HOMESERVER
|
||||||
|
|
@ -30,11 +30,11 @@ import io.element.android.libraries.matrix.test.auth.FakeAuthenticationService
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
class AccountProviderPresenterTest {
|
class ConfirmAccountProviderPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - initial test`() = runTest {
|
fun `present - initial test`() = runTest {
|
||||||
val presenter = AccountProviderPresenter(
|
val presenter = ConfirmAccountProviderPresenter(
|
||||||
AccountProviderPresenterParams(isAccountCreation = false),
|
ConfirmAccountProviderPresenter.Params(isAccountCreation = false),
|
||||||
AccountProviderDataSource(),
|
AccountProviderDataSource(),
|
||||||
FakeAuthenticationService(),
|
FakeAuthenticationService(),
|
||||||
)
|
)
|
||||||
|
|
@ -52,8 +52,8 @@ class AccountProviderPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - continue password login`() = runTest {
|
fun `present - continue password login`() = runTest {
|
||||||
val authServer = FakeAuthenticationService()
|
val authServer = FakeAuthenticationService()
|
||||||
val presenter = AccountProviderPresenter(
|
val presenter = ConfirmAccountProviderPresenter(
|
||||||
AccountProviderPresenterParams(isAccountCreation = false),
|
ConfirmAccountProviderPresenter.Params(isAccountCreation = false),
|
||||||
AccountProviderDataSource(),
|
AccountProviderDataSource(),
|
||||||
authServer,
|
authServer,
|
||||||
)
|
)
|
||||||
|
|
@ -62,7 +62,7 @@ class AccountProviderPresenterTest {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
initialState.eventSink.invoke(AccountProviderEvents.Continue)
|
initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue)
|
||||||
val loadingState = awaitItem()
|
val loadingState = awaitItem()
|
||||||
assertThat(loadingState.submitEnabled).isTrue()
|
assertThat(loadingState.submitEnabled).isTrue()
|
||||||
assertThat(loadingState.loginFlow).isInstanceOf(Async.Loading::class.java)
|
assertThat(loadingState.loginFlow).isInstanceOf(Async.Loading::class.java)
|
||||||
|
|
@ -76,8 +76,8 @@ class AccountProviderPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - continue oidc`() = runTest {
|
fun `present - continue oidc`() = runTest {
|
||||||
val authServer = FakeAuthenticationService()
|
val authServer = FakeAuthenticationService()
|
||||||
val presenter = AccountProviderPresenter(
|
val presenter = ConfirmAccountProviderPresenter(
|
||||||
AccountProviderPresenterParams(isAccountCreation = false),
|
ConfirmAccountProviderPresenter.Params(isAccountCreation = false),
|
||||||
AccountProviderDataSource(),
|
AccountProviderDataSource(),
|
||||||
authServer,
|
authServer,
|
||||||
)
|
)
|
||||||
|
|
@ -86,7 +86,7 @@ class AccountProviderPresenterTest {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
initialState.eventSink.invoke(AccountProviderEvents.Continue)
|
initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue)
|
||||||
val loadingState = awaitItem()
|
val loadingState = awaitItem()
|
||||||
assertThat(loadingState.submitEnabled).isTrue()
|
assertThat(loadingState.submitEnabled).isTrue()
|
||||||
assertThat(loadingState.loginFlow).isInstanceOf(Async.Loading::class.java)
|
assertThat(loadingState.loginFlow).isInstanceOf(Async.Loading::class.java)
|
||||||
|
|
@ -100,8 +100,8 @@ class AccountProviderPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - submit fails`() = runTest {
|
fun `present - submit fails`() = runTest {
|
||||||
val authServer = FakeAuthenticationService()
|
val authServer = FakeAuthenticationService()
|
||||||
val presenter = AccountProviderPresenter(
|
val presenter = ConfirmAccountProviderPresenter(
|
||||||
AccountProviderPresenterParams(isAccountCreation = false),
|
ConfirmAccountProviderPresenter.Params(isAccountCreation = false),
|
||||||
AccountProviderDataSource(),
|
AccountProviderDataSource(),
|
||||||
authServer,
|
authServer,
|
||||||
)
|
)
|
||||||
|
|
@ -110,7 +110,7 @@ class AccountProviderPresenterTest {
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
authServer.givenChangeServerError(Throwable())
|
authServer.givenChangeServerError(Throwable())
|
||||||
initialState.eventSink.invoke(AccountProviderEvents.Continue)
|
initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue)
|
||||||
skipItems(1) // Loading
|
skipItems(1) // Loading
|
||||||
val failureState = awaitItem()
|
val failureState = awaitItem()
|
||||||
assertThat(failureState.submitEnabled).isFalse()
|
assertThat(failureState.submitEnabled).isFalse()
|
||||||
|
|
@ -121,8 +121,8 @@ class AccountProviderPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - clear error`() = runTest {
|
fun `present - clear error`() = runTest {
|
||||||
val authenticationService = FakeAuthenticationService()
|
val authenticationService = FakeAuthenticationService()
|
||||||
val presenter = AccountProviderPresenter(
|
val presenter = ConfirmAccountProviderPresenter(
|
||||||
AccountProviderPresenterParams(isAccountCreation = false),
|
ConfirmAccountProviderPresenter.Params(isAccountCreation = false),
|
||||||
AccountProviderDataSource(),
|
AccountProviderDataSource(),
|
||||||
authenticationService,
|
authenticationService,
|
||||||
)
|
)
|
||||||
|
|
@ -133,7 +133,7 @@ class AccountProviderPresenterTest {
|
||||||
|
|
||||||
// Submit will return an error
|
// Submit will return an error
|
||||||
authenticationService.givenChangeServerError(A_THROWABLE)
|
authenticationService.givenChangeServerError(A_THROWABLE)
|
||||||
initialState.eventSink(AccountProviderEvents.Continue)
|
initialState.eventSink(ConfirmAccountProviderEvents.Continue)
|
||||||
|
|
||||||
skipItems(1) // Loading
|
skipItems(1) // Loading
|
||||||
|
|
||||||
|
|
@ -142,7 +142,7 @@ class AccountProviderPresenterTest {
|
||||||
assertThat(submittedState.loginFlow).isInstanceOf(Async.Failure::class.java)
|
assertThat(submittedState.loginFlow).isInstanceOf(Async.Failure::class.java)
|
||||||
|
|
||||||
// Assert the error is then cleared
|
// Assert the error is then cleared
|
||||||
submittedState.eventSink(AccountProviderEvents.ClearError)
|
submittedState.eventSink(ConfirmAccountProviderEvents.ClearError)
|
||||||
val clearedState = awaitItem()
|
val clearedState = awaitItem()
|
||||||
assertThat(clearedState.loginFlow).isEqualTo(Async.Uninitialized)
|
assertThat(clearedState.loginFlow).isEqualTo(Async.Uninitialized)
|
||||||
}
|
}
|
||||||
|
|
@ -14,13 +14,13 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.loginpassword
|
package io.element.android.features.login.impl.screens.loginpassword
|
||||||
|
|
||||||
import app.cash.molecule.RecompositionClock
|
import app.cash.molecule.RecompositionClock
|
||||||
import app.cash.molecule.moleculeFlow
|
import app.cash.molecule.moleculeFlow
|
||||||
import app.cash.turbine.test
|
import app.cash.turbine.test
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.login.impl.datasource.AccountProviderDataSource
|
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
|
||||||
import io.element.android.features.login.impl.util.defaultAccountProvider
|
import io.element.android.features.login.impl.util.defaultAccountProvider
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.Async
|
||||||
import io.element.android.libraries.matrix.api.core.SessionId
|
import io.element.android.libraries.matrix.api.core.SessionId
|
||||||
|
|
@ -14,17 +14,19 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.impl.changeaccountprovider.form
|
package io.element.android.features.login.impl.screens.searchaccountprovider
|
||||||
|
|
||||||
import app.cash.molecule.RecompositionClock
|
import app.cash.molecule.RecompositionClock
|
||||||
import app.cash.molecule.moleculeFlow
|
import app.cash.molecule.moleculeFlow
|
||||||
import app.cash.turbine.test
|
import app.cash.turbine.test
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.login.impl.changeaccountprovider.common.ChangeServerPresenter
|
import io.element.android.features.login.impl.changeserver.ChangeServerPresenter
|
||||||
import io.element.android.features.login.impl.changeaccountprovider.form.network.WellKnown
|
import io.element.android.features.login.impl.changeserver.resolver.network.WellKnown
|
||||||
import io.element.android.features.login.impl.changeaccountprovider.form.network.WellKnownBaseConfig
|
import io.element.android.features.login.impl.changeserver.resolver.network.WellKnownBaseConfig
|
||||||
import io.element.android.features.login.impl.changeaccountprovider.form.network.WellKnownSlidingSyncConfig
|
import io.element.android.features.login.impl.changeserver.resolver.network.WellKnownSlidingSyncConfig
|
||||||
import io.element.android.features.login.impl.datasource.AccountProviderDataSource
|
import io.element.android.features.login.impl.changeserver.resolver.HomeserverResolver
|
||||||
|
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
|
||||||
|
import io.element.android.features.login.impl.changeserver.resolver.network.FakeWellknownRequest
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.Async
|
||||||
import io.element.android.libraries.matrix.test.A_HOMESERVER_URL
|
import io.element.android.libraries.matrix.test.A_HOMESERVER_URL
|
||||||
import io.element.android.libraries.matrix.test.auth.FakeAuthenticationService
|
import io.element.android.libraries.matrix.test.auth.FakeAuthenticationService
|
||||||
|
|
@ -32,7 +34,7 @@ import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
class ChangeAccountProviderFormPresenterTest {
|
class SearchAccountProviderPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - initial state`() = runTest {
|
fun `present - initial state`() = runTest {
|
||||||
val fakeWellknownRequest = FakeWellknownRequest()
|
val fakeWellknownRequest = FakeWellknownRequest()
|
||||||
|
|
@ -40,8 +42,8 @@ class ChangeAccountProviderFormPresenterTest {
|
||||||
FakeAuthenticationService(),
|
FakeAuthenticationService(),
|
||||||
AccountProviderDataSource()
|
AccountProviderDataSource()
|
||||||
)
|
)
|
||||||
val presenter = ChangeAccountProviderFormPresenter(
|
val presenter = SearchAccountProviderPresenter(
|
||||||
DefaultHomeserverResolver(testCoroutineDispatchers(), fakeWellknownRequest),
|
HomeserverResolver(testCoroutineDispatchers(), fakeWellknownRequest),
|
||||||
changeServerPresenter
|
changeServerPresenter
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionClock.Immediate) {
|
moleculeFlow(RecompositionClock.Immediate) {
|
||||||
|
|
@ -60,15 +62,15 @@ class ChangeAccountProviderFormPresenterTest {
|
||||||
FakeAuthenticationService(),
|
FakeAuthenticationService(),
|
||||||
AccountProviderDataSource()
|
AccountProviderDataSource()
|
||||||
)
|
)
|
||||||
val presenter = ChangeAccountProviderFormPresenter(
|
val presenter = SearchAccountProviderPresenter(
|
||||||
DefaultHomeserverResolver(testCoroutineDispatchers(), fakeWellknownRequest),
|
HomeserverResolver(testCoroutineDispatchers(), fakeWellknownRequest),
|
||||||
changeServerPresenter
|
changeServerPresenter
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionClock.Immediate) {
|
moleculeFlow(RecompositionClock.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
initialState.eventSink.invoke(ChangeAccountProviderFormEvents.UserInput("test"))
|
initialState.eventSink.invoke(SearchAccountProviderEvents.UserInput("test"))
|
||||||
val withInputState = awaitItem()
|
val withInputState = awaitItem()
|
||||||
assertThat(withInputState.userInput).isEqualTo("test")
|
assertThat(withInputState.userInput).isEqualTo("test")
|
||||||
assertThat(initialState.userInputResult).isEqualTo(Async.Uninitialized)
|
assertThat(initialState.userInputResult).isEqualTo(Async.Uninitialized)
|
||||||
|
|
@ -84,15 +86,15 @@ class ChangeAccountProviderFormPresenterTest {
|
||||||
FakeAuthenticationService(),
|
FakeAuthenticationService(),
|
||||||
AccountProviderDataSource()
|
AccountProviderDataSource()
|
||||||
)
|
)
|
||||||
val presenter = ChangeAccountProviderFormPresenter(
|
val presenter = SearchAccountProviderPresenter(
|
||||||
DefaultHomeserverResolver(testCoroutineDispatchers(), fakeWellknownRequest),
|
HomeserverResolver(testCoroutineDispatchers(), fakeWellknownRequest),
|
||||||
changeServerPresenter
|
changeServerPresenter
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionClock.Immediate) {
|
moleculeFlow(RecompositionClock.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
initialState.eventSink.invoke(ChangeAccountProviderFormEvents.UserInput("https://test.org"))
|
initialState.eventSink.invoke(SearchAccountProviderEvents.UserInput("https://test.org"))
|
||||||
val withInputState = awaitItem()
|
val withInputState = awaitItem()
|
||||||
assertThat(withInputState.userInput).isEqualTo("https://test.org")
|
assertThat(withInputState.userInput).isEqualTo("https://test.org")
|
||||||
assertThat(initialState.userInputResult).isEqualTo(Async.Uninitialized)
|
assertThat(initialState.userInputResult).isEqualTo(Async.Uninitialized)
|
||||||
|
|
@ -119,15 +121,15 @@ class ChangeAccountProviderFormPresenterTest {
|
||||||
FakeAuthenticationService(),
|
FakeAuthenticationService(),
|
||||||
AccountProviderDataSource()
|
AccountProviderDataSource()
|
||||||
)
|
)
|
||||||
val presenter = ChangeAccountProviderFormPresenter(
|
val presenter = SearchAccountProviderPresenter(
|
||||||
DefaultHomeserverResolver(testCoroutineDispatchers(), fakeWellknownRequest),
|
HomeserverResolver(testCoroutineDispatchers(), fakeWellknownRequest),
|
||||||
changeServerPresenter
|
changeServerPresenter
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionClock.Immediate) {
|
moleculeFlow(RecompositionClock.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
initialState.eventSink.invoke(ChangeAccountProviderFormEvents.UserInput("test"))
|
initialState.eventSink.invoke(SearchAccountProviderEvents.UserInput("test"))
|
||||||
val withInputState = awaitItem()
|
val withInputState = awaitItem()
|
||||||
assertThat(withInputState.userInput).isEqualTo("test")
|
assertThat(withInputState.userInput).isEqualTo("test")
|
||||||
assertThat(initialState.userInputResult).isEqualTo(Async.Uninitialized)
|
assertThat(initialState.userInputResult).isEqualTo(Async.Uninitialized)
|
||||||
|
|
@ -154,15 +156,15 @@ class ChangeAccountProviderFormPresenterTest {
|
||||||
FakeAuthenticationService(),
|
FakeAuthenticationService(),
|
||||||
AccountProviderDataSource()
|
AccountProviderDataSource()
|
||||||
)
|
)
|
||||||
val presenter = ChangeAccountProviderFormPresenter(
|
val presenter = SearchAccountProviderPresenter(
|
||||||
DefaultHomeserverResolver(testCoroutineDispatchers(), fakeWellknownRequest),
|
HomeserverResolver(testCoroutineDispatchers(), fakeWellknownRequest),
|
||||||
changeServerPresenter
|
changeServerPresenter
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionClock.Immediate) {
|
moleculeFlow(RecompositionClock.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
initialState.eventSink.invoke(ChangeAccountProviderFormEvents.UserInput("test"))
|
initialState.eventSink.invoke(SearchAccountProviderEvents.UserInput("test"))
|
||||||
val withInputState = awaitItem()
|
val withInputState = awaitItem()
|
||||||
assertThat(withInputState.userInput).isEqualTo("test")
|
assertThat(withInputState.userInput).isEqualTo("test")
|
||||||
assertThat(initialState.userInputResult).isEqualTo(Async.Uninitialized)
|
assertThat(initialState.userInputResult).isEqualTo(Async.Uninitialized)
|
||||||
|
|
@ -20,9 +20,9 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import io.element.android.features.login.impl.datasource.AccountProviderDataSource
|
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
|
||||||
import io.element.android.features.login.impl.loginpassword.LoginPasswordPresenter
|
import io.element.android.features.login.impl.screens.loginpassword.LoginPasswordPresenter
|
||||||
import io.element.android.features.login.impl.loginpassword.LoginPasswordView
|
import io.element.android.features.login.impl.screens.loginpassword.LoginPasswordView
|
||||||
import io.element.android.features.login.impl.util.defaultAccountProvider
|
import io.element.android.features.login.impl.util.defaultAccountProvider
|
||||||
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
|
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue