Validate server on user click.

This commit is contained in:
Benoit Marty 2023-06-08 15:52:45 +02:00 committed by Benoit Marty
parent 4df03762a8
commit b039f0d01d
21 changed files with 353 additions and 51 deletions

View file

@ -33,7 +33,6 @@ import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode
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.form.ChangeAccountProviderFormNode
import io.element.android.features.login.impl.datasource.AccountProviderDataSource
@ -125,8 +124,7 @@ class LoginFlowNode @AssistedInject constructor(
}
NavTarget.ChangeAccountProvider -> {
val callback = object : ChangeAccountProviderNode.Callback {
override fun onAccountProviderClicked(data: AccountProvider) {
accountProviderDataSource.userSelection(data)
override fun onDone() {
// Go back to the Account Provider screen
backstack.singleTop(NavTarget.AccountProvider)
}
@ -140,8 +138,7 @@ class LoginFlowNode @AssistedInject constructor(
}
NavTarget.ChangeAccountProviderForm -> {
val callback = object : ChangeAccountProviderFormNode.Callback {
override fun onAccountProviderClicked(data: AccountProvider) {
accountProviderDataSource.userSelection(data)
override fun onDone() {
// Go back to the Account Provider screen
backstack.singleTop(NavTarget.AccountProvider)
}

View file

@ -16,9 +16,6 @@
package io.element.android.features.login.impl.accountprovider
import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
@ -29,10 +26,9 @@ import com.bumble.appyx.core.plugin.plugins
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode
import io.element.android.features.login.impl.util.LoginConstants
import io.element.android.features.login.impl.util.openLearnMorePage
import io.element.android.libraries.architecture.NodeInputs
import io.element.android.libraries.architecture.inputs
import io.element.android.libraries.core.data.tryOrNull
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.matrix.api.auth.OidcDetails
@ -72,11 +68,6 @@ class AccountProviderNode @AssistedInject constructor(
plugins<Callback>().forEach { it.onChangeAccountProvider() }
}
private fun openLearnMorePage(context: Context) {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(LoginConstants.SLIDING_SYNC_READ_MORE_URL))
tryOrNull { context.startActivity(intent) }
}
@Composable
override fun View(modifier: Modifier) {
val state = presenter.present()

View file

@ -60,8 +60,6 @@ fun AccountProviderView(
}
}
val eventSink = state.eventSink
val invalidHomeserverError = (state.loginFlow as? Async.Failure)?.error as? ChangeServerError.InlineErrorMessage
val slidingSyncNotSupportedError = (state.loginFlow as? Async.Failure)?.error as? ChangeServerError.SlidingSyncAlert
HeaderFooterPage(
modifier = modifier,
@ -112,7 +110,26 @@ fun AccountProviderView(
}
) {
when (state.loginFlow) {
is Async.Failure -> Unit // Error dialog will be displayed
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) {
@ -122,22 +139,6 @@ fun AccountProviderView(
}
Async.Uninitialized -> Unit
}
if (slidingSyncNotSupportedError != null) {
SlidingSyncNotSupportedDialog(onLearnMoreClicked = {
onLearnMoreClicked()
eventSink(AccountProviderEvents.ClearError)
}, onDismiss = {
eventSink(AccountProviderEvents.ClearError)
})
}
if (invalidHomeserverError != null) {
ErrorDialog(
content = invalidHomeserverError.message(),
onDismiss = {
eventSink.invoke(AccountProviderEvents.ClearError)
}
)
}
}
}

View file

@ -17,14 +17,20 @@
package io.element.android.features.login.impl.accountprovider
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import io.element.android.features.login.impl.R
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
import io.element.android.libraries.ui.strings.R as StringR
@Composable
internal fun SlidingSyncNotSupportedDialog(onLearnMoreClicked: () -> Unit, onDismiss: () -> Unit) {
internal fun SlidingSyncNotSupportedDialog(
onLearnMoreClicked: () -> Unit,
onDismiss: () -> Unit,
modifier: Modifier = Modifier,
) {
ConfirmationDialog(
modifier = modifier,
onDismiss = onDismiss,
submitText = stringResource(StringR.string.action_learn_more),
onSubmitClicked = onLearnMoreClicked,

View file

@ -18,6 +18,7 @@ package io.element.android.features.login.impl.changeaccountprovider
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin
@ -25,7 +26,7 @@ import com.bumble.appyx.core.plugin.plugins
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode
import io.element.android.features.login.impl.accountprovider.item.AccountProvider
import io.element.android.features.login.impl.util.openLearnMorePage
import io.element.android.libraries.di.AppScope
@ContributesNode(AppScope::class)
@ -36,12 +37,12 @@ class ChangeAccountProviderNode @AssistedInject constructor(
) : Node(buildContext, plugins = plugins) {
interface Callback : Plugin {
fun onAccountProviderClicked(data: AccountProvider)
fun onDone()
fun onOtherClicked()
}
private fun onAccountProviderClicked(data: AccountProvider) {
plugins<Callback>().forEach { it.onAccountProviderClicked(data) }
private fun onDone() {
plugins<Callback>().forEach { it.onDone() }
}
private fun onOtherClicked() {
@ -51,11 +52,13 @@ class ChangeAccountProviderNode @AssistedInject constructor(
@Composable
override fun View(modifier: Modifier) {
val state = presenter.present()
val context = LocalContext.current
ChangeAccountProviderView(
state = state,
modifier = modifier,
onBackPressed = ::navigateUp,
onAccountProviderClicked = ::onAccountProviderClicked,
onLearnMoreClicked = { openLearnMorePage(context) },
onDone = ::onDone,
onOtherProviderClicked = ::onOtherClicked,
)
}

View file

@ -18,14 +18,17 @@ package io.element.android.features.login.impl.changeaccountprovider
import androidx.compose.runtime.Composable
import io.element.android.features.login.impl.accountprovider.item.AccountProvider
import io.element.android.features.login.impl.changeaccountprovider.common.ChangeServerPresenter
import io.element.android.libraries.architecture.Presenter
import javax.inject.Inject
class ChangeAccountProviderPresenter @Inject constructor(
private val changeServerPresenter: ChangeServerPresenter,
) : Presenter<ChangeAccountProviderState> {
@Composable
override fun present(): ChangeAccountProviderState {
val changeServerState = changeServerPresenter.present()
return ChangeAccountProviderState(
// Just matrix.org by default for now
accountProviders = listOf(
@ -38,6 +41,7 @@ class ChangeAccountProviderPresenter @Inject constructor(
supportSlidingSync = true,
)
),
changeServerState = changeServerState,
)
}
}

View file

@ -17,8 +17,10 @@
package io.element.android.features.login.impl.changeaccountprovider
import io.element.android.features.login.impl.accountprovider.item.AccountProvider
import io.element.android.features.login.impl.changeaccountprovider.common.ChangeServerState
// Do not use default value, so no member get forgotten in the presenters.
data class ChangeAccountProviderState constructor(
val accountProviders: List<AccountProvider>,
val changeServerState: ChangeServerState,
)

View file

@ -18,6 +18,7 @@ package io.element.android.features.login.impl.changeaccountprovider
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.features.login.impl.accountprovider.item.anAccountProvider
import io.element.android.features.login.impl.changeaccountprovider.common.aChangeServerState
open class ChangeAccountProviderStateProvider : PreviewParameterProvider<ChangeAccountProviderState> {
override val values: Sequence<ChangeAccountProviderState>
@ -31,4 +32,5 @@ fun aChangeAccountProviderState() = ChangeAccountProviderState(
accountProviders = listOf(
anAccountProvider()
),
changeServerState = aChangeServerState(),
)

View file

@ -42,6 +42,8 @@ import androidx.compose.ui.unit.dp
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.item.AccountProviderView
import io.element.android.features.login.impl.changeaccountprovider.common.ChangeServerEvents
import io.element.android.features.login.impl.changeaccountprovider.common.ChangeServerView
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
@ -57,7 +59,8 @@ fun ChangeAccountProviderView(
state: ChangeAccountProviderState,
modifier: Modifier = Modifier,
onBackPressed: () -> Unit = {},
onAccountProviderClicked: (AccountProvider) -> Unit = {},
onLearnMoreClicked: () -> Unit = {},
onDone: () -> Unit = {},
onOtherProviderClicked: () -> Unit = {},
) {
val scrollState = rememberScrollState()
@ -104,7 +107,7 @@ fun ChangeAccountProviderView(
AccountProviderView(
item = alteredItem,
onClick = {
onAccountProviderClicked(alteredItem)
state.changeServerState.eventSink.invoke(ChangeServerEvents.ChangeServer(alteredItem))
}
)
}
@ -117,6 +120,11 @@ fun ChangeAccountProviderView(
)
Spacer(Modifier.height(32.dp))
}
ChangeServerView(
state = state.changeServerState,
onLearnMoreClicked = onLearnMoreClicked,
onDone = onDone,
)
}
}
}

View file

@ -0,0 +1,24 @@
/*
* 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.common
import io.element.android.features.login.impl.accountprovider.item.AccountProvider
sealed interface ChangeServerEvents {
data class ChangeServer(val accountProvider: AccountProvider) : ChangeServerEvents
object ClearError : ChangeServerEvents
}

View file

@ -0,0 +1,76 @@
/*
* 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.common
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import io.element.android.features.login.impl.accountprovider.item.AccountProvider
import io.element.android.features.login.impl.datasource.AccountProviderDataSource
import io.element.android.features.login.impl.error.ChangeServerError
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.architecture.execute
import io.element.android.libraries.core.data.tryOrNull
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import java.net.URL
import javax.inject.Inject
class ChangeServerPresenter @Inject constructor(
private val authenticationService: MatrixAuthenticationService,
private val accountProviderDataSource: AccountProviderDataSource,
) : Presenter<ChangeServerState> {
@Composable
override fun present(): ChangeServerState {
val localCoroutineScope = rememberCoroutineScope()
val changeServerAction: MutableState<Async<Unit>> = remember {
mutableStateOf(Async.Uninitialized)
}
fun handleEvents(event: ChangeServerEvents) {
when (event) {
is ChangeServerEvents.ChangeServer -> localCoroutineScope.changeServer(event.accountProvider, changeServerAction)
ChangeServerEvents.ClearError -> changeServerAction.value = Async.Uninitialized
}
}
return ChangeServerState(
changeServerAction = changeServerAction.value,
eventSink = ::handleEvents
)
}
private fun CoroutineScope.changeServer(
data: AccountProvider,
changeServerAction: MutableState<Async<Unit>>,
) = launch {
suspend {
val domain = tryOrNull { URL(data.title) }?.host ?: data.title
authenticationService.setHomeserver(domain).map {
authenticationService.getHomeserverDetails().value!!
// Valid, remember user choice
accountProviderDataSource.userSelection(data)
}.getOrThrow()
}.execute(changeServerAction, errorMapping = ChangeServerError::from)
}
}

View file

@ -0,0 +1,24 @@
/*
* 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.common
import io.element.android.libraries.architecture.Async
data class ChangeServerState(
val changeServerAction: Async<Unit>,
val eventSink: (ChangeServerEvents) -> Unit
)

View file

@ -0,0 +1,32 @@
/*
* 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.common
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.architecture.Async
open class ChangeServerStateProvider : PreviewParameterProvider<ChangeServerState> {
override val values: Sequence<ChangeServerState>
get() = sequenceOf(
aChangeServerState(),
)
}
fun aChangeServerState() = ChangeServerState(
changeServerAction = Async.Uninitialized,
eventSink = {}
)

View file

@ -0,0 +1,84 @@
/*
* 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.common
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import io.element.android.features.login.impl.accountprovider.SlidingSyncNotSupportedDialog
import io.element.android.features.login.impl.error.ChangeServerError
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.designsystem.components.ProgressDialog
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
@Composable
fun ChangeServerView(
state: ChangeServerState,
modifier: Modifier = Modifier,
onLearnMoreClicked: () -> Unit = {},
onDone: () -> Unit = {},
) {
val eventSink = state.eventSink
when (state.changeServerAction) {
is Async.Failure -> {
when (val error = state.changeServerAction.error) {
is ChangeServerError.InlineErrorMessage -> {
ErrorDialog(
modifier = modifier,
content = error.message(),
onDismiss = {
eventSink.invoke(ChangeServerEvents.ClearError)
}
)
}
is ChangeServerError.SlidingSyncAlert -> {
SlidingSyncNotSupportedDialog(
modifier = modifier,
onLearnMoreClicked = {
onLearnMoreClicked()
eventSink.invoke(ChangeServerEvents.ClearError)
}, onDismiss = {
eventSink.invoke(ChangeServerEvents.ClearError)
})
}
}
}
is Async.Loading -> ProgressDialog()
is Async.Success -> onDone()
Async.Uninitialized -> Unit
}
}
@Preview
@Composable
fun ChangeServerViewLightPreview(@PreviewParameter(ChangeServerStateProvider::class) state: ChangeServerState) =
ElementPreviewLight { ContentToPreview(state) }
@Preview
@Composable
fun ChangeServerViewDarkPreview(@PreviewParameter(ChangeServerStateProvider::class) state: ChangeServerState) =
ElementPreviewDark { ContentToPreview(state) }
@Composable
private fun ContentToPreview(state: ChangeServerState) {
ChangeServerView(
state = state,
)
}

View file

@ -18,6 +18,7 @@ package io.element.android.features.login.impl.changeaccountprovider.form
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin
@ -25,7 +26,7 @@ import com.bumble.appyx.core.plugin.plugins
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode
import io.element.android.features.login.impl.accountprovider.item.AccountProvider
import io.element.android.features.login.impl.util.openLearnMorePage
import io.element.android.libraries.di.AppScope
@ContributesNode(AppScope::class)
@ -36,21 +37,23 @@ class ChangeAccountProviderFormNode @AssistedInject constructor(
) : Node(buildContext, plugins = plugins) {
interface Callback : Plugin {
fun onAccountProviderClicked(data: AccountProvider)
fun onDone()
}
private fun onAccountProviderClicked(data: AccountProvider) {
plugins<Callback>().forEach { it.onAccountProviderClicked(data) }
private fun onDone() {
plugins<Callback>().forEach { it.onDone() }
}
@Composable
override fun View(modifier: Modifier) {
val state = presenter.present()
val context = LocalContext.current
ChangeAccountProviderFormView(
state = state,
modifier = modifier,
onBackPressed = ::navigateUp,
onAccountProviderClicked = ::onAccountProviderClicked
onLearnMoreClicked = { openLearnMorePage(context) },
onDone = ::onDone,
)
}
}

View file

@ -22,6 +22,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import io.element.android.features.login.impl.changeaccountprovider.common.ChangeServerPresenter
import io.element.android.libraries.architecture.Presenter
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@ -29,6 +30,7 @@ import javax.inject.Inject
class ChangeAccountProviderFormPresenter @Inject constructor(
private val homeserverResolver: HomeserverResolver,
private val changeServerPresenter: ChangeServerPresenter,
) : Presenter<ChangeAccountProviderFormState> {
@Composable
@ -38,6 +40,7 @@ class ChangeAccountProviderFormPresenter @Inject constructor(
val userInput = rememberSaveable {
mutableStateOf("")
}
val changeServerState = changeServerPresenter.present()
val data by homeserverResolver.flow().collectAsState()
fun handleEvents(event: ChangeAccountProviderFormEvents) {
@ -52,6 +55,7 @@ class ChangeAccountProviderFormPresenter @Inject constructor(
return ChangeAccountProviderFormState(
userInput = userInput.value,
userInputResult = data,
changeServerState = changeServerState,
eventSink = ::handleEvents
)
}

View file

@ -16,11 +16,13 @@
package io.element.android.features.login.impl.changeaccountprovider.form
import io.element.android.features.login.impl.changeaccountprovider.common.ChangeServerState
import io.element.android.libraries.architecture.Async
// Do not use default value, so no member get forgotten in the presenters.
data class ChangeAccountProviderFormState(
val userInput: String,
val userInputResult: Async<List<HomeserverData>>,
val changeServerState: ChangeServerState,
val eventSink: (ChangeAccountProviderFormEvents) -> Unit
)

View file

@ -17,6 +17,7 @@
package io.element.android.features.login.impl.changeaccountprovider.form
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.features.login.impl.changeaccountprovider.common.aChangeServerState
import io.element.android.libraries.architecture.Async
open class ChangeAccountProviderFormStateProvider : PreviewParameterProvider<ChangeAccountProviderFormState> {
@ -34,6 +35,7 @@ fun aChangeAccountProviderFormState(
) = ChangeAccountProviderFormState(
userInput = userInput,
userInputResult = userInputResult,
changeServerState = aChangeServerState(),
eventSink = {}
)

View file

@ -52,6 +52,8 @@ import androidx.compose.ui.unit.dp
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.item.AccountProviderView
import io.element.android.features.login.impl.changeaccountprovider.common.ChangeServerEvents
import io.element.android.features.login.impl.changeaccountprovider.common.ChangeServerView
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
import io.element.android.libraries.designsystem.components.button.BackButton
@ -72,7 +74,8 @@ fun ChangeAccountProviderFormView(
state: ChangeAccountProviderFormState,
modifier: Modifier = Modifier,
onBackPressed: () -> Unit = {},
onAccountProviderClicked: (AccountProvider) -> Unit = {},
onLearnMoreClicked: () -> Unit = {},
onDone: () -> Unit = {},
) {
val eventSink = state.eventSink
val scrollState = rememberScrollState()
@ -164,7 +167,7 @@ fun ChangeAccountProviderFormView(
AccountProviderView(
item = item,
onClick = {
onAccountProviderClicked(item)
state.changeServerState.eventSink.invoke(ChangeServerEvents.ChangeServer(item))
}
)
}
@ -173,6 +176,11 @@ fun ChangeAccountProviderFormView(
}
Spacer(Modifier.height(32.dp))
}
ChangeServerView(
state = state.changeServerState,
onLearnMoreClicked = onLearnMoreClicked,
onDone = onDone,
)
}
}
}

View file

@ -0,0 +1,27 @@
/*
* 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.util
import android.content.Context
import android.content.Intent
import android.net.Uri
import io.element.android.libraries.core.data.tryOrNull
fun openLearnMorePage(context: Context) {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(LoginConstants.SLIDING_SYNC_READ_MORE_URL))
tryOrNull { context.startActivity(intent) }
}

View file

@ -24,12 +24,14 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.ui.strings.R as StringR
@Composable
fun AsyncFailure(
@ -43,11 +45,11 @@ fun AsyncFailure(
.padding(vertical = 32.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(text = throwable.message ?: "An error occurred")
Text(text = throwable.message ?: stringResource(id = StringR.string.error_unknown))
if (onRetry != null) {
Spacer(modifier = Modifier.height(24.dp))
Button(onClick = onRetry) {
Text(text = "Retry")
Text(text = stringResource(id = StringR.string.action_retry))
}
}
}