Introduce SessionComponent

This commit is contained in:
ganfra 2022-12-16 17:24:55 +01:00
parent 2ea87307a7
commit 9aa0ce9438
18 changed files with 176 additions and 104 deletions

View file

@ -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)
}
}
}

View file

@ -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()
}
}
}

View file

@ -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)
}

View file

@ -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
}

View file

@ -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
}
}

View file

@ -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
}
}
}

View file

@ -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()
}