Introduce SessionComponent
This commit is contained in:
@@ -3,29 +3,33 @@ package io.element.android.x
|
||||
import android.app.Application
|
||||
import androidx.startup.AppInitializer
|
||||
import io.element.android.x.core.di.DaggerComponentOwner
|
||||
import io.element.android.x.core.di.bindings
|
||||
import io.element.android.x.di.AppBindings
|
||||
import io.element.android.x.di.AppComponent
|
||||
import io.element.android.x.di.DaggerAppComponent
|
||||
import io.element.android.x.di.SessionComponentsOwner
|
||||
import io.element.android.x.initializer.CoilInitializer
|
||||
import io.element.android.x.initializer.MatrixInitializer
|
||||
import io.element.android.x.initializer.MavericksInitializer
|
||||
import io.element.android.x.matrix.MatrixInstance
|
||||
import kotlinx.coroutines.CoroutineName
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.plus
|
||||
|
||||
class ElementXApplication : Application(), DaggerComponentOwner {
|
||||
|
||||
override lateinit var daggerComponent: Any
|
||||
private lateinit var appComponent: AppComponent
|
||||
private var sessionComponentsOwner: SessionComponentsOwner? = null
|
||||
|
||||
private val applicationScope = MainScope() + CoroutineName("ElementX Scope")
|
||||
override val daggerComponent: Any
|
||||
get() = listOfNotNull(sessionComponentsOwner?.activeSessionComponent, appComponent)
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
daggerComponent = DaggerAppComponent.factory().create(applicationContext)
|
||||
MatrixInstance.init(this, applicationScope)
|
||||
appComponent = DaggerAppComponent.factory().create(applicationContext)
|
||||
sessionComponentsOwner = bindings<AppBindings>().sessionComponentsOwner()
|
||||
AppInitializer.getInstance(this).apply {
|
||||
initializeComponent(MatrixInitializer::class.java)
|
||||
initializeComponent(CoilInitializer::class.java)
|
||||
initializeComponent(MavericksInitializer::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -8,8 +8,7 @@ import dagger.assisted.AssistedInject
|
||||
import io.element.android.x.anvilannotations.ContributesViewModel
|
||||
import io.element.android.x.core.di.daggerMavericksViewModelFactory
|
||||
import io.element.android.x.di.AppScope
|
||||
import io.element.android.x.features.messages.MessagesViewModel
|
||||
import io.element.android.x.features.messages.model.MessagesViewState
|
||||
import io.element.android.x.di.SessionComponentsOwner
|
||||
import io.element.android.x.matrix.Matrix
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -19,10 +18,12 @@ data class MainState(val fake: Boolean = false) : MavericksState
|
||||
@ContributesViewModel(AppScope::class)
|
||||
class MainViewModel @AssistedInject constructor(
|
||||
private val matrix: Matrix,
|
||||
private val sessionComponentsOwner: SessionComponentsOwner,
|
||||
@Assisted initialState: MainState
|
||||
) : MavericksViewModel<MainState>(initialState) {
|
||||
|
||||
companion object : MavericksViewModelFactory<MainViewModel, MainState> by daggerMavericksViewModelFactory()
|
||||
companion object :
|
||||
MavericksViewModelFactory<MainViewModel, MainState> by daggerMavericksViewModelFactory()
|
||||
|
||||
suspend fun isLoggedIn(): Boolean {
|
||||
return matrix.isLoggedIn().first()
|
||||
@@ -31,19 +32,22 @@ class MainViewModel @AssistedInject constructor(
|
||||
fun startSyncIfLogged() {
|
||||
viewModelScope.launch {
|
||||
if (!isLoggedIn()) return@launch
|
||||
matrix.activeClient().startSync()
|
||||
}
|
||||
}
|
||||
|
||||
fun stopSyncIfLogged() {
|
||||
viewModelScope.launch {
|
||||
if (!isLoggedIn()) return@launch
|
||||
matrix.activeClient().stopSync()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun restoreSession() {
|
||||
matrix.restoreSession()
|
||||
matrix.activeClient().startSync()
|
||||
val matrixClient = matrix.restoreSession()
|
||||
if (matrixClient == null) {
|
||||
throw IllegalStateException("Couldn't restore session...")
|
||||
} else {
|
||||
sessionComponentsOwner.create(matrixClient)
|
||||
matrixClient.startSync()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,14 @@
|
||||
package io.element.android.x
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
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.core.di.bindings
|
||||
import io.element.android.x.destinations.*
|
||||
import io.element.android.x.di.AppBindings
|
||||
import io.element.android.x.features.login.LoginScreen
|
||||
import io.element.android.x.features.login.changeserver.ChangeServerScreen
|
||||
import io.element.android.x.features.messages.MessagesScreen
|
||||
@@ -29,11 +32,13 @@ fun OnBoardingScreenNavigation(navigator: DestinationsNavigator) {
|
||||
@Destination
|
||||
@Composable
|
||||
fun LoginScreenNavigation(navigator: DestinationsNavigator) {
|
||||
val sessionComponentsOwner = LocalContext.current.bindings<AppBindings>().sessionComponentsOwner()
|
||||
LoginScreen(
|
||||
onChangeServer = {
|
||||
navigator.navigate(ChangeServerScreenNavigationDestination)
|
||||
},
|
||||
onLoginWithSuccess = {
|
||||
sessionComponentsOwner.create(it)
|
||||
navigator.navigate(RoomListScreenNavigationDestination) {
|
||||
popUpTo(OnBoardingScreenNavigationDestination) {
|
||||
inclusive = true
|
||||
@@ -58,11 +63,13 @@ fun ChangeServerScreenNavigation(navigator: DestinationsNavigator) {
|
||||
@Destination
|
||||
@Composable
|
||||
fun RoomListScreenNavigation(navigator: DestinationsNavigator) {
|
||||
val sessionComponentsOwner = LocalContext.current.bindings<AppBindings>().sessionComponentsOwner()
|
||||
RoomListScreen(
|
||||
onRoomClicked = { roomId: RoomId ->
|
||||
navigator.navigate(MessagesScreenNavigationDestination(roomId = roomId.value))
|
||||
},
|
||||
onSuccessLogout = {
|
||||
sessionComponentsOwner.releaseActiveSession()
|
||||
navigator.navigate(OnBoardingScreenNavigationDestination) {
|
||||
popUpTo(RoomListScreenNavigationDestination) {
|
||||
inclusive = true
|
||||
@@ -75,7 +82,7 @@ fun RoomListScreenNavigation(navigator: DestinationsNavigator) {
|
||||
@Destination
|
||||
@Composable
|
||||
fun MessagesScreenNavigation(roomId: String, navigator: DestinationsNavigator) {
|
||||
MessagesScreen(roomId, navigator::navigateUp)
|
||||
MessagesScreen(roomId = roomId, onBackPressed = navigator::navigateUp)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package io.element.android.x.di
|
||||
|
||||
import com.squareup.anvil.annotations.ContributesTo
|
||||
import io.element.android.x.di.AppScope
|
||||
import io.element.android.x.matrix.Matrix
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
||||
@@ -9,4 +8,5 @@ import kotlinx.coroutines.CoroutineScope
|
||||
interface AppBindings {
|
||||
fun coroutineScope(): CoroutineScope
|
||||
fun matrix(): Matrix
|
||||
fun sessionComponentsOwner(): SessionComponentsOwner
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package io.element.android.x.di
|
||||
|
||||
import com.squareup.anvil.annotations.ContributesTo
|
||||
import com.squareup.anvil.annotations.MergeSubcomponent
|
||||
import dagger.BindsInstance
|
||||
import dagger.Subcomponent
|
||||
import io.element.android.x.core.di.DaggerMavericksBindings
|
||||
import io.element.android.x.matrix.MatrixClient
|
||||
|
||||
@SingleIn(SessionScope::class)
|
||||
@MergeSubcomponent(SessionScope::class)
|
||||
interface SessionComponent: DaggerMavericksBindings {
|
||||
|
||||
fun matrixClient(): MatrixClient
|
||||
|
||||
@Subcomponent.Builder
|
||||
interface Builder {
|
||||
@BindsInstance
|
||||
fun client(matrixClient: MatrixClient): Builder
|
||||
fun build(): SessionComponent
|
||||
}
|
||||
|
||||
@ContributesTo(AppScope::class)
|
||||
interface ParentBindings {
|
||||
fun sessionComponentBuilder(): Builder
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package io.element.android.x.di
|
||||
|
||||
import android.content.Context
|
||||
import io.element.android.x.core.di.bindings
|
||||
import io.element.android.x.matrix.MatrixClient
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import javax.inject.Inject
|
||||
|
||||
@SingleIn(AppScope::class)
|
||||
class SessionComponentsOwner @Inject constructor(@ApplicationContext private val context: Context) {
|
||||
|
||||
private val sessionComponents = ConcurrentHashMap<String, SessionComponent>()
|
||||
var activeSessionComponent: SessionComponent? = null
|
||||
private set
|
||||
|
||||
fun setActive(sessionId: String) {
|
||||
val sessionComponent = sessionComponents[sessionId]
|
||||
if (activeSessionComponent != sessionComponent) {
|
||||
activeSessionComponent = sessionComponent
|
||||
}
|
||||
}
|
||||
|
||||
fun create(matrixClient: MatrixClient) {
|
||||
val sessionId = matrixClient.sessionId
|
||||
val sessionComponent =
|
||||
context.bindings<SessionComponent.ParentBindings>().sessionComponentBuilder()
|
||||
.client(matrixClient).build()
|
||||
sessionComponents[sessionId] = sessionComponent
|
||||
setActive(sessionId)
|
||||
}
|
||||
|
||||
fun releaseActiveSession() {
|
||||
activeSessionComponent?.also {
|
||||
release(it.matrixClient().sessionId)
|
||||
}
|
||||
}
|
||||
|
||||
fun release(sessionId: String) {
|
||||
val sessionComponent = sessionComponents.remove(sessionId)
|
||||
if (activeSessionComponent == sessionComponent) {
|
||||
activeSessionComponent = null
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,7 +25,13 @@ private class ElementImageLoaderFactory(
|
||||
return ImageLoader
|
||||
.Builder(context)
|
||||
.components {
|
||||
context.bindings<AppBindings>().matrix().registerCoilComponents(this)
|
||||
val appBindings = context.bindings<AppBindings>()
|
||||
val matrix = appBindings.matrix()
|
||||
val matrixClientProvider = {
|
||||
appBindings
|
||||
.sessionComponentsOwner().activeSessionComponent?.matrixClient()
|
||||
}
|
||||
matrix.registerCoilComponents(this, matrixClientProvider)
|
||||
}
|
||||
.build()
|
||||
}
|
||||
|
||||
@@ -30,13 +30,14 @@ import com.airbnb.mvrx.compose.collectAsState
|
||||
import com.airbnb.mvrx.compose.mavericksViewModel
|
||||
import io.element.android.x.designsystem.ElementXTheme
|
||||
import io.element.android.x.features.login.error.loginError
|
||||
import io.element.android.x.matrix.MatrixClient
|
||||
import timber.log.Timber
|
||||
|
||||
@Composable
|
||||
fun LoginScreen(
|
||||
viewModel: LoginViewModel = mavericksViewModel(),
|
||||
onChangeServer: () -> Unit = { },
|
||||
onLoginWithSuccess: () -> Unit = { },
|
||||
onLoginWithSuccess: (MatrixClient) -> Unit = { },
|
||||
) {
|
||||
val state: LoginViewState by viewModel.collectAsState()
|
||||
val formState: LoginFormState by viewModel.formState
|
||||
@@ -65,7 +66,7 @@ fun LoginContent(
|
||||
onLoginChanged: (String) -> Unit = {},
|
||||
onPasswordChanged: (String) -> Unit = {},
|
||||
onSubmitClicked: () -> Unit = {},
|
||||
onLoginWithSuccess: () -> Unit = {},
|
||||
onLoginWithSuccess: (MatrixClient) -> Unit = {},
|
||||
) {
|
||||
Surface(color = MaterialTheme.colorScheme.background) {
|
||||
Box(
|
||||
@@ -82,7 +83,7 @@ fun LoginContent(
|
||||
)
|
||||
.padding(horizontal = 16.dp),
|
||||
) {
|
||||
val isError = state.isLoggedIn is Fail
|
||||
val isError = state.loggedInClient is Fail
|
||||
// Title
|
||||
Text(
|
||||
text = "Welcome back",
|
||||
@@ -137,7 +138,7 @@ fun LoginContent(
|
||||
),
|
||||
)
|
||||
var passwordVisible by remember { mutableStateOf(false) }
|
||||
if (state.isLoggedIn is Loading) {
|
||||
if (state.loggedInClient is Loading) {
|
||||
// Ensure password is hidden when user submits the form
|
||||
passwordVisible = false
|
||||
}
|
||||
@@ -170,9 +171,9 @@ fun LoginContent(
|
||||
onDone = { onSubmitClicked() }
|
||||
),
|
||||
)
|
||||
if (state.isLoggedIn is Fail) {
|
||||
if (state.loggedInClient is Fail) {
|
||||
Text(
|
||||
text = loginError(state.formState, state.isLoggedIn.error),
|
||||
text = loginError(state.formState, state.loggedInClient.error),
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
modifier = Modifier.padding(start = 16.dp)
|
||||
@@ -189,11 +190,12 @@ fun LoginContent(
|
||||
) {
|
||||
Text(text = "Continue")
|
||||
}
|
||||
if (state.isLoggedIn is Success) {
|
||||
onLoginWithSuccess()
|
||||
when (val loggedInClient = state.loggedInClient) {
|
||||
is Success -> onLoginWithSuccess(loggedInClient())
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
if (state.isLoggedIn is Loading) {
|
||||
if (state.loggedInClient is Loading) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.align(Alignment.Center)
|
||||
)
|
||||
|
||||
@@ -11,7 +11,6 @@ import io.element.android.x.anvilannotations.ContributesViewModel
|
||||
import io.element.android.x.core.di.daggerMavericksViewModelFactory
|
||||
import io.element.android.x.di.AppScope
|
||||
import io.element.android.x.matrix.Matrix
|
||||
import io.element.android.x.matrix.MatrixInstance
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -49,21 +48,22 @@ class LoginViewModel @AssistedInject constructor(
|
||||
val state = awaitState()
|
||||
// Ensure the server is provided to the Rust SDK
|
||||
matrix.setHomeserver(state.homeserver)
|
||||
matrix.login(state.formState.login.trim(), state.formState.password.trim())
|
||||
matrix.activeClient().startSync()
|
||||
matrix.login(state.formState.login.trim(), state.formState.password.trim()).also {
|
||||
it.startSync()
|
||||
}
|
||||
}.execute {
|
||||
copy(isLoggedIn = it)
|
||||
copy(loggedInClient = it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onSetPassword(password: String) {
|
||||
formState.value = formState.value.copy(password = password)
|
||||
setState { copy(isLoggedIn = Uninitialized) }
|
||||
setState { copy(loggedInClient = Uninitialized) }
|
||||
}
|
||||
|
||||
fun onSetName(name: String) {
|
||||
formState.value = formState.value.copy(login = name)
|
||||
setState { copy(isLoggedIn = Uninitialized) }
|
||||
setState { copy(loggedInClient = Uninitialized) }
|
||||
}
|
||||
}
|
||||
@@ -4,14 +4,15 @@ import com.airbnb.mvrx.Async
|
||||
import com.airbnb.mvrx.Loading
|
||||
import com.airbnb.mvrx.MavericksState
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
import io.element.android.x.matrix.MatrixClient
|
||||
|
||||
data class LoginViewState(
|
||||
val homeserver: String = "",
|
||||
val isLoggedIn: Async<Unit> = Uninitialized,
|
||||
val loggedInClient: Async<MatrixClient> = Uninitialized,
|
||||
val formState: LoginFormState = LoginFormState.Default,
|
||||
) : MavericksState {
|
||||
val submitEnabled =
|
||||
formState.login.isNotEmpty() && formState.password.isNotEmpty() && isLoggedIn !is Loading
|
||||
formState.login.isNotEmpty() && formState.password.isNotEmpty() && loggedInClient !is Loading
|
||||
}
|
||||
|
||||
data class LoginFormState(
|
||||
|
||||
@@ -1,14 +1,25 @@
|
||||
package io.element.android.x.features.login.changeserver
|
||||
|
||||
import com.airbnb.mvrx.MavericksViewModel
|
||||
import com.airbnb.mvrx.MavericksViewModelFactory
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
import io.element.android.x.matrix.MatrixInstance
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import io.element.android.x.anvilannotations.ContributesViewModel
|
||||
import io.element.android.x.core.di.daggerMavericksViewModelFactory
|
||||
import io.element.android.x.di.AppScope
|
||||
import io.element.android.x.matrix.Matrix
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class ChangeServerViewModel(initialState: ChangeServerViewState) :
|
||||
@ContributesViewModel(AppScope::class)
|
||||
class ChangeServerViewModel @AssistedInject constructor(
|
||||
private val matrix: Matrix,
|
||||
@Assisted initialState: ChangeServerViewState
|
||||
) :
|
||||
MavericksViewModel<ChangeServerViewState>(initialState) {
|
||||
|
||||
private val matrix = MatrixInstance.getInstance()
|
||||
companion object :
|
||||
MavericksViewModelFactory<ChangeServerViewModel, ChangeServerViewState> by daggerMavericksViewModelFactory()
|
||||
|
||||
init {
|
||||
setState {
|
||||
@@ -32,7 +43,7 @@ class ChangeServerViewModel(initialState: ChangeServerViewState) :
|
||||
suspend {
|
||||
val state = awaitState()
|
||||
matrix.setHomeserver(state.homeserver)
|
||||
}.execute { it ->
|
||||
}.execute {
|
||||
copy(changeServerAction = it)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import io.element.android.x.core.di.daggerMavericksViewModelFactory
|
||||
import io.element.android.x.designsystem.components.avatar.AvatarData
|
||||
import io.element.android.x.designsystem.components.avatar.AvatarSize
|
||||
import io.element.android.x.di.AppScope
|
||||
import io.element.android.x.di.SessionScope
|
||||
import io.element.android.x.features.messages.model.MessagesItemAction
|
||||
import io.element.android.x.features.messages.model.MessagesItemActionsSheetState
|
||||
import io.element.android.x.features.messages.model.MessagesTimelineItemState
|
||||
@@ -17,6 +18,7 @@ import io.element.android.x.features.messages.model.MessagesViewState
|
||||
import io.element.android.x.features.messages.model.content.MessagesTimelineItemRedactedContent
|
||||
import io.element.android.x.features.messages.model.content.MessagesTimelineItemTextBasedContent
|
||||
import io.element.android.x.matrix.Matrix
|
||||
import io.element.android.x.matrix.MatrixClient
|
||||
import io.element.android.x.matrix.media.MediaResolver
|
||||
import io.element.android.x.matrix.timeline.MatrixTimeline
|
||||
import io.element.android.x.matrix.timeline.MatrixTimelineItem
|
||||
@@ -28,16 +30,15 @@ import kotlinx.coroutines.launch
|
||||
|
||||
private const val PAGINATION_COUNT = 50
|
||||
|
||||
@ContributesViewModel(AppScope::class)
|
||||
@ContributesViewModel(SessionScope::class)
|
||||
class MessagesViewModel @AssistedInject constructor(
|
||||
matrix: Matrix,
|
||||
private val client: MatrixClient,
|
||||
@Assisted private val initialState: MessagesViewState
|
||||
) :
|
||||
MavericksViewModel<MessagesViewState>(initialState) {
|
||||
|
||||
companion object : MavericksViewModelFactory<MessagesViewModel, MessagesViewState> by daggerMavericksViewModelFactory()
|
||||
|
||||
private val client = matrix.activeClient()
|
||||
private val room = client.getRoom(initialState.roomId)!!
|
||||
private val messageTimelineItemStateFactory =
|
||||
MessageTimelineItemStateFactory(client, room, Dispatchers.Default)
|
||||
|
||||
@@ -7,20 +7,18 @@ import dagger.assisted.AssistedInject
|
||||
import io.element.android.x.anvilannotations.ContributesViewModel
|
||||
import io.element.android.x.core.data.StableCharSequence
|
||||
import io.element.android.x.core.di.daggerMavericksViewModelFactory
|
||||
import io.element.android.x.di.AppScope
|
||||
import io.element.android.x.matrix.Matrix
|
||||
import io.element.android.x.di.SessionScope
|
||||
import io.element.android.x.matrix.MatrixClient
|
||||
|
||||
@ContributesViewModel(AppScope::class)
|
||||
@ContributesViewModel(SessionScope::class)
|
||||
class MessageComposerViewModel @AssistedInject constructor(
|
||||
private val matrix: Matrix,
|
||||
private val client: MatrixClient,
|
||||
@Assisted private val initialState: MessageComposerViewState
|
||||
) : MavericksViewModel<MessageComposerViewState>(initialState) {
|
||||
|
||||
companion object :
|
||||
MavericksViewModelFactory<MessageComposerViewModel, MessageComposerViewState> by daggerMavericksViewModelFactory()
|
||||
|
||||
private val client = matrix.activeClient()
|
||||
|
||||
fun onComposerFullScreenChange() {
|
||||
setState {
|
||||
copy(
|
||||
|
||||
@@ -9,13 +9,13 @@ import io.element.android.x.core.di.daggerMavericksViewModelFactory
|
||||
import io.element.android.x.designsystem.components.avatar.AvatarData
|
||||
import io.element.android.x.designsystem.components.avatar.AvatarSize
|
||||
import io.element.android.x.di.AppScope
|
||||
import io.element.android.x.di.SessionScope
|
||||
import io.element.android.x.features.roomlist.model.MatrixUser
|
||||
import io.element.android.x.features.roomlist.model.RoomListRoomSummary
|
||||
import io.element.android.x.features.roomlist.model.RoomListRoomSummaryPlaceholders
|
||||
import io.element.android.x.features.roomlist.model.RoomListViewState
|
||||
import io.element.android.x.matrix.Matrix
|
||||
import io.element.android.x.matrix.MatrixClient
|
||||
import io.element.android.x.matrix.MatrixInstance
|
||||
import io.element.android.x.matrix.media.MediaResolver
|
||||
import io.element.android.x.matrix.room.RoomSummary
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -28,9 +28,9 @@ import kotlinx.coroutines.launch
|
||||
|
||||
private const val extendedRangeSize = 40
|
||||
|
||||
@ContributesViewModel(AppScope::class)
|
||||
@ContributesViewModel(SessionScope::class)
|
||||
class RoomListViewModel @AssistedInject constructor(
|
||||
matrix: Matrix,
|
||||
private val client: MatrixClient,
|
||||
@Assisted initialState: RoomListViewState
|
||||
) :
|
||||
MavericksViewModel<RoomListViewState>(initialState) {
|
||||
@@ -38,7 +38,6 @@ class RoomListViewModel @AssistedInject constructor(
|
||||
companion object : MavericksViewModelFactory<RoomListViewModel, RoomListViewState> by daggerMavericksViewModelFactory()
|
||||
|
||||
private val lastMessageFormatter = LastMessageFormatter()
|
||||
private val client = matrix.activeClient()
|
||||
|
||||
init {
|
||||
handleInit()
|
||||
|
||||
@@ -3,8 +3,8 @@ package io.element.android.x.matrix
|
||||
import android.content.Context
|
||||
import coil.ComponentRegistry
|
||||
import io.element.android.x.core.coroutine.CoroutineDispatchers
|
||||
import io.element.android.x.di.ApplicationContext
|
||||
import io.element.android.x.di.AppScope
|
||||
import io.element.android.x.di.ApplicationContext
|
||||
import io.element.android.x.di.SingleIn
|
||||
import io.element.android.x.matrix.media.MediaFetcher
|
||||
import io.element.android.x.matrix.media.MediaKeyer
|
||||
@@ -37,35 +37,18 @@ class Matrix @Inject constructor(
|
||||
)
|
||||
private val baseDirectory = File(context.filesDir, "sessions")
|
||||
private val sessionStore = SessionStore(context)
|
||||
private val matrixClient = MutableStateFlow<Optional<MatrixClient>>(Optional.empty())
|
||||
private val authService = AuthenticationService(baseDirectory.absolutePath)
|
||||
|
||||
init {
|
||||
sessionStore.isLoggedIn()
|
||||
.distinctUntilChanged()
|
||||
.onEach { isLoggedIn ->
|
||||
if (!isLoggedIn) {
|
||||
matrixClient.value = Optional.empty()
|
||||
}
|
||||
}
|
||||
.launchIn(coroutineScope)
|
||||
}
|
||||
|
||||
fun isLoggedIn(): Flow<Boolean> {
|
||||
return sessionStore.isLoggedIn()
|
||||
}
|
||||
|
||||
fun client(): Flow<Optional<MatrixClient>> {
|
||||
return matrixClient
|
||||
}
|
||||
|
||||
fun activeClient(): MatrixClient {
|
||||
return matrixClient.value.get()
|
||||
}
|
||||
|
||||
fun registerCoilComponents(builder: ComponentRegistry.Builder) {
|
||||
fun registerCoilComponents(
|
||||
builder: ComponentRegistry.Builder,
|
||||
activeClientProvider: () -> MatrixClient?
|
||||
) {
|
||||
builder.add(MediaKeyer())
|
||||
builder.add(MediaFetcher.Factory(this))
|
||||
builder.add(MediaFetcher.Factory(activeClientProvider))
|
||||
}
|
||||
|
||||
suspend fun restoreSession() = withContext(coroutineDispatchers.io) {
|
||||
@@ -116,8 +99,6 @@ class Matrix @Inject constructor(
|
||||
coroutineScope = coroutineScope,
|
||||
dispatchers = coroutineDispatchers,
|
||||
baseDirectory = baseDirectory,
|
||||
).also {
|
||||
matrixClient.value = Optional.of(it)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.element.android.x.matrix
|
||||
|
||||
import io.element.android.x.core.coroutine.CoroutineDispatchers
|
||||
import io.element.android.x.di.SingleIn
|
||||
import io.element.android.x.matrix.core.UserId
|
||||
import io.element.android.x.matrix.media.MediaResolver
|
||||
import io.element.android.x.matrix.media.RustMediaResolver
|
||||
@@ -25,6 +26,9 @@ class MatrixClient internal constructor(
|
||||
private val baseDirectory: File,
|
||||
) : Closeable {
|
||||
|
||||
val sessionId: String
|
||||
get() = "${client.session().userId}_${client.session().deviceId}"
|
||||
|
||||
private val clientDelegate = object : ClientDelegate {
|
||||
override fun didReceiveAuthError(isSoftLogout: Boolean) {
|
||||
Timber.v("didReceiveAuthError()")
|
||||
@@ -78,7 +82,7 @@ class MatrixClient internal constructor(
|
||||
client.setDelegate(clientDelegate)
|
||||
}
|
||||
|
||||
private fun onRestartSync(){
|
||||
private fun onRestartSync() {
|
||||
slidingSyncObserverToken = slidingSync.sync()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
package io.element.android.x.matrix
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
||||
|
||||
object MatrixInstance {
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private lateinit var instance: Matrix
|
||||
|
||||
fun init(context: Application, coroutineScope: CoroutineScope) {
|
||||
instance = Matrix(coroutineScope, context)
|
||||
}
|
||||
|
||||
fun getInstance(): Matrix {
|
||||
return instance
|
||||
}
|
||||
}
|
||||
@@ -4,31 +4,32 @@ import coil.ImageLoader
|
||||
import coil.fetch.FetchResult
|
||||
import coil.fetch.Fetcher
|
||||
import coil.request.Options
|
||||
import io.element.android.x.matrix.Matrix
|
||||
import io.element.android.x.matrix.MatrixClient
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
internal class MediaFetcher(
|
||||
private val mediaResolver: MediaResolver,
|
||||
private val mediaResolver: MediaResolver?,
|
||||
private val meta: MediaResolver.Meta,
|
||||
private val options: Options,
|
||||
private val imageLoader: ImageLoader
|
||||
) : Fetcher {
|
||||
|
||||
override suspend fun fetch(): FetchResult? {
|
||||
val byteArray = mediaResolver.resolve(meta) ?: return null
|
||||
val byteArray = mediaResolver?.resolve(meta) ?: return null
|
||||
val byteBuffer = ByteBuffer.wrap(byteArray)
|
||||
return imageLoader.components.newFetcher(byteBuffer, options, imageLoader)?.first?.fetch()
|
||||
}
|
||||
|
||||
class Factory(private val matrix: Matrix) : Fetcher.Factory<MediaResolver.Meta> {
|
||||
class Factory(private val activeClientProvider: () -> MatrixClient?) :
|
||||
Fetcher.Factory<MediaResolver.Meta> {
|
||||
override fun create(
|
||||
data: MediaResolver.Meta,
|
||||
options: Options,
|
||||
imageLoader: ImageLoader
|
||||
): Fetcher {
|
||||
val activeClient = matrix.activeClient()
|
||||
val activeClient = activeClientProvider()
|
||||
return MediaFetcher(
|
||||
mediaResolver = activeClient.mediaResolver(),
|
||||
mediaResolver = activeClient?.mediaResolver(),
|
||||
meta = data,
|
||||
options = options,
|
||||
imageLoader = imageLoader
|
||||
|
||||
Reference in New Issue
Block a user