Login: try to fix some stuff around login/logout..

This commit is contained in:
ganfra 2022-11-22 12:04:01 +01:00
parent 8f03957c23
commit 0cbf4bf328
5 changed files with 66 additions and 23 deletions

View file

@ -4,17 +4,18 @@ package io.element.android.x.features.login
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
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.*
import androidx.compose.runtime.Composable import androidx.compose.runtime.*
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction 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.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@ -24,6 +25,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.core.compose.textFieldState
import io.element.android.x.designsystem.ElementXTheme import io.element.android.x.designsystem.ElementXTheme
import timber.log.Timber import timber.log.Timber
@ -59,6 +61,8 @@ fun LoginContent(
onSubmitClicked: () -> Unit = {}, onSubmitClicked: () -> Unit = {},
onLoginWithSuccess: () -> Unit = {}, onLoginWithSuccess: () -> Unit = {},
) { ) {
var login by textFieldState(state.login)
var password by textFieldState(state.password)
Surface(color = MaterialTheme.colorScheme.background) { Surface(color = MaterialTheme.colorScheme.background) {
Box( Box(
modifier = Modifier modifier = Modifier
@ -113,31 +117,42 @@ fun LoginContent(
) )
} }
OutlinedTextField( OutlinedTextField(
value = state.login, value = login,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(top = 60.dp), .padding(top = 60.dp),
label = { label = {
Text(text = "Email or username") Text(text = "Email or username")
}, },
onValueChange = onLoginChanged, onValueChange = {
login = it
onLoginChanged(it)
},
keyboardOptions = KeyboardOptions( keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Text, keyboardType = KeyboardType.Email,
imeAction = ImeAction.Next
), ),
) )
OutlinedTextField( OutlinedTextField(
value = state.password, value = password,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(top = 24.dp), .padding(top = 24.dp),
onValueChange = onPasswordChanged, onValueChange = {
password = it
onPasswordChanged(it)
},
label = { label = {
Text(text = "Password") Text(text = "Password")
}, },
isError = isError, isError = isError,
keyboardOptions = KeyboardOptions( keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Password, keyboardType = KeyboardType.Password,
imeAction = ImeAction.Send, imeAction = ImeAction.Done,
),
visualTransformation = PasswordVisualTransformation(),
keyboardActions = KeyboardActions(
onDone = { onSubmitClicked() }
), ),
) )
if (isError) { if (isError) {

View file

@ -1,6 +1,7 @@
package io.element.android.x.features.login 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.MavericksViewModel
import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.Uninitialized
import io.element.android.x.matrix.MatrixInstance import io.element.android.x.matrix.MatrixInstance
@ -20,18 +21,15 @@ class LoginViewModel(initialState: LoginViewState) :
} }
} }
fun onSubmit() = withState { state -> fun onSubmit() {
setState {
copy(isLoggedIn = Loading())
}
viewModelScope.launch { viewModelScope.launch {
suspend { suspend {
val state = awaitState()
// Ensure the server is provided to the Rust SDK // Ensure the server is provided to the Rust SDK
if (matrix.getHomeserver() == null) { if (matrix.getHomeserver() == null) {
matrix.setHomeserver(state.homeserver) matrix.setHomeserver(state.homeserver)
} }
matrix.login(state.login, state.password) matrix.login(state.login.trim(), state.password.trim())
matrix.activeClient().startSync() matrix.activeClient().startSync()
}.execute { }.execute {
copy(isLoggedIn = it) copy(isLoggedIn = it)

View file

@ -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<String> =
remember(stateValue) { mutableStateOf(stateValue) }

View file

@ -12,6 +12,7 @@ import kotlinx.coroutines.withContext
import org.matrix.rustcomponents.sdk.AuthenticationService import org.matrix.rustcomponents.sdk.AuthenticationService
import org.matrix.rustcomponents.sdk.Client import org.matrix.rustcomponents.sdk.Client
import org.matrix.rustcomponents.sdk.ClientBuilder import org.matrix.rustcomponents.sdk.ClientBuilder
import timber.log.Timber
import java.io.File import java.io.File
import java.util.* import java.util.*
import java.util.concurrent.Executors import java.util.concurrent.Executors
@ -26,10 +27,10 @@ class Matrix(
main = Dispatchers.Main, main = Dispatchers.Main,
diffUpdateDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher() 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 sessionStore = SessionStore(context)
private val matrixClient = MutableStateFlow<Optional<MatrixClient>>(Optional.empty()) private val matrixClient = MutableStateFlow<Optional<MatrixClient>>(Optional.empty())
private val authService = AuthenticationService(baseFolder.absolutePath) private val authService = AuthenticationService(baseDirectory.absolutePath)
init { init {
sessionStore.isLoggedIn() sessionStore.isLoggedIn()
@ -59,7 +60,7 @@ class Matrix(
?.let { session -> ?.let { session ->
try { try {
ClientBuilder() ClientBuilder()
.basePath(baseFolder.absolutePath) .basePath(baseDirectory.absolutePath)
.username(session.userId) .username(session.userId)
.build().apply { .build().apply {
restoreSession(session) restoreSession(session)
@ -85,7 +86,12 @@ class Matrix(
suspend fun login(username: String, password: String): MatrixClient = suspend fun login(username: String, password: String): MatrixClient =
withContext(coroutineDispatchers.io) { 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()) sessionStore.storeData(client.session())
createMatrixClient(client) createMatrixClient(client)
} }
@ -95,7 +101,8 @@ class Matrix(
client = client, client = client,
sessionStore = sessionStore, sessionStore = sessionStore,
coroutineScope = coroutineScope, coroutineScope = coroutineScope,
dispatchers = coroutineDispatchers dispatchers = coroutineDispatchers,
baseDirectory = baseDirectory,
).also { ).also {
matrixClient.value = Optional.of(it) matrixClient.value = Optional.of(it)
} }

View file

@ -14,6 +14,7 @@ import kotlinx.coroutines.withContext
import org.matrix.rustcomponents.sdk.* import org.matrix.rustcomponents.sdk.*
import timber.log.Timber import timber.log.Timber
import java.io.Closeable import java.io.Closeable
import java.io.File
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
class MatrixClient internal constructor( class MatrixClient internal constructor(
@ -21,6 +22,7 @@ class MatrixClient internal constructor(
private val sessionStore: SessionStore, private val sessionStore: SessionStore,
private val coroutineScope: CoroutineScope, private val coroutineScope: CoroutineScope,
private val dispatchers: CoroutineDispatchers, private val dispatchers: CoroutineDispatchers,
private val baseDirectory: File,
) : Closeable { ) : Closeable {
private val clientDelegate = object : ClientDelegate { private val clientDelegate = object : ClientDelegate {
@ -114,7 +116,12 @@ class MatrixClient internal constructor(
suspend fun logout() = withContext(dispatchers.io) { suspend fun logout() = withContext(dispatchers.io) {
close() 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() 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()
}
} }