Improve error management on login screens
This commit is contained in:
@@ -10,6 +10,7 @@ dependencies {
|
||||
implementation(project(":libraries:core"))
|
||||
implementation(project(":libraries:matrix"))
|
||||
implementation(project(":libraries:designsystem"))
|
||||
implementation(project(":libraries:elementresources"))
|
||||
implementation(libs.mavericks.compose)
|
||||
implementation(libs.timber)
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
|
||||
@@ -29,6 +29,7 @@ import com.airbnb.mvrx.Success
|
||||
import com.airbnb.mvrx.compose.collectAsState
|
||||
import com.airbnb.mvrx.compose.mavericksViewModel
|
||||
import io.element.android.x.designsystem.ElementXTheme
|
||||
import io.element.android.x.features.login.error.loginError
|
||||
import timber.log.Timber
|
||||
|
||||
@Composable
|
||||
@@ -164,14 +165,12 @@ fun LoginContent(
|
||||
imeAction = ImeAction.Done,
|
||||
),
|
||||
keyboardActions = KeyboardActions(
|
||||
onDone = {
|
||||
if (state.submitEnabled) onSubmitClicked()
|
||||
}
|
||||
onDone = { onSubmitClicked() }
|
||||
),
|
||||
)
|
||||
if (isError) {
|
||||
if (state.isLoggedIn is Fail) {
|
||||
Text(
|
||||
text = (state.isLoggedIn as? Fail)?.toString().orEmpty(),
|
||||
text = loginError(state.formState, state.isLoggedIn.error),
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
modifier = Modifier.padding(start = 16.dp)
|
||||
|
||||
@@ -3,6 +3,7 @@ package io.element.android.x.features.login
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import com.airbnb.mvrx.MavericksViewModel
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
import io.element.android.x.matrix.MatrixInstance
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
@@ -47,9 +48,11 @@ class LoginViewModel(initialState: LoginViewState) :
|
||||
|
||||
fun onSetPassword(password: String) {
|
||||
formState.value = formState.value.copy(password = password)
|
||||
setState { copy(isLoggedIn = Uninitialized) }
|
||||
}
|
||||
|
||||
fun onSetName(name: String) {
|
||||
formState.value = formState.value.copy(login = name)
|
||||
setState { copy(isLoggedIn = Uninitialized) }
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.*
|
||||
@@ -25,6 +26,7 @@ import com.airbnb.mvrx.Loading
|
||||
import com.airbnb.mvrx.Success
|
||||
import com.airbnb.mvrx.compose.collectAsState
|
||||
import com.airbnb.mvrx.compose.mavericksViewModel
|
||||
import io.element.android.x.features.login.error.changeServerError
|
||||
|
||||
@Composable
|
||||
fun ChangeServerScreen(
|
||||
@@ -107,12 +109,18 @@ fun ChangeServerContent(
|
||||
isError = isError,
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Password,
|
||||
imeAction = ImeAction.Send,
|
||||
imeAction = ImeAction.Done,
|
||||
),
|
||||
keyboardActions = KeyboardActions(
|
||||
onDone = { onChangeServerSubmit() }
|
||||
)
|
||||
)
|
||||
if (isError) {
|
||||
if (state.changeServerAction is Fail) {
|
||||
Text(
|
||||
text = (state.changeServerAction as? Fail)?.toString().orEmpty(),
|
||||
text = changeServerError(
|
||||
state.homeserver,
|
||||
state.changeServerAction.error
|
||||
),
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
modifier = Modifier.padding(start = 16.dp)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.element.android.x.features.login.changeserver
|
||||
|
||||
import com.airbnb.mvrx.MavericksViewModel
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
import io.element.android.x.matrix.MatrixInstance
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@@ -19,7 +20,10 @@ class ChangeServerViewModel(initialState: ChangeServerViewState) :
|
||||
|
||||
fun setServer(server: String) {
|
||||
setState {
|
||||
copy(homeserver = server)
|
||||
copy(
|
||||
homeserver = server,
|
||||
changeServerAction = Uninitialized,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package io.element.android.x.features.login.error
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import io.element.android.x.core.uri.isValidUrl
|
||||
import io.element.android.x.features.login.LoginFormState
|
||||
import io.element.android.x.element.resources.R as ElementR
|
||||
|
||||
@Composable
|
||||
fun loginError(
|
||||
data: LoginFormState,
|
||||
throwable: Throwable?
|
||||
): String {
|
||||
return when {
|
||||
data.login.isEmpty() -> "Please enter a login"
|
||||
data.password.isEmpty() -> "Please enter a password"
|
||||
throwable != null -> stringResource(id = ElementR.string.auth_invalid_login_param)
|
||||
else -> "No error provided"
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun changeServerError(
|
||||
data: String,
|
||||
throwable: Throwable?
|
||||
): String {
|
||||
return when {
|
||||
data.isEmpty() -> "Please enter a server URL"
|
||||
!data.isValidUrl() -> stringResource(id = ElementR.string.login_error_invalid_home_server)
|
||||
throwable != null -> "That server doesn’t seem right. Please check the address."
|
||||
else -> "No error provided"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package io.element.android.x.core.uri
|
||||
|
||||
import java.net.URL
|
||||
|
||||
fun String.isValidUrl(): Boolean {
|
||||
return try {
|
||||
URL(this)
|
||||
true
|
||||
} catch (t: Throwable) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure string starts with "http". If it is not the case, "https://" is added, only if the String is not empty
|
||||
*/
|
||||
fun String.ensureProtocol(): String {
|
||||
return when {
|
||||
isEmpty() -> this
|
||||
!startsWith("http") -> "https://$this"
|
||||
else -> this
|
||||
}
|
||||
}
|
||||
|
||||
fun String.ensureTrailingSlash(): String {
|
||||
return when {
|
||||
isEmpty() -> this
|
||||
!endsWith("/") -> "$this/"
|
||||
else -> this
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user