From 0cbf4bf328914533d1ddac0e042cbcf5adbeedad Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 22 Nov 2022 12:04:01 +0100 Subject: [PATCH] Login: try to fix some stuff around login/logout.. --- .../android/x/features/login/LoginScreen.kt | 33 ++++++++++++++----- .../x/features/login/LoginViewModel.kt | 12 +++---- .../x/core/compose/TextFieldLocalState.kt | 11 +++++++ .../io/element/android/x/matrix/Matrix.kt | 17 +++++++--- .../element/android/x/matrix/MatrixClient.kt | 16 +++++++-- 5 files changed, 66 insertions(+), 23 deletions(-) create mode 100644 libraries/core/src/main/java/io/element/android/x/core/compose/TextFieldLocalState.kt diff --git a/features/login/src/main/java/io/element/android/x/features/login/LoginScreen.kt b/features/login/src/main/java/io/element/android/x/features/login/LoginScreen.kt index 7634f87f34..1a2a581265 100644 --- a/features/login/src/main/java/io/element/android/x/features/login/LoginScreen.kt +++ b/features/login/src/main/java/io/element/android/x/features/login/LoginScreen.kt @@ -4,17 +4,18 @@ package io.element.android.x.features.login import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll import androidx.compose.material3.* -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -24,6 +25,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.core.compose.textFieldState import io.element.android.x.designsystem.ElementXTheme import timber.log.Timber @@ -59,6 +61,8 @@ fun LoginContent( onSubmitClicked: () -> Unit = {}, onLoginWithSuccess: () -> Unit = {}, ) { + var login by textFieldState(state.login) + var password by textFieldState(state.password) Surface(color = MaterialTheme.colorScheme.background) { Box( modifier = Modifier @@ -113,31 +117,42 @@ fun LoginContent( ) } OutlinedTextField( - value = state.login, + value = login, modifier = Modifier .fillMaxWidth() .padding(top = 60.dp), label = { Text(text = "Email or username") }, - onValueChange = onLoginChanged, + onValueChange = { + login = it + onLoginChanged(it) + }, keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Text, + keyboardType = KeyboardType.Email, + imeAction = ImeAction.Next ), ) OutlinedTextField( - value = state.password, + value = password, modifier = Modifier .fillMaxWidth() .padding(top = 24.dp), - onValueChange = onPasswordChanged, + onValueChange = { + password = it + onPasswordChanged(it) + }, label = { Text(text = "Password") }, isError = isError, keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Password, - imeAction = ImeAction.Send, + imeAction = ImeAction.Done, + ), + visualTransformation = PasswordVisualTransformation(), + keyboardActions = KeyboardActions( + onDone = { onSubmitClicked() } ), ) if (isError) { diff --git a/features/login/src/main/java/io/element/android/x/features/login/LoginViewModel.kt b/features/login/src/main/java/io/element/android/x/features/login/LoginViewModel.kt index 1218f63b63..aaca27e4ad 100644 --- a/features/login/src/main/java/io/element/android/x/features/login/LoginViewModel.kt +++ b/features/login/src/main/java/io/element/android/x/features/login/LoginViewModel.kt @@ -1,6 +1,7 @@ package io.element.android.x.features.login -import com.airbnb.mvrx.Loading +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue import com.airbnb.mvrx.MavericksViewModel import com.airbnb.mvrx.Uninitialized import io.element.android.x.matrix.MatrixInstance @@ -20,18 +21,15 @@ class LoginViewModel(initialState: LoginViewState) : } } - fun onSubmit() = withState { state -> - setState { - copy(isLoggedIn = Loading()) - } - + fun onSubmit() { viewModelScope.launch { suspend { + val state = awaitState() // Ensure the server is provided to the Rust SDK if (matrix.getHomeserver() == null) { matrix.setHomeserver(state.homeserver) } - matrix.login(state.login, state.password) + matrix.login(state.login.trim(), state.password.trim()) matrix.activeClient().startSync() }.execute { copy(isLoggedIn = it) diff --git a/libraries/core/src/main/java/io/element/android/x/core/compose/TextFieldLocalState.kt b/libraries/core/src/main/java/io/element/android/x/core/compose/TextFieldLocalState.kt new file mode 100644 index 0000000000..188d99260d --- /dev/null +++ b/libraries/core/src/main/java/io/element/android/x/core/compose/TextFieldLocalState.kt @@ -0,0 +1,11 @@ +package io.element.android.x.core.compose + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember + + +@Composable +public fun textFieldState(stateValue: String): MutableState = + remember(stateValue) { mutableStateOf(stateValue) } \ No newline at end of file diff --git a/libraries/matrix/src/main/java/io/element/android/x/matrix/Matrix.kt b/libraries/matrix/src/main/java/io/element/android/x/matrix/Matrix.kt index 4f2e6da495..323b837e13 100644 --- a/libraries/matrix/src/main/java/io/element/android/x/matrix/Matrix.kt +++ b/libraries/matrix/src/main/java/io/element/android/x/matrix/Matrix.kt @@ -12,6 +12,7 @@ import kotlinx.coroutines.withContext import org.matrix.rustcomponents.sdk.AuthenticationService import org.matrix.rustcomponents.sdk.Client import org.matrix.rustcomponents.sdk.ClientBuilder +import timber.log.Timber import java.io.File import java.util.* import java.util.concurrent.Executors @@ -26,10 +27,10 @@ class Matrix( main = Dispatchers.Main, diffUpdateDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher() ) - private val baseFolder = File(context.filesDir, "matrix") + private val baseDirectory = File(context.filesDir, "sessions") private val sessionStore = SessionStore(context) private val matrixClient = MutableStateFlow>(Optional.empty()) - private val authService = AuthenticationService(baseFolder.absolutePath) + private val authService = AuthenticationService(baseDirectory.absolutePath) init { sessionStore.isLoggedIn() @@ -59,7 +60,7 @@ class Matrix( ?.let { session -> try { ClientBuilder() - .basePath(baseFolder.absolutePath) + .basePath(baseDirectory.absolutePath) .username(session.userId) .build().apply { restoreSession(session) @@ -85,7 +86,12 @@ class Matrix( suspend fun login(username: String, password: String): MatrixClient = withContext(coroutineDispatchers.io) { - val client = authService.login(username, password, "MatrixRustSDKSample", null) + val client = try { + authService.login(username, password, "ElementX", null) + }catch (failure:Throwable){ + Timber.e(failure,"Fail login") + throw failure + } sessionStore.storeData(client.session()) createMatrixClient(client) } @@ -95,7 +101,8 @@ class Matrix( client = client, sessionStore = sessionStore, coroutineScope = coroutineScope, - dispatchers = coroutineDispatchers + dispatchers = coroutineDispatchers, + baseDirectory = baseDirectory, ).also { matrixClient.value = Optional.of(it) } diff --git a/libraries/matrix/src/main/java/io/element/android/x/matrix/MatrixClient.kt b/libraries/matrix/src/main/java/io/element/android/x/matrix/MatrixClient.kt index 77858899ea..4744a982fd 100644 --- a/libraries/matrix/src/main/java/io/element/android/x/matrix/MatrixClient.kt +++ b/libraries/matrix/src/main/java/io/element/android/x/matrix/MatrixClient.kt @@ -14,6 +14,7 @@ import kotlinx.coroutines.withContext import org.matrix.rustcomponents.sdk.* import timber.log.Timber import java.io.Closeable +import java.io.File import java.util.concurrent.atomic.AtomicBoolean class MatrixClient internal constructor( @@ -21,6 +22,7 @@ class MatrixClient internal constructor( private val sessionStore: SessionStore, private val coroutineScope: CoroutineScope, private val dispatchers: CoroutineDispatchers, + private val baseDirectory: File, ) : Closeable { private val clientDelegate = object : ClientDelegate { @@ -114,7 +116,12 @@ class MatrixClient internal constructor( suspend fun logout() = withContext(dispatchers.io) { close() - client.logout() + try { + client.logout() + } catch (failure: Throwable) { + Timber.e(failure, "Fail to call logout on HS. Still delete local files.") + } + baseDirectory.deleteSessionDirectory(userID = client.userId()) sessionStore.reset() } @@ -150,5 +157,10 @@ class MatrixClient internal constructor( } } - + private fun File.deleteSessionDirectory(userID: String): Boolean { + // Rust sanitises the user ID replacing invalid characters with an _ + val sanitisedUserID = userID.replace(":", "_") + val sessionDirectory = File(this, sanitisedUserID) + return sessionDirectory.deleteRecursively() + } }