diff --git a/app/build.gradle b/app/build.gradle index 6cbb9d4bd5..6dded367f7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -68,6 +68,7 @@ dependencies { implementation project(":libraries:matrix") implementation project(":features:login") implementation project(":features:roomlist") + implementation project(":features:messages") coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:1.2.0" implementation 'io.github.raamcosta.compose-destinations:core:1.7.23-beta' diff --git a/app/src/main/java/io/element/android/x/MainActivity.kt b/app/src/main/java/io/element/android/x/MainActivity.kt index 400d443543..a0f2e41f5e 100644 --- a/app/src/main/java/io/element/android/x/MainActivity.kt +++ b/app/src/main/java/io/element/android/x/MainActivity.kt @@ -33,7 +33,12 @@ private fun MainScreen(viewModel: MainViewModel) { val engine = rememberNavHostEngine() val navController = engine.rememberNavController() val startRoute = runBlocking { - if (!viewModel.hasSession()) LoginScreenNavigationDestination else NavGraphs.root.startRoute + if (!viewModel.isLoggedIn()) { + LoginScreenNavigationDestination + } else { + viewModel.restoreSession() + NavGraphs.root.startRoute + } } DestinationsNavHost( diff --git a/app/src/main/java/io/element/android/x/MainViewModel.kt b/app/src/main/java/io/element/android/x/MainViewModel.kt index 7790a9f133..d0f8bb68a1 100644 --- a/app/src/main/java/io/element/android/x/MainViewModel.kt +++ b/app/src/main/java/io/element/android/x/MainViewModel.kt @@ -2,11 +2,16 @@ package io.element.android.x import androidx.lifecycle.ViewModel import io.element.android.x.matrix.MatrixInstance +import kotlinx.coroutines.flow.first class MainViewModel : ViewModel() { private val matrix = MatrixInstance.getInstance() - suspend fun hasSession(): Boolean { - return matrix.restoreSession() != null + suspend fun isLoggedIn(): Boolean { + return matrix.isLoggedIn().first() + } + + suspend fun restoreSession() { + matrix.restoreSession() } } \ No newline at end of file diff --git a/app/src/main/java/io/element/android/x/Navigation.kt b/app/src/main/java/io/element/android/x/Navigation.kt index 8342655064..1006c4ab75 100644 --- a/app/src/main/java/io/element/android/x/Navigation.kt +++ b/app/src/main/java/io/element/android/x/Navigation.kt @@ -4,17 +4,25 @@ import androidx.compose.runtime.Composable import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.RootNavGraph import com.ramcosta.composedestinations.navigation.DestinationsNavigator +import com.ramcosta.composedestinations.navigation.popUpTo import io.element.android.x.destinations.LoginScreenNavigationDestination +import io.element.android.x.destinations.MessagesScreenNavigationDestination import io.element.android.x.destinations.RoomListScreenNavigationDestination import io.element.android.x.features.login.LoginScreen +import io.element.android.x.features.messages.MessagesScreen import io.element.android.x.features.roomlist.RoomListScreen +import io.element.android.x.matrix.core.RoomId @Destination @Composable fun LoginScreenNavigation(navigator: DestinationsNavigator) { LoginScreen( onLoginWithSuccess = { - navigator.clearBackStack(RoomListScreenNavigationDestination) + navigator.navigate(RoomListScreenNavigationDestination){ + popUpTo(LoginScreenNavigationDestination){ + inclusive = true + } + } } ) } @@ -23,9 +31,24 @@ fun LoginScreenNavigation(navigator: DestinationsNavigator) { @Destination @Composable fun RoomListScreenNavigation(navigator: DestinationsNavigator) { - RoomListScreen(onSuccessLogout = { - navigator.clearBackStack(LoginScreenNavigationDestination) - }) + RoomListScreen( + onRoomClicked = { roomId: RoomId -> + navigator.navigate(MessagesScreenNavigationDestination(roomId = roomId.value)) + }, + onSuccessLogout = { + navigator.navigate(LoginScreenNavigationDestination){ + popUpTo(RoomListScreenNavigationDestination){ + inclusive = true + } + } + }) +} + +@Destination +@Composable +fun MessagesScreenNavigation(roomId: String) { + MessagesScreen(roomId) } + 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 ce1d093925..f286942c13 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,10 +1,6 @@ package io.element.android.x.features.login -import android.util.Log -import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModel -import com.airbnb.mvrx.Success import io.element.android.x.matrix.MatrixInstance import kotlinx.coroutines.launch @@ -24,13 +20,11 @@ class LoginViewModel(initialState: LoginViewState) : private fun handleSubmit() = withState { state -> viewModelScope.launch { - setState { copy(isLoggedIn = Loading()) } - try { + suspend { matrix.login(state.homeserver, state.login, state.password) - setState { copy(isLoggedIn = Success(Unit)) } - } catch (throwable: Throwable) { - Log.e("Error", "Cannot login", throwable) - setState { copy(isLoggedIn = Fail(throwable)) } + Unit + }.execute { + copy(isLoggedIn = it) } } } diff --git a/features/messages/build.gradle b/features/messages/build.gradle deleted file mode 100644 index b67bfc7680..0000000000 --- a/features/messages/build.gradle +++ /dev/null @@ -1,41 +0,0 @@ -plugins { - id 'com.android.library' - id 'org.jetbrains.kotlin.android' -} - -android { - namespace 'io.element.android.x.features.messages' - compileSdk 32 - - defaultConfig { - minSdk 24 - targetSdk 32 - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles "consumer-rules.pro" - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - kotlinOptions { - jvmTarget = '1.8' - } -} - -dependencies { - - implementation 'androidx.core:core-ktx:1.7.0' - implementation 'androidx.appcompat:appcompat:1.4.1' - implementation 'com.google.android.material:material:1.5.0' - testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test.ext:junit:1.1.3' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' -} \ No newline at end of file diff --git a/features/messages/build.gradle.kts b/features/messages/build.gradle.kts new file mode 100644 index 0000000000..93405fa512 --- /dev/null +++ b/features/messages/build.gradle.kts @@ -0,0 +1,19 @@ +plugins { + id("io.element.android-compose") +} + +android { + namespace = "io.element.android.x.features.messages" +} + +dependencies { + implementation(project(":libraries:core")) + implementation(project(":libraries:matrix")) + implementation(project(":libraries:designsystem")) + implementation(libs.mavericks.compose) + implementation(libs.timber) + implementation(libs.datetime) + testImplementation("junit:junit:4.13.2") + androidTestImplementation("androidx.test.ext:junit:1.1.3") + androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0") +} \ No newline at end of file diff --git a/features/messages/proguard-rules.pro b/features/messages/proguard-rules.pro index 481bb43481..ff59496d81 100644 --- a/features/messages/proguard-rules.pro +++ b/features/messages/proguard-rules.pro @@ -1,6 +1,6 @@ # Add project specific ProGuard rules here. # You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. +# proguardFiles setting in build.gradle.kts. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/MessagesScreen.kt b/features/messages/src/main/java/io/element/android/x/features/messages/MessagesScreen.kt new file mode 100644 index 0000000000..51b4169e56 --- /dev/null +++ b/features/messages/src/main/java/io/element/android/x/features/messages/MessagesScreen.kt @@ -0,0 +1,44 @@ +@file:OptIn(ExperimentalMaterial3Api::class) + +package io.element.android.x.features.messages + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.nestedScroll +import com.airbnb.mvrx.compose.collectAsState +import com.airbnb.mvrx.compose.mavericksViewModel +import io.element.android.x.core.data.LogCompositions +import io.element.android.x.features.messages.model.MessagesViewState + +@Composable +fun MessagesScreen(roomId: String) { + val viewModel: MessagesViewModel = mavericksViewModel(argsFactory = { roomId }) + LogCompositions(tag = "MessagesScreen", msg = "Root") + val roomTitle by viewModel.collectAsState(prop1 = MessagesViewState::roomTitle) + MessagesContent(roomTitle) +} + +@Composable +fun MessagesContent(roomTitle: String) { + val appBarState = rememberTopAppBarState() + val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(appBarState) + LogCompositions(tag = "RoomListScreen", msg = "Content") + Scaffold( + modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), + topBar = { + TopAppBar( + title = { Text(text = roomTitle) } + ) + }, + content = { padding -> + Box(modifier = Modifier.padding(padding)) + } + ) +} + + + diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/MessagesViewModel.kt b/features/messages/src/main/java/io/element/android/x/features/messages/MessagesViewModel.kt new file mode 100644 index 0000000000..f0d5a877c2 --- /dev/null +++ b/features/messages/src/main/java/io/element/android/x/features/messages/MessagesViewModel.kt @@ -0,0 +1,62 @@ +package io.element.android.x.features.messages + +import com.airbnb.mvrx.Fail +import com.airbnb.mvrx.Loading +import com.airbnb.mvrx.MavericksViewModel +import com.airbnb.mvrx.Success +import io.element.android.x.core.data.parallelMap +import io.element.android.x.designsystem.components.avatar.AvatarData +import io.element.android.x.designsystem.components.avatar.AvatarSize +import io.element.android.x.features.messages.model.MessagesViewState +import io.element.android.x.matrix.MatrixClient +import io.element.android.x.matrix.MatrixInstance +import io.element.android.x.matrix.room.RoomSummary +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch +import org.matrix.rustcomponents.sdk.mediaSourceFromUrl + +class MessagesViewModel(initialState: MessagesViewState) : + MavericksViewModel(initialState) { + + private val matrix = MatrixInstance.getInstance() + + init { + handleInit() + } + + private fun handleInit() { + viewModelScope.launch { + + + } + } + + private suspend fun loadAvatarData( + client: MatrixClient, + name: String, + url: String?, + size: AvatarSize = AvatarSize.MEDIUM + ): AvatarData { + val mediaContent = url?.let { + val mediaSource = mediaSourceFromUrl(it) + client.loadMediaThumbnailForSource(mediaSource, size.value.toLong(), size.value.toLong()) + } + return mediaContent?.fold( + { it }, + { null } + ).let { model -> + AvatarData(name.first().uppercase(), model, size) + } + } + + private suspend fun getClient(): MatrixClient { + return matrix.matrixClient().first().get() + } + + override fun onCleared() { + super.onCleared() + } +} \ No newline at end of file diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/model/MessagesViewState.kt b/features/messages/src/main/java/io/element/android/x/features/messages/model/MessagesViewState.kt new file mode 100644 index 0000000000..d3656966f5 --- /dev/null +++ b/features/messages/src/main/java/io/element/android/x/features/messages/model/MessagesViewState.kt @@ -0,0 +1,16 @@ +package io.element.android.x.features.messages.model + +import com.airbnb.mvrx.MavericksState +import io.element.android.x.designsystem.components.avatar.AvatarData +import io.element.android.x.matrix.core.RoomId + +data class MessagesViewState( + val roomId: String, + val roomTitle: String = "", + val roomAvatar: AvatarData? = null +) : MavericksState { + + @Suppress("unused") + constructor(roomId: String) : this(roomId = roomId, roomTitle = "", roomAvatar = null) + +} diff --git a/features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListScreen.kt b/features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListScreen.kt index 53368503b3..9c92649be1 100644 --- a/features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListScreen.kt +++ b/features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListScreen.kt @@ -14,11 +14,13 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.tooling.preview.Preview +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.data.LogCompositions import io.element.android.x.designsystem.ElementXTheme +import io.element.android.x.designsystem.components.ProgressDialog import io.element.android.x.designsystem.components.avatar.AvatarData import io.element.android.x.features.roomlist.components.RoomItem import io.element.android.x.features.roomlist.components.RoomListTopBar @@ -46,7 +48,8 @@ fun RoomListScreen( roomSummaries = roomSummaries().orEmpty(), matrixUser = matrixUser(), onRoomClicked = onRoomClicked, - onLogoutClicked = viewModel::logout + onLogoutClicked = viewModel::logout, + isLoginOut = logoutAction is Loading ) } @@ -56,6 +59,7 @@ fun RoomListContent( matrixUser: MatrixUser?, onRoomClicked: (RoomId) -> Unit, onLogoutClicked: () -> Unit, + isLoginOut: Boolean, ) { val appBarState = rememberTopAppBarState() val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(appBarState) @@ -75,6 +79,9 @@ fun RoomListContent( } } ) + if (isLoginOut) { + ProgressDialog("Login out...") + } } @@ -86,7 +93,8 @@ private fun PreviewableRoomListContent() { roomSummaries = stubbedRoomSummaries(), matrixUser = MatrixUser("User#1", avatarData = AvatarData("U")), onRoomClicked = {}, - onLogoutClicked = {} + onLogoutClicked = {}, + isLoginOut = false ) } } @@ -99,7 +107,8 @@ private fun PreviewableDarkRoomListContent() { roomSummaries = stubbedRoomSummaries(), matrixUser = MatrixUser("User#1", avatarData = AvatarData("U")), onRoomClicked = {}, - onLogoutClicked = {} + onLogoutClicked = {}, + isLoginOut = true ) } } diff --git a/features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListViewModel.kt b/features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListViewModel.kt index 098e55359a..1fd8728c72 100644 --- a/features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListViewModel.kt +++ b/features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListViewModel.kt @@ -1,9 +1,6 @@ package io.element.android.x.features.roomlist -import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModel -import com.airbnb.mvrx.Success import io.element.android.x.core.data.parallelMap import io.element.android.x.designsystem.components.avatar.AvatarData import io.element.android.x.designsystem.components.avatar.AvatarSize @@ -14,6 +11,8 @@ import io.element.android.x.matrix.MatrixClient import io.element.android.x.matrix.MatrixInstance import io.element.android.x.matrix.room.RoomSummary import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch @@ -47,7 +46,12 @@ class RoomListViewModel(initialState: RoomListViewState) : val userAvatarUrl = client.loadUserAvatarURLString().getOrNull() val userDisplayName = client.loadUserDisplayName().getOrNull() val avatarData = - loadAvatarData(client, userDisplayName ?: client.userId().value, userAvatarUrl, AvatarSize.SMALL) + loadAvatarData( + client, + userDisplayName ?: client.userId().value, + userAvatarUrl, + AvatarSize.SMALL + ) MatrixUser( username = userDisplayName ?: client.userId().value, avatarUrl = userAvatarUrl, @@ -101,7 +105,11 @@ class RoomListViewModel(initialState: RoomListViewState) : ): AvatarData { val mediaContent = url?.let { val mediaSource = mediaSourceFromUrl(it) - client.loadMediaThumbnailForSource(mediaSource, size.value.toLong(), size.value.toLong()) + client.loadMediaThumbnailForSource( + mediaSource, + size.value.toLong(), + size.value.toLong() + ) } return mediaContent?.fold( { it }, @@ -113,18 +121,17 @@ class RoomListViewModel(initialState: RoomListViewState) : private fun handleLogout() { viewModelScope.launch { - setState { copy(logoutAction = Loading()) } - try { + suspend { + delay(2000) getClient().logout() - setState { copy(logoutAction = Success(Unit)) } - } catch (throwable: Throwable) { - setState { copy(logoutAction = Fail(throwable)) } + }.execute { + copy(logoutAction = it) } } } private suspend fun getClient(): MatrixClient { - return matrix.restoreSession()!! + return matrix.matrixClient().first().get() } override fun onCleared() { diff --git a/libraries/designsystem/src/main/java/io/element/android/x/designsystem/Theme.kt b/libraries/designsystem/src/main/java/io/element/android/x/designsystem/Theme.kt index d41f75b7e1..ca6e3c1f82 100644 --- a/libraries/designsystem/src/main/java/io/element/android/x/designsystem/Theme.kt +++ b/libraries/designsystem/src/main/java/io/element/android/x/designsystem/Theme.kt @@ -1,16 +1,12 @@ package io.element.android.x.designsystem -import android.app.Activity import android.os.Build import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.runtime.SideEffect import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalView -import androidx.core.view.WindowCompat import com.google.accompanist.systemuicontroller.rememberSystemUiController private val DarkColorScheme = darkColorScheme( diff --git a/libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/ProgressDialog.kt b/libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/ProgressDialog.kt new file mode 100644 index 0000000000..997d73b1da --- /dev/null +++ b/libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/ProgressDialog.kt @@ -0,0 +1,39 @@ +package io.element.android.x.designsystem.components + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog +import androidx.compose.ui.window.DialogProperties + +@Composable +fun ProgressDialog(text: String? = null, onDismiss: () -> Unit = {}) { + Dialog( + onDismissRequest = onDismiss, + DialogProperties(dismissOnBackPress = false, dismissOnClickOutside = false) + ) { + Box( + contentAlignment = Alignment.Center, + modifier = Modifier + .fillMaxWidth() + .background( + MaterialTheme.colorScheme.onBackground, + shape = RoundedCornerShape(8.dp) + ) + ) { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + CircularProgressIndicator(modifier = Modifier.padding(16.dp), color = MaterialTheme.colorScheme.background) + if (!text.isNullOrBlank()) { + Text(text = text, Modifier.padding(16.dp)) + } + } + } + } +} \ 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 459359484a..a2c743cd96 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 @@ -4,15 +4,20 @@ import android.content.Context import io.element.android.x.core.data.CoroutineDispatchers import io.element.android.x.matrix.session.SessionStore import io.element.android.x.matrix.util.logError +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.withContext import org.matrix.rustcomponents.sdk.AuthenticationService +import org.matrix.rustcomponents.sdk.Client import org.matrix.rustcomponents.sdk.ClientBuilder import java.io.File +import java.util.Optional class Matrix( + coroutineScope: CoroutineScope, context: Context, ) { - private val coroutineDispatchers = CoroutineDispatchers( io = Dispatchers.IO, computation = Dispatchers.Default, @@ -20,9 +25,31 @@ class Matrix( ) private val baseFolder = File(context.filesDir, "matrix") private val sessionStore = SessionStore(context) + private val matrixClient = MutableStateFlow>(Optional.empty()) + private val isLoggedIn = MutableStateFlow(false) - suspend fun restoreSession(): MatrixClient? { - return sessionStore.getStoredData() + init { + sessionStore.isLoggedIn() + .distinctUntilChanged() + .onEach { isLoggedIn -> + this.isLoggedIn.value = isLoggedIn + if (!isLoggedIn) { + matrixClient.value = Optional.empty() + } + } + .launchIn(coroutineScope) + } + + fun isLoggedIn(): Flow { + return isLoggedIn + } + + fun matrixClient(): Flow> { + return matrixClient + } + + suspend fun restoreSession() = withContext(coroutineDispatchers.io) { + sessionStore.getStoredData() ?.let { sessionData -> try { ClientBuilder() @@ -36,15 +63,26 @@ class Matrix( null } }?.let { - MatrixClient(it, sessionStore, coroutineDispatchers) + createMatrixClient(it) } } - suspend fun login(homeserver: String, username: String, password: String): MatrixClient { - val authService = AuthenticationService(baseFolder.absolutePath) - authService.configureHomeserver(homeserver) - val client = authService.login(username, password, "MatrixRustSDKSample", null) - sessionStore.storeData(SessionStore.SessionData(client.userId(), client.restoreToken())) - return MatrixClient(client, sessionStore, coroutineDispatchers) + suspend fun login(homeserver: String, username: String, password: String): MatrixClient = + withContext(coroutineDispatchers.io) { + val authService = AuthenticationService(baseFolder.absolutePath) + authService.configureHomeserver(homeserver) + val client = authService.login(username, password, "MatrixRustSDKSample", null) + sessionStore.storeData(SessionStore.SessionData(client.userId(), client.restoreToken())) + createMatrixClient(client) + } + + private fun createMatrixClient(client: Client): MatrixClient { + return MatrixClient( + client = client, + sessionStore = sessionStore, + dispatchers = coroutineDispatchers + ).also { + matrixClient.value = Optional.of(it) + } } } \ No newline at end of file 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 e7cc71e996..f974b322a1 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 @@ -61,7 +61,6 @@ class MatrixClient internal constructor( init { client.setDelegate(clientDelegate) } - fun startSync() { slidingSync.setObserver(slidingSyncObserver) slidingSyncObserverToken = slidingSync.sync() diff --git a/libraries/matrix/src/main/java/io/element/android/x/matrix/MatrixInstance.kt b/libraries/matrix/src/main/java/io/element/android/x/matrix/MatrixInstance.kt index f7217b1695..4bb65d2af2 100644 --- a/libraries/matrix/src/main/java/io/element/android/x/matrix/MatrixInstance.kt +++ b/libraries/matrix/src/main/java/io/element/android/x/matrix/MatrixInstance.kt @@ -2,6 +2,7 @@ package io.element.android.x.matrix import android.annotation.SuppressLint import android.content.Context +import kotlinx.coroutines.GlobalScope object MatrixInstance { @@ -9,7 +10,7 @@ object MatrixInstance { private lateinit var instance: Matrix fun init(context: Context) { - instance = Matrix(context) + instance = Matrix(GlobalScope, context) } fun getInstance(): Matrix { diff --git a/libraries/matrix/src/main/java/io/element/android/x/matrix/session/SessionStore.kt b/libraries/matrix/src/main/java/io/element/android/x/matrix/session/SessionStore.kt index ff39c6ce3e..44815490bd 100644 --- a/libraries/matrix/src/main/java/io/element/android/x/matrix/session/SessionStore.kt +++ b/libraries/matrix/src/main/java/io/element/android/x/matrix/session/SessionStore.kt @@ -6,10 +6,13 @@ import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.preferencesDataStore +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.map private val Context.dataStore: DataStore by preferencesDataStore(name = "elementx_sessions") private val userIdPreference = stringPreferencesKey("userId") + // TODO It contains the access token, so it has to be stored in a more secured storage. // I would expect the Rust SDK to provide a more obscure token. private val restoreTokenPreference = stringPreferencesKey("restoreToken") @@ -25,6 +28,11 @@ internal class SessionStore( private val store = context.dataStore + fun isLoggedIn(): Flow { + return store.data.map { prefs -> + prefs[userIdPreference] != null && prefs[restoreTokenPreference] != null + } + } suspend fun storeData(sessionData: SessionData) { store.edit { prefs ->