Improve error management on login screens
This commit is contained in:
parent
b3930d38f5
commit
4722ed58b6
7 changed files with 88 additions and 9 deletions
|
|
@ -10,6 +10,7 @@ dependencies {
|
||||||
implementation(project(":libraries:core"))
|
implementation(project(":libraries:core"))
|
||||||
implementation(project(":libraries:matrix"))
|
implementation(project(":libraries:matrix"))
|
||||||
implementation(project(":libraries:designsystem"))
|
implementation(project(":libraries:designsystem"))
|
||||||
|
implementation(project(":libraries:elementresources"))
|
||||||
implementation(libs.mavericks.compose)
|
implementation(libs.mavericks.compose)
|
||||||
implementation(libs.timber)
|
implementation(libs.timber)
|
||||||
testImplementation("junit:junit:4.13.2")
|
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.collectAsState
|
||||||
import com.airbnb.mvrx.compose.mavericksViewModel
|
import com.airbnb.mvrx.compose.mavericksViewModel
|
||||||
import io.element.android.x.designsystem.ElementXTheme
|
import io.element.android.x.designsystem.ElementXTheme
|
||||||
|
import io.element.android.x.features.login.error.loginError
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -164,14 +165,12 @@ fun LoginContent(
|
||||||
imeAction = ImeAction.Done,
|
imeAction = ImeAction.Done,
|
||||||
),
|
),
|
||||||
keyboardActions = KeyboardActions(
|
keyboardActions = KeyboardActions(
|
||||||
onDone = {
|
onDone = { onSubmitClicked() }
|
||||||
if (state.submitEnabled) onSubmitClicked()
|
|
||||||
}
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
if (isError) {
|
if (state.isLoggedIn is Fail) {
|
||||||
Text(
|
Text(
|
||||||
text = (state.isLoggedIn as? Fail)?.toString().orEmpty(),
|
text = loginError(state.formState, state.isLoggedIn.error),
|
||||||
color = MaterialTheme.colorScheme.error,
|
color = MaterialTheme.colorScheme.error,
|
||||||
style = MaterialTheme.typography.bodySmall,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
modifier = Modifier.padding(start = 16.dp)
|
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.mutableStateOf
|
||||||
import androidx.compose.runtime.snapshotFlow
|
import androidx.compose.runtime.snapshotFlow
|
||||||
import com.airbnb.mvrx.MavericksViewModel
|
import com.airbnb.mvrx.MavericksViewModel
|
||||||
|
import com.airbnb.mvrx.Uninitialized
|
||||||
import io.element.android.x.matrix.MatrixInstance
|
import io.element.android.x.matrix.MatrixInstance
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
|
@ -47,9 +48,11 @@ class LoginViewModel(initialState: LoginViewState) :
|
||||||
|
|
||||||
fun onSetPassword(password: String) {
|
fun onSetPassword(password: String) {
|
||||||
formState.value = formState.value.copy(password = password)
|
formState.value = formState.value.copy(password = password)
|
||||||
|
setState { copy(isLoggedIn = Uninitialized) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onSetName(name: String) {
|
fun onSetName(name: String) {
|
||||||
formState.value = formState.value.copy(login = name)
|
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.layout.*
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
|
|
@ -25,6 +26,7 @@ import com.airbnb.mvrx.Loading
|
||||||
import com.airbnb.mvrx.Success
|
import com.airbnb.mvrx.Success
|
||||||
import com.airbnb.mvrx.compose.collectAsState
|
import com.airbnb.mvrx.compose.collectAsState
|
||||||
import com.airbnb.mvrx.compose.mavericksViewModel
|
import com.airbnb.mvrx.compose.mavericksViewModel
|
||||||
|
import io.element.android.x.features.login.error.changeServerError
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ChangeServerScreen(
|
fun ChangeServerScreen(
|
||||||
|
|
@ -107,12 +109,18 @@ fun ChangeServerContent(
|
||||||
isError = isError,
|
isError = isError,
|
||||||
keyboardOptions = KeyboardOptions(
|
keyboardOptions = KeyboardOptions(
|
||||||
keyboardType = KeyboardType.Password,
|
keyboardType = KeyboardType.Password,
|
||||||
imeAction = ImeAction.Send,
|
imeAction = ImeAction.Done,
|
||||||
),
|
),
|
||||||
|
keyboardActions = KeyboardActions(
|
||||||
|
onDone = { onChangeServerSubmit() }
|
||||||
|
)
|
||||||
)
|
)
|
||||||
if (isError) {
|
if (state.changeServerAction is Fail) {
|
||||||
Text(
|
Text(
|
||||||
text = (state.changeServerAction as? Fail)?.toString().orEmpty(),
|
text = changeServerError(
|
||||||
|
state.homeserver,
|
||||||
|
state.changeServerAction.error
|
||||||
|
),
|
||||||
color = MaterialTheme.colorScheme.error,
|
color = MaterialTheme.colorScheme.error,
|
||||||
style = MaterialTheme.typography.bodySmall,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
modifier = Modifier.padding(start = 16.dp)
|
modifier = Modifier.padding(start = 16.dp)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package io.element.android.x.features.login.changeserver
|
package io.element.android.x.features.login.changeserver
|
||||||
|
|
||||||
import com.airbnb.mvrx.MavericksViewModel
|
import com.airbnb.mvrx.MavericksViewModel
|
||||||
|
import com.airbnb.mvrx.Uninitialized
|
||||||
import io.element.android.x.matrix.MatrixInstance
|
import io.element.android.x.matrix.MatrixInstance
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
|
@ -19,7 +20,10 @@ class ChangeServerViewModel(initialState: ChangeServerViewState) :
|
||||||
|
|
||||||
fun setServer(server: String) {
|
fun setServer(server: String) {
|
||||||
setState {
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue