This commit is contained in:
Benoit Marty 2023-06-07 10:01:41 +02:00
parent 6eeac4dc41
commit 7b6a5d3af4
28 changed files with 99 additions and 92 deletions

View file

@ -33,9 +33,9 @@ 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.AccountProviderNode
import io.element.android.features.login.impl.accountprovider.item.AccountProvider
import io.element.android.features.login.impl.changeaccountprovider.ChangeAccountProviderNode 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.changeaccountprovider.form.ChangeAccountProviderFormNode
import io.element.android.features.login.impl.changeaccountprovider.item.AccountProviderItem
import io.element.android.features.login.impl.changeserver.ChangeServerNode import io.element.android.features.login.impl.changeserver.ChangeServerNode
import io.element.android.features.login.impl.datasource.AccountProviderDataSource 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.loginpassword.LoginPasswordNode
@ -155,7 +155,7 @@ class LoginFlowNode @AssistedInject constructor(
} }
NavTarget.ChangeAccountProvider -> { NavTarget.ChangeAccountProvider -> {
val callback = object : ChangeAccountProviderNode.Callback { val callback = object : ChangeAccountProviderNode.Callback {
override fun onAccountProviderItemClicked(data: AccountProviderItem) { override fun onAccountProviderClicked(data: AccountProvider) {
accountProviderDataSource.userSelection(data) accountProviderDataSource.userSelection(data)
// Go back to the Account Provider screen // Go back to the Account Provider screen
backstack.singleTop(NavTarget.AccountProvider) backstack.singleTop(NavTarget.AccountProvider)
@ -170,7 +170,7 @@ class LoginFlowNode @AssistedInject constructor(
} }
NavTarget.ChangeAccountProviderForm -> { NavTarget.ChangeAccountProviderForm -> {
val callback = object : ChangeAccountProviderFormNode.Callback { val callback = object : ChangeAccountProviderFormNode.Callback {
override fun onAccountProviderItemClicked(data: AccountProviderItem) { override fun onAccountProviderClicked(data: AccountProvider) {
accountProviderDataSource.userSelection(data) accountProviderDataSource.userSelection(data)
// Go back to the Account Provider screen // Go back to the Account Provider screen
backstack.singleTop(NavTarget.AccountProvider) backstack.singleTop(NavTarget.AccountProvider)

View file

@ -27,7 +27,7 @@ import androidx.compose.runtime.saveable.rememberSaveable
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.changeserver.ChangeServerError import io.element.android.features.login.impl.error.ChangeServerError
import io.element.android.features.login.impl.datasource.AccountProviderDataSource import io.element.android.features.login.impl.datasource.AccountProviderDataSource
import io.element.android.features.login.impl.util.LoginConstants import io.element.android.features.login.impl.util.LoginConstants
import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.Async
@ -35,7 +35,6 @@ import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.architecture.execute import io.element.android.libraries.architecture.execute
import io.element.android.libraries.core.data.tryOrNull import io.element.android.libraries.core.data.tryOrNull
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.net.URL import java.net.URL
@ -59,13 +58,6 @@ class AccountProviderPresenter @AssistedInject constructor(
override fun present(): AccountProviderState { override fun present(): AccountProviderState {
val accountProvider by accountProviderDataSource.flow().collectAsState() val accountProvider by accountProviderDataSource.flow().collectAsState()
val currentHomeServerDetails = authenticationService.getHomeserverDetails().collectAsState().value val currentHomeServerDetails = authenticationService.getHomeserverDetails().collectAsState().value
val getHomeServerDetailsAction: MutableState<Async<MatrixHomeServerDetails>> = remember {
if (currentHomeServerDetails != null) {
mutableStateOf(Async.Success(currentHomeServerDetails))
} else {
mutableStateOf(Async.Uninitialized)
}
}
val localCoroutineScope = rememberCoroutineScope() val localCoroutineScope = rememberCoroutineScope()
val homeserver = rememberSaveable { val homeserver = rememberSaveable {
@ -85,8 +77,7 @@ class AccountProviderPresenter @AssistedInject constructor(
} }
return AccountProviderState( return AccountProviderState(
homeserver = accountProvider.title, accountProvider = accountProvider,
isMatrix = accountProvider.isMatrixOrg,
isAccountCreation = params.isAccountCreation, isAccountCreation = params.isAccountCreation,
loginFlow = loginFlowAction.value, loginFlow = loginFlowAction.value,
eventSink = ::handleEvents eventSink = ::handleEvents

View file

@ -16,18 +16,18 @@
package io.element.android.features.login.impl.accountprovider package io.element.android.features.login.impl.accountprovider
import io.element.android.features.login.impl.accountprovider.item.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 AccountProviderState(
val homeserver: String, val accountProvider: AccountProvider,
val isMatrix: Boolean,
val isAccountCreation: Boolean, val isAccountCreation: Boolean,
val loginFlow: Async<LoginFlow>, val loginFlow: Async<LoginFlow>,
val eventSink: (AccountProviderEvents) -> Unit val eventSink: (AccountProviderEvents) -> Unit
) { ) {
val submitEnabled: Boolean get() = homeserver.isNotEmpty() && (loginFlow is Async.Uninitialized || loginFlow is Async.Loading) val submitEnabled: Boolean get() = accountProvider.title.isNotEmpty() && (loginFlow is Async.Uninitialized || loginFlow is Async.Loading)
} }
sealed interface LoginFlow { sealed interface LoginFlow {

View file

@ -17,19 +17,19 @@
package io.element.android.features.login.impl.accountprovider package io.element.android.features.login.impl.accountprovider
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.libraries.architecture.Async import io.element.android.libraries.architecture.Async
open class AccountProviderStateProvider : PreviewParameterProvider<AccountProviderState> { open class AccountProviderStateProvider : PreviewParameterProvider<AccountProviderState> {
override val values: Sequence<AccountProviderState> override val values: Sequence<AccountProviderState>
get() = sequenceOf( get() = sequenceOf(
aAccountProviderState(), anAccountProviderState(),
// Add other state here // Add other state here
) )
} }
fun aAccountProviderState() = AccountProviderState( fun anAccountProviderState() = AccountProviderState(
homeserver = "matrix.org", accountProvider = anAccountProvider(),
isMatrix = true,
isAccountCreation = false, isAccountCreation = false,
loginFlow = Async.Uninitialized, loginFlow = Async.Uninitialized,
eventSink = {} eventSink = {}

View file

@ -30,14 +30,13 @@ 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.changeserver.ChangeServerError import io.element.android.features.login.impl.error.ChangeServerError
import io.element.android.features.login.impl.changeserver.SlidingSyncNotSupportedDialog import io.element.android.features.login.impl.changeserver.SlidingSyncNotSupportedDialog
import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.Async
import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule 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.molecules.IconTitleSubtitleMolecule
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
import io.element.android.libraries.designsystem.components.async.AsyncFailure import io.element.android.libraries.designsystem.components.async.AsyncFailure
import io.element.android.libraries.designsystem.components.async.AsyncLoading
import io.element.android.libraries.designsystem.components.button.ButtonWithProgress import io.element.android.libraries.designsystem.components.button.ButtonWithProgress
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog 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
@ -78,7 +77,7 @@ fun AccountProviderView(
} else { } else {
R.string.screen_account_provider_signin_title R.string.screen_account_provider_signin_title
}, },
state.homeserver state.accountProvider.title
), ),
subTitle = stringResource( subTitle = stringResource(
id = if (state.isAccountCreation) { id = if (state.isAccountCreation) {
@ -123,7 +122,7 @@ fun AccountProviderView(
} }
) )
} }
is Async.Loading -> AsyncLoading() is Async.Loading -> Unit // The Continue button shows the loading state
is Async.Success -> { is Async.Success -> {
when (val loginFlowState = state.loginFlow.state) { when (val loginFlowState = state.loginFlow.state) {
is LoginFlow.OidcFlow -> onOidcDetails(loginFlowState.oidcDetails) is LoginFlow.OidcFlow -> onOidcDetails(loginFlowState.oidcDetails)

View file

@ -14,9 +14,9 @@
* limitations under the License. * limitations under the License.
*/ */
package io.element.android.features.login.impl.changeaccountprovider.item package io.element.android.features.login.impl.accountprovider.item
data class AccountProviderItem constructor( data class AccountProvider constructor(
val title: String, val title: String,
val subtitle: String? = null, val subtitle: String? = null,
val isPublic: Boolean = false, val isPublic: Boolean = false,

View file

@ -14,21 +14,21 @@
* limitations under the License. * limitations under the License.
*/ */
package io.element.android.features.login.impl.changeaccountprovider.item package io.element.android.features.login.impl.accountprovider.item
import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.tooling.preview.PreviewParameterProvider
open class ChangeAccountProviderItemProvider : PreviewParameterProvider<AccountProviderItem> { open class AccountProviderProvider : PreviewParameterProvider<AccountProvider> {
override val values: Sequence<AccountProviderItem> override val values: Sequence<AccountProvider>
get() = sequenceOf( get() = sequenceOf(
aChangeAccountProviderItem(), anAccountProvider(),
aChangeAccountProviderItem().copy(subtitle = null), anAccountProvider().copy(subtitle = null),
aChangeAccountProviderItem().copy(title = "Other", subtitle = null, isPublic = false, isMatrixOrg = false), anAccountProvider().copy(title = "Other", subtitle = null, isPublic = false, isMatrixOrg = false),
// Add other state here // Add other state here
) )
} }
fun aChangeAccountProviderItem() = AccountProviderItem( fun anAccountProvider() = AccountProvider(
title = "matrix.org", title = "matrix.org",
subtitle = "Matrix.org is an open network for secure, decentralized communication.", subtitle = "Matrix.org is an open network for secure, decentralized communication.",
isPublic = true, isPublic = true,

View file

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package io.element.android.features.login.impl.changeaccountprovider.item package io.element.android.features.login.impl.accountprovider.item
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@ -48,8 +48,8 @@ import io.element.android.libraries.designsystem.theme.components.Text
* https://www.figma.com/file/o9p34zmiuEpZRyvZXJZAYL/FTUE?type=design&node-id=604-60817 * https://www.figma.com/file/o9p34zmiuEpZRyvZXJZAYL/FTUE?type=design&node-id=604-60817
*/ */
@Composable @Composable
fun ChangeAccountProviderItemView( fun AccountProviderView(
item: AccountProviderItem, item: AccountProvider,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onClick: () -> Unit, onClick: () -> Unit,
) { ) {
@ -115,17 +115,17 @@ fun ChangeAccountProviderItemView(
@Preview @Preview
@Composable @Composable
fun ChangeAccountProviderViewLightPreview(@PreviewParameter(ChangeAccountProviderItemProvider::class) item: AccountProviderItem) = fun AccountProviderViewLightPreview(@PreviewParameter(AccountProviderProvider::class) item: AccountProvider) =
ElementPreviewLight { ContentToPreview(item) } ElementPreviewLight { ContentToPreview(item) }
@Preview @Preview
@Composable @Composable
fun ChangeAccountProviderViewDarkPreview(@PreviewParameter(ChangeAccountProviderItemProvider::class) item: AccountProviderItem) = fun AccountProviderViewDarkPreview(@PreviewParameter(AccountProviderProvider::class) item: AccountProvider) =
ElementPreviewDark { ContentToPreview(item) } ElementPreviewDark { ContentToPreview(item) }
@Composable @Composable
private fun ContentToPreview(item: AccountProviderItem) { private fun ContentToPreview(item: AccountProvider) {
ChangeAccountProviderItemView( AccountProviderView(
item = item, item = item,
onClick = { } onClick = { }
) )

View file

@ -25,7 +25,7 @@ import com.bumble.appyx.core.plugin.plugins
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode import io.element.android.anvilannotations.ContributesNode
import io.element.android.features.login.impl.changeaccountprovider.item.AccountProviderItem import io.element.android.features.login.impl.accountprovider.item.AccountProvider
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
@ContributesNode(AppScope::class) @ContributesNode(AppScope::class)
@ -36,12 +36,12 @@ class ChangeAccountProviderNode @AssistedInject constructor(
) : Node(buildContext, plugins = plugins) { ) : Node(buildContext, plugins = plugins) {
interface Callback : Plugin { interface Callback : Plugin {
fun onAccountProviderItemClicked(data: AccountProviderItem) fun onAccountProviderClicked(data: AccountProvider)
fun onOtherClicked() fun onOtherClicked()
} }
private fun onAccountProviderItemClicked(data: AccountProviderItem) { private fun onAccountProviderClicked(data: AccountProvider) {
plugins<Callback>().forEach { it.onAccountProviderItemClicked(data) } plugins<Callback>().forEach { it.onAccountProviderClicked(data) }
} }
private fun onOtherClicked() { private fun onOtherClicked() {
@ -55,7 +55,7 @@ class ChangeAccountProviderNode @AssistedInject constructor(
state = state, state = state,
modifier = modifier, modifier = modifier,
onBackPressed = ::navigateUp, onBackPressed = ::navigateUp,
onAccountProviderItemClicked = ::onAccountProviderItemClicked, onAccountProviderClicked = ::onAccountProviderClicked,
onOtherProviderClicked = ::onOtherClicked, onOtherProviderClicked = ::onOtherClicked,
) )
} }

View file

@ -17,7 +17,7 @@
package io.element.android.features.login.impl.changeaccountprovider package io.element.android.features.login.impl.changeaccountprovider
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import io.element.android.features.login.impl.changeaccountprovider.item.AccountProviderItem import io.element.android.features.login.impl.accountprovider.item.AccountProvider
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import javax.inject.Inject import javax.inject.Inject
@ -28,8 +28,8 @@ class ChangeAccountProviderPresenter @Inject constructor(
override fun present(): ChangeAccountProviderState { override fun present(): ChangeAccountProviderState {
return ChangeAccountProviderState( return ChangeAccountProviderState(
// Just matrix.org by default for now // Just matrix.org by default for now
accountProviderItems = listOf( accountProviders = listOf(
AccountProviderItem( AccountProvider(
title = "matrix.org", title = "matrix.org",
subtitle = null, subtitle = null,
isPublic = true, isPublic = true,

View file

@ -16,9 +16,9 @@
package io.element.android.features.login.impl.changeaccountprovider package io.element.android.features.login.impl.changeaccountprovider
import io.element.android.features.login.impl.changeaccountprovider.item.AccountProviderItem import io.element.android.features.login.impl.accountprovider.item.AccountProvider
// 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(
val accountProviderItems: List<AccountProviderItem>, val accountProviders: List<AccountProvider>,
) )

View file

@ -17,7 +17,7 @@
package io.element.android.features.login.impl.changeaccountprovider package io.element.android.features.login.impl.changeaccountprovider
import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.features.login.impl.changeaccountprovider.item.AccountProviderItem import io.element.android.features.login.impl.accountprovider.item.anAccountProvider
open class ChangeAccountProviderStateProvider : PreviewParameterProvider<ChangeAccountProviderState> { open class ChangeAccountProviderStateProvider : PreviewParameterProvider<ChangeAccountProviderState> {
override val values: Sequence<ChangeAccountProviderState> override val values: Sequence<ChangeAccountProviderState>
@ -28,12 +28,7 @@ open class ChangeAccountProviderStateProvider : PreviewParameterProvider<ChangeA
} }
fun aChangeAccountProviderState() = ChangeAccountProviderState( fun aChangeAccountProviderState() = ChangeAccountProviderState(
accountProviderItems = listOf( accountProviders = listOf(
AccountProviderItem( anAccountProvider()
title = "matrix.org",
subtitle = null,
isPublic = true,
isMatrixOrg = true,
)
), ),
) )

View file

@ -40,8 +40,8 @@ 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.changeaccountprovider.item.AccountProviderItem import io.element.android.features.login.impl.accountprovider.item.AccountProvider
import io.element.android.features.login.impl.changeaccountprovider.item.ChangeAccountProviderItemView import io.element.android.features.login.impl.accountprovider.item.AccountProviderView
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
@ -57,7 +57,7 @@ fun ChangeAccountProviderView(
state: ChangeAccountProviderState, state: ChangeAccountProviderState,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onBackPressed: () -> Unit, onBackPressed: () -> Unit,
onAccountProviderItemClicked: (AccountProviderItem) -> Unit = {}, onAccountProviderClicked: (AccountProvider) -> Unit = {},
onOtherProviderClicked: () -> Unit = {}, onOtherProviderClicked: () -> Unit = {},
) { ) {
val scrollState = rememberScrollState() val scrollState = rememberScrollState()
@ -92,7 +92,7 @@ fun ChangeAccountProviderView(
subTitle = stringResource(id = R.string.screen_change_account_provider_subtitle), subTitle = stringResource(id = R.string.screen_change_account_provider_subtitle),
) )
state.accountProviderItems.forEach { item -> state.accountProviders.forEach { item ->
val alteredItem = if (item.isMatrixOrg) { val alteredItem = if (item.isMatrixOrg) {
// Set the subtitle from the resource // Set the subtitle from the resource
item.copy( item.copy(
@ -101,16 +101,16 @@ fun ChangeAccountProviderView(
} else { } else {
item item
} }
ChangeAccountProviderItemView( AccountProviderView(
item = alteredItem, item = alteredItem,
onClick = { onClick = {
onAccountProviderItemClicked(alteredItem) onAccountProviderClicked(alteredItem)
} }
) )
} }
// Other // Other
ChangeAccountProviderItemView( AccountProviderView(
item = AccountProviderItem( item = AccountProvider(
title = stringResource(id = R.string.screen_change_account_provider_other), title = stringResource(id = R.string.screen_change_account_provider_other),
), ),
onClick = onOtherProviderClicked onClick = onOtherProviderClicked

View file

@ -25,7 +25,7 @@ import com.bumble.appyx.core.plugin.plugins
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode import io.element.android.anvilannotations.ContributesNode
import io.element.android.features.login.impl.changeaccountprovider.item.AccountProviderItem import io.element.android.features.login.impl.accountprovider.item.AccountProvider
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
@ContributesNode(AppScope::class) @ContributesNode(AppScope::class)
@ -36,11 +36,11 @@ class ChangeAccountProviderFormNode @AssistedInject constructor(
) : Node(buildContext, plugins = plugins) { ) : Node(buildContext, plugins = plugins) {
interface Callback : Plugin { interface Callback : Plugin {
fun onAccountProviderItemClicked(data: AccountProviderItem) fun onAccountProviderClicked(data: AccountProvider)
} }
private fun onAccountProviderItemClicked(data: AccountProviderItem) { private fun onAccountProviderClicked(data: AccountProvider) {
plugins<Callback>().forEach { it.onAccountProviderItemClicked(data) } plugins<Callback>().forEach { it.onAccountProviderClicked(data) }
} }
@Composable @Composable
@ -50,7 +50,7 @@ class ChangeAccountProviderFormNode @AssistedInject constructor(
state = state, state = state,
modifier = modifier, modifier = modifier,
onBackPressed = ::navigateUp, onBackPressed = ::navigateUp,
onProviderClicked = ::onAccountProviderItemClicked onAccountProviderClicked = ::onAccountProviderClicked
) )
} }
} }

View file

@ -50,8 +50,8 @@ 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.changeaccountprovider.item.AccountProviderItem import io.element.android.features.login.impl.accountprovider.item.AccountProvider
import io.element.android.features.login.impl.changeaccountprovider.item.ChangeAccountProviderItemView import io.element.android.features.login.impl.accountprovider.item.AccountProviderView
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,7 +72,7 @@ fun ChangeAccountProviderFormView(
state: ChangeAccountProviderFormState, state: ChangeAccountProviderFormState,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onBackPressed: () -> Unit, onBackPressed: () -> Unit,
onProviderClicked: (AccountProviderItem) -> Unit = {}, onAccountProviderClicked: (AccountProvider) -> Unit = {},
) { ) {
val eventSink = state.eventSink val eventSink = state.eventSink
val scrollState = rememberScrollState() val scrollState = rememberScrollState()
@ -161,16 +161,16 @@ fun ChangeAccountProviderFormView(
is Async.Success -> { is Async.Success -> {
state.userInputResult.state.forEach { homeserverData -> state.userInputResult.state.forEach { homeserverData ->
val isMatrixOrg = homeserverData.homeserverUrl == "https://matrix.org" val isMatrixOrg = homeserverData.homeserverUrl == "https://matrix.org"
val item = AccountProviderItem( val item = AccountProvider(
title = homeserverData.homeserverUrl.removePrefix("http://").removePrefix("https://"), title = homeserverData.homeserverUrl.removePrefix("http://").removePrefix("https://"),
subtitle = if (isMatrixOrg) stringResource(id = R.string.screen_change_account_provider_matrix_org_subtitle) else null, subtitle = if (isMatrixOrg) stringResource(id = R.string.screen_change_account_provider_matrix_org_subtitle) else null,
isPublic = isMatrixOrg, // There is no need to know for other servers right now isPublic = isMatrixOrg, // There is no need to know for other servers right now
isMatrixOrg = isMatrixOrg, isMatrixOrg = isMatrixOrg,
) )
ChangeAccountProviderItemView( AccountProviderView(
item = item, item = item,
onClick = { onClick = {
onProviderClicked(item) onAccountProviderClicked(item)
} }
) )
} }

View file

@ -32,6 +32,9 @@ import io.element.android.features.login.impl.util.LoginConstants
import io.element.android.libraries.core.data.tryOrNull import io.element.android.libraries.core.data.tryOrNull
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
/**
* Not used anymore.
*/
@ContributesNode(AppScope::class) @ContributesNode(AppScope::class)
class ChangeServerNode @AssistedInject constructor( class ChangeServerNode @AssistedInject constructor(
@Assisted buildContext: BuildContext, @Assisted buildContext: BuildContext,

View file

@ -22,6 +22,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.error.ChangeServerError
import io.element.android.features.login.impl.util.LoginConstants import io.element.android.features.login.impl.util.LoginConstants
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

View file

@ -18,6 +18,7 @@ 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.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.architecture.Async import io.element.android.libraries.architecture.Async
open class ChangeServerStateProvider : PreviewParameterProvider<ChangeServerState> { open class ChangeServerStateProvider : PreviewParameterProvider<ChangeServerState> {
@ -30,7 +31,8 @@ open class ChangeServerStateProvider : PreviewParameterProvider<ChangeServerStat
homeserver = "invalid.org", homeserver = "invalid.org",
changeServerAction = Async.Failure(ChangeServerError.InlineErrorMessage(R.string.screen_change_server_error_invalid_homeserver)) changeServerAction = Async.Failure(ChangeServerError.InlineErrorMessage(R.string.screen_change_server_error_invalid_homeserver))
), ),
aChangeServerState().copy(homeserver = "invalid.org", changeServerAction = Async.Failure(ChangeServerError.SlidingSyncAlert)), aChangeServerState().copy(homeserver = "invalid.org", changeServerAction = Async.Failure(
ChangeServerError.SlidingSyncAlert)),
aChangeServerState().copy(homeserver = "matrix.org", changeServerAction = Async.Success(Unit)), aChangeServerState().copy(homeserver = "matrix.org", changeServerAction = Async.Success(Unit)),
) )
} }

View file

@ -60,6 +60,7 @@ 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.features.login.impl.util.LoginConstants import io.element.android.features.login.impl.util.LoginConstants
import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.Async
import io.element.android.libraries.designsystem.ElementTextStyles import io.element.android.libraries.designsystem.ElementTextStyles

View file

@ -16,8 +16,8 @@
package io.element.android.features.login.impl.datasource package io.element.android.features.login.impl.datasource
import io.element.android.features.login.impl.changeaccountprovider.item.AccountProviderItem import io.element.android.features.login.impl.accountprovider.item.AccountProvider
import io.element.android.features.login.impl.util.defaultAccountProviderItem 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
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@ -28,15 +28,15 @@ import javax.inject.Inject
@SingleIn(AppScope::class) @SingleIn(AppScope::class)
class AccountProviderDataSource @Inject constructor( class AccountProviderDataSource @Inject constructor(
) { ) {
private val accountProvider: MutableStateFlow<AccountProviderItem> = MutableStateFlow( private val accountProvider: MutableStateFlow<AccountProvider> = MutableStateFlow(
defaultAccountProviderItem defaultAccountProvider
) )
fun flow(): StateFlow<AccountProviderItem> { fun flow(): StateFlow<AccountProvider> {
return accountProvider.asStateFlow() return accountProvider.asStateFlow()
} }
fun userSelection(data: AccountProviderItem) { fun userSelection(data: AccountProvider) {
accountProvider.tryEmit(data) accountProvider.tryEmit(data)
} }
} }

View file

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package io.element.android.features.login.impl.changeserver package io.element.android.features.login.impl.error
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable

View file

@ -16,12 +16,14 @@
package io.element.android.features.login.impl.error package io.element.android.features.login.impl.error
import androidx.annotation.StringRes
import io.element.android.features.login.impl.R import io.element.android.features.login.impl.R
import io.element.android.libraries.matrix.api.auth.AuthErrorCode import io.element.android.libraries.matrix.api.auth.AuthErrorCode
import io.element.android.libraries.matrix.api.auth.AuthenticationException import io.element.android.libraries.matrix.api.auth.AuthenticationException
import io.element.android.libraries.matrix.api.auth.errorCode import io.element.android.libraries.matrix.api.auth.errorCode
import io.element.android.libraries.ui.strings.R.string as StringR import io.element.android.libraries.ui.strings.R.string as StringR
@StringRes
fun loginError( fun loginError(
throwable: Throwable throwable: Throwable
): Int { ): Int {

View file

@ -18,10 +18,13 @@ package io.element.android.features.login.impl.loginpassword
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
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 androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import io.element.android.features.login.impl.datasource.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
@ -32,6 +35,7 @@ import javax.inject.Inject
class LoginPasswordPresenter @Inject constructor( class LoginPasswordPresenter @Inject constructor(
private val authenticationService: MatrixAuthenticationService, private val authenticationService: MatrixAuthenticationService,
private val accountProviderDataSource: AccountProviderDataSource,
) : Presenter<LoginPasswordState> { ) : Presenter<LoginPasswordState> {
@Composable @Composable
@ -44,6 +48,7 @@ class LoginPasswordPresenter @Inject constructor(
val formState = rememberSaveable { val formState = rememberSaveable {
mutableStateOf(LoginFormState.Default) mutableStateOf(LoginFormState.Default)
} }
val accountProvider by accountProviderDataSource.flow().collectAsState()
fun handleEvents(event: LoginPasswordEvents) { fun handleEvents(event: LoginPasswordEvents) {
when (event) { when (event) {
@ -61,6 +66,7 @@ class LoginPasswordPresenter @Inject constructor(
} }
return LoginPasswordState( return LoginPasswordState(
accountProvider = accountProvider,
formState = formState.value, formState = formState.value,
loginAction = loginAction.value, loginAction = loginAction.value,
eventSink = ::handleEvents eventSink = ::handleEvents

View file

@ -17,11 +17,13 @@
package io.element.android.features.login.impl.loginpassword package io.element.android.features.login.impl.loginpassword
import android.os.Parcelable import android.os.Parcelable
import io.element.android.features.login.impl.accountprovider.item.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
data class LoginPasswordState( data class LoginPasswordState(
val accountProvider: AccountProvider,
val formState: LoginFormState, val formState: LoginFormState,
val loginAction: Async<SessionId>, val loginAction: Async<SessionId>,
val eventSink: (LoginPasswordEvents) -> Unit val eventSink: (LoginPasswordEvents) -> Unit

View file

@ -17,6 +17,7 @@
package io.element.android.features.login.impl.loginpassword package io.element.android.features.login.impl.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.libraries.architecture.Async import io.element.android.libraries.architecture.Async
open class LoginPasswordStateProvider : PreviewParameterProvider<LoginPasswordState> { open class LoginPasswordStateProvider : PreviewParameterProvider<LoginPasswordState> {
@ -31,6 +32,7 @@ open class LoginPasswordStateProvider : PreviewParameterProvider<LoginPasswordSt
} }
fun aLoginPasswordState() = LoginPasswordState( fun aLoginPasswordState() = LoginPasswordState(
accountProvider = anAccountProvider(),
formState = LoginFormState.Default, formState = LoginFormState.Default,
loginAction = Async.Uninitialized, loginAction = Async.Uninitialized,
eventSink = {} eventSink = {}

View file

@ -100,6 +100,7 @@ fun LoginPasswordView(
} }
Scaffold( Scaffold(
modifier = modifier,
topBar = { topBar = {
TopAppBar( TopAppBar(
title = {}, title = {},
@ -108,7 +109,7 @@ fun LoginPasswordView(
} }
) { padding -> ) { padding ->
Box( Box(
modifier = modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.imePadding() .imePadding()
.padding(padding) .padding(padding)
@ -121,14 +122,13 @@ fun LoginPasswordView(
.verticalScroll(state = scrollState) .verticalScroll(state = scrollState)
.padding(horizontal = 16.dp), .padding(horizontal = 16.dp),
) { ) {
Spacer(Modifier.height(16.dp))
// Title // Title
IconTitleSubtitleMolecule( IconTitleSubtitleMolecule(
modifier = Modifier.padding(top = 60.dp), modifier = Modifier.padding(top = 20.dp),
iconImageVector = Icons.Filled.AccountCircle, iconImageVector = Icons.Filled.AccountCircle,
title = stringResource( title = stringResource(
id = R.string.screen_account_provider_signin_title, id = R.string.screen_account_provider_signin_title,
"state.homeserver" // TODO state.accountProvider.title
), ),
subTitle = stringResource(id = R.string.screen_login_form_header) subTitle = stringResource(id = R.string.screen_login_form_header)
) )

View file

@ -28,6 +28,9 @@ import io.element.android.anvilannotations.ContributesNode
import io.element.android.libraries.di.AppScope 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
/**
* Not used anymore.
*/
@ContributesNode(AppScope::class) @ContributesNode(AppScope::class)
class LoginRootNode @AssistedInject constructor( class LoginRootNode @AssistedInject constructor(
@Assisted buildContext: BuildContext, @Assisted buildContext: BuildContext,

View file

@ -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.changeaccountprovider.item.AccountProviderItem import io.element.android.features.login.impl.accountprovider.item.AccountProvider
object LoginConstants { object LoginConstants {
const val MATRIX_ORG_URL = "matrix.org" const val MATRIX_ORG_URL = "matrix.org"
@ -25,7 +25,7 @@ object LoginConstants {
const val SLIDING_SYNC_READ_MORE_URL = "https://github.com/matrix-org/sliding-sync/blob/main/docs/Landing.md" const val SLIDING_SYNC_READ_MORE_URL = "https://github.com/matrix-org/sliding-sync/blob/main/docs/Landing.md"
} }
val defaultAccountProviderItem = AccountProviderItem( val defaultAccountProvider = AccountProvider(
title = LoginConstants.DEFAULT_HOMESERVER_URL, title = LoginConstants.DEFAULT_HOMESERVER_URL,
subtitle = null, subtitle = null,
isPublic = LoginConstants.DEFAULT_HOMESERVER_URL == LoginConstants.MATRIX_ORG_URL, isPublic = LoginConstants.DEFAULT_HOMESERVER_URL == LoginConstants.MATRIX_ORG_URL,