Login: try to fix some stuff around login/logout..
This commit is contained in:
parent
8f03957c23
commit
0cbf4bf328
5 changed files with 66 additions and 23 deletions
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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) }
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue