@@ -2,6 +2,7 @@ package org.cru.godtools.ui.dashboard.lessons
22
33import android.content.Context
44import androidx.compose.runtime.Composable
5+ import androidx.compose.runtime.DisposableEffect
56import androidx.compose.runtime.LaunchedEffect
67import androidx.compose.runtime.collectAsState
78import androidx.compose.runtime.getValue
@@ -11,7 +12,10 @@ import androidx.compose.runtime.remember
1112import androidx.compose.runtime.saveable.rememberSaveable
1213import androidx.compose.runtime.setValue
1314import androidx.compose.runtime.snapshotFlow
15+ import com.google.firebase.remoteconfig.FirebaseRemoteConfig
1416import com.slack.circuit.codegen.annotations.CircuitInject
17+ import com.slack.circuit.runtime.CircuitContext
18+ import com.slack.circuit.runtime.CircuitUiEvent
1519import com.slack.circuit.runtime.CircuitUiState
1620import com.slack.circuit.runtime.Navigator
1721import com.slack.circuit.runtime.presenter.Presenter
@@ -28,56 +32,90 @@ import kotlinx.coroutines.CoroutineDispatcher
2832import kotlinx.coroutines.ExperimentalCoroutinesApi
2933import kotlinx.coroutines.flow.combine
3034import kotlinx.coroutines.flow.distinctUntilChanged
35+ import kotlinx.coroutines.flow.first
3136import kotlinx.coroutines.flow.flatMapLatest
3237import kotlinx.coroutines.flow.flowOn
3338import kotlinx.coroutines.flow.map
3439import org.ccci.gto.android.common.dagger.coroutines.DispatcherType
3540import org.ccci.gto.android.common.dagger.coroutines.DispatcherType.Type.IO
41+ import org.ccci.gto.android.common.sync.SyncTracker
3642import org.cru.godtools.analytics.model.OpenAnalyticsActionEvent
3743import org.cru.godtools.analytics.model.OpenAnalyticsActionEvent.Companion.ACTION_OPEN_LESSON
3844import org.cru.godtools.analytics.model.OpenAnalyticsActionEvent.Companion.SOURCE_LESSONS
45+ import org.cru.godtools.base.CONFIG_UI_DASHBOARD_PERSONALIZATION_ENABLED
3946import org.cru.godtools.base.Settings
4047import org.cru.godtools.base.ui.circuit.screen.dashboard.page.LessonsScreen
4148import org.cru.godtools.db.repository.LanguagesRepository
4249import org.cru.godtools.db.repository.ToolsRepository
4350import org.cru.godtools.db.repository.TranslationsRepository
4451import org.cru.godtools.model.Language
4552import org.cru.godtools.model.Language.Companion.filterByDisplayAndNativeName
53+ import org.cru.godtools.sync.GodToolsSyncService
54+ import org.cru.godtools.ui.dashboard.SyncTaskRegistry.Companion.syncTaskRegistry
4655import org.cru.godtools.ui.dashboard.filters.FilterMenu
4756import org.cru.godtools.ui.dashboard.lessons.LessonsPresenter.UiState
4857import org.cru.godtools.ui.tools.ToolCardPresenter
4958import org.cru.godtools.ui.tools.ToolCardPresenter.ToolCardEvent
5059import org.cru.godtools.util.createToolIntent
5160import org.greenrobot.eventbus.EventBus
5261
53- class LessonsPresenter @AssistedInject constructor(
62+ class LessonsPresenter @AssistedInject internal constructor(
5463 @param:ApplicationContext
5564 private val context : Context ,
5665 private val eventBus : EventBus ,
5766 private val languagesRepository : LanguagesRepository ,
67+ private val lessonsFlowProducer : LessonsFlowProducer ,
68+ private val remoteConfig : FirebaseRemoteConfig ,
5869 private val settings : Settings ,
70+ private val syncService : GodToolsSyncService ,
5971 private val toolCardPresenter : ToolCardPresenter ,
6072 private val toolsRepository : ToolsRepository ,
6173 private val translationsRepository : TranslationsRepository ,
6274 @param:DispatcherType(IO ) private val ioDispatcher : CoroutineDispatcher ,
75+ @Assisted private val circuitContext : CircuitContext ,
6376 @Assisted private val navigator : Navigator ,
6477) : Presenter<UiState> {
65- // region UiState
66- data class UiState (
78+ // region UiState / UiEvent
79+ @ConsistentCopyVisibility
80+ data class UiState internal constructor(
81+ val mode : Mode = Mode .ALL_LESSONS ,
82+ val isPersonalizationEnabled : Boolean = false ,
6783 val languageFilter : FilterMenu .UiState <Language > = FilterMenu .UiState (),
6884 val lessons : List <ToolCardPresenter .UiState > = emptyList(),
69- ) : CircuitUiState
70- // endregion UiState
85+ internal val eventSink : (UiEvent ) -> Unit = {},
86+ ) : CircuitUiState {
87+ enum class Mode { PERSONALIZATION , ALL_LESSONS }
88+ }
89+
90+ internal sealed interface UiEvent : CircuitUiEvent {
91+ data class ChangeMode (val mode : UiState .Mode ) : UiEvent
92+ }
93+ // endregion UiState / UiEvent
7194
7295 @Composable
7396 override fun present (): UiState {
97+ val isPersonalizationEnabled = rememberSaveable {
98+ remoteConfig.getBoolean(CONFIG_UI_DASHBOARD_PERSONALIZATION_ENABLED )
99+ }
100+ var mode by rememberSaveable {
101+ mutableStateOf(if (isPersonalizationEnabled) UiState .Mode .PERSONALIZATION else UiState .Mode .ALL_LESSONS )
102+ }
103+
74104 val appLanguage by settings.appLanguageFlow.collectAsState()
75105 val languageFilter = rememberLanguagesFilter()
76106
107+ RegisterSyncTask (languageFilter.selectedItem?.code ? : appLanguage)
108+
77109 return UiState (
110+ mode = mode,
111+ isPersonalizationEnabled = isPersonalizationEnabled,
78112 languageFilter = languageFilter,
79- lessons = rememberLessons(languageFilter.selectedItem?.code ? : appLanguage),
80- )
113+ lessons = rememberLessons(mode, languageFilter.selectedItem?.code ? : appLanguage),
114+ ) {
115+ when (it) {
116+ is UiEvent .ChangeMode -> mode = it.mode
117+ }
118+ }
81119 }
82120
83121 @Composable
@@ -140,12 +178,8 @@ class LessonsPresenter @AssistedInject constructor(
140178 }
141179
142180 @Composable
143- private fun rememberLessons (locale : Locale ): List <ToolCardPresenter .UiState > {
144- val lessons by remember(locale) {
145- toolsRepository.getLessonsFlowByLanguage(locale)
146- .map { it.filterNot { it.isHidden }.sortedBy { it.defaultOrder } }
147- }.collectAsState(emptyList())
148-
181+ private fun rememberLessons (mode : UiState .Mode , locale : Locale ): List <ToolCardPresenter .UiState > {
182+ val lessons by remember(mode, locale) { lessonsFlowProducer.getFlow(mode, locale) }.collectAsState(emptyList())
149183 return lessons.map { tool ->
150184 key(tool.code) {
151185 lateinit var toolState: ToolCardPresenter .UiState
@@ -176,9 +210,24 @@ class LessonsPresenter @AssistedInject constructor(
176210 }
177211 }
178212
213+ @Composable
214+ private fun RegisterSyncTask (locale : Locale ) {
215+ val syncRegistry = circuitContext.syncTaskRegistry
216+ DisposableEffect (syncRegistry, locale) {
217+ if (syncRegistry == null ) return @DisposableEffect onDispose { }
218+ val id = syncRegistry.registerSyncTask { force -> syncData(locale, force) }
219+ onDispose { syncRegistry.unregisterSyncTask(id) }
220+ }
221+ }
222+
223+ private fun SyncTracker.syncData (locale : Locale , force : Boolean = false) = launchSync {
224+ val country = settings.getCountrySettingFlow().first()
225+ syncService.syncToolOrder(locale, country, force)
226+ }
227+
179228 @AssistedFactory
180229 @CircuitInject(LessonsScreen ::class , SingletonComponent ::class )
181230 interface Factory {
182- fun create (navigator : Navigator ): LessonsPresenter
231+ fun create (circuitContext : CircuitContext , navigator : Navigator ): LessonsPresenter
183232 }
184233}
0 commit comments