Skip to content

Commit 39e63c7

Browse files
committed
create a LessonsPresenter
1 parent aab98bb commit 39e63c7

3 files changed

Lines changed: 449 additions & 0 deletions

File tree

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
package org.cru.godtools.ui.dashboard.lessons
2+
3+
import android.content.Context
4+
import androidx.compose.runtime.Composable
5+
import androidx.compose.runtime.collectAsState
6+
import androidx.compose.runtime.getValue
7+
import androidx.compose.runtime.key
8+
import androidx.compose.runtime.mutableStateOf
9+
import androidx.compose.runtime.remember
10+
import androidx.compose.runtime.saveable.rememberSaveable
11+
import androidx.compose.runtime.snapshotFlow
12+
import com.slack.circuit.codegen.annotations.CircuitInject
13+
import com.slack.circuit.runtime.Navigator
14+
import com.slack.circuit.runtime.presenter.Presenter
15+
import com.slack.circuitx.android.IntentScreen
16+
import dagger.assisted.Assisted
17+
import dagger.assisted.AssistedFactory
18+
import dagger.assisted.AssistedInject
19+
import dagger.hilt.android.qualifiers.ApplicationContext
20+
import dagger.hilt.components.SingletonComponent
21+
import java.util.Locale
22+
import kotlinx.collections.immutable.ImmutableList
23+
import kotlinx.collections.immutable.persistentListOf
24+
import kotlinx.collections.immutable.toImmutableList
25+
import kotlinx.coroutines.ExperimentalCoroutinesApi
26+
import kotlinx.coroutines.flow.combine
27+
import kotlinx.coroutines.flow.distinctUntilChanged
28+
import kotlinx.coroutines.flow.flatMapLatest
29+
import kotlinx.coroutines.flow.map
30+
import org.cru.godtools.analytics.model.OpenAnalyticsActionEvent
31+
import org.cru.godtools.analytics.model.OpenAnalyticsActionEvent.Companion.ACTION_OPEN_LESSON
32+
import org.cru.godtools.analytics.model.OpenAnalyticsActionEvent.Companion.SOURCE_LESSONS
33+
import org.cru.godtools.base.Settings
34+
import org.cru.godtools.db.repository.LanguagesRepository
35+
import org.cru.godtools.db.repository.ToolsRepository
36+
import org.cru.godtools.db.repository.TranslationsRepository
37+
import org.cru.godtools.model.Language
38+
import org.cru.godtools.model.Language.Companion.filterByDisplayAndNativeName
39+
import org.cru.godtools.ui.dashboard.filters.FilterMenu
40+
import org.cru.godtools.ui.tools.ToolCard
41+
import org.cru.godtools.ui.tools.ToolCardPresenter
42+
import org.cru.godtools.util.createToolIntent
43+
import org.greenrobot.eventbus.EventBus
44+
45+
class LessonsPresenter @AssistedInject constructor(
46+
@ApplicationContext
47+
private val context: Context,
48+
private val eventBus: EventBus,
49+
private val languagesRepository: LanguagesRepository,
50+
private val settings: Settings,
51+
private val toolCardPresenter: ToolCardPresenter,
52+
private val toolsRepository: ToolsRepository,
53+
private val translationsRepository: TranslationsRepository,
54+
@Assisted
55+
private val navigator: Navigator,
56+
) : Presenter<LessonsScreen.UiState> {
57+
@Composable
58+
override fun present(): LessonsScreen.UiState {
59+
val appLanguage by settings.appLanguageFlow.collectAsState()
60+
val languageFilter = rememberLanguagesFilter()
61+
62+
return LessonsScreen.UiState(
63+
languageFilter = languageFilter,
64+
lessons = rememberLessons(languageFilter.selectedItem?.code ?: appLanguage),
65+
)
66+
}
67+
68+
@Composable
69+
@OptIn(ExperimentalCoroutinesApi::class)
70+
private fun rememberLanguagesFilter(): FilterMenu.UiState<Language> {
71+
val appLanguage by settings.appLanguageFlow.collectAsState()
72+
val locale = rememberSaveable(appLanguage) { mutableStateOf(appLanguage) }
73+
val query = rememberSaveable { mutableStateOf("") }
74+
75+
return FilterMenu.UiState(
76+
menuExpanded = rememberSaveable { mutableStateOf(false) },
77+
query = query,
78+
selectedItem = remember {
79+
snapshotFlow { locale.value }
80+
.flatMapLatest { locale ->
81+
languagesRepository.findLanguageFlow(locale).map { it ?: Language(locale) }
82+
}
83+
}.collectAsState(Language(locale.value)).value,
84+
items = remember {
85+
combine(
86+
languagesRepository.getLanguagesFlow(),
87+
snapshotFlow { query.value },
88+
settings.appLanguageFlow,
89+
toolsRepository.getLessonsFlow()
90+
.map { it.mapNotNullTo(mutableSetOf()) { it.code } }
91+
.distinctUntilChanged()
92+
.flatMapLatest { translationsRepository.getTranslationsFlowForTools(it) }
93+
.map {
94+
it.groupBy { it.languageCode }
95+
.mapValues { it.value.distinctBy { it.toolCode }.count() }
96+
},
97+
) { languages, query, appLanguage, toolCounts ->
98+
languages
99+
.filterByDisplayAndNativeName(query, context, appLanguage)
100+
.map { FilterMenu.UiState.Item(it, toolCounts[it.code] ?: 0) }
101+
.filter { it.count > 0 }
102+
.toImmutableList()
103+
}
104+
}.collectAsState(persistentListOf()).value,
105+
eventSink = {
106+
when (it) {
107+
is FilterMenu.Event.SelectItem -> locale.value = it.item.code
108+
}
109+
}
110+
)
111+
}
112+
113+
@Composable
114+
private fun rememberLessons(locale: Locale): ImmutableList<ToolCard.State> {
115+
val lessons by remember(locale) {
116+
toolsRepository.getLessonsFlowByLanguage(locale)
117+
.map { it.filterNot { it.isHidden }.sortedBy { it.defaultOrder } }
118+
}.collectAsState(emptyList())
119+
120+
return lessons.mapTo(persistentListOf<ToolCard.State>().builder()) { tool ->
121+
key(tool.code) {
122+
lateinit var toolState: ToolCard.State
123+
toolState = toolCardPresenter.present(
124+
tool = tool,
125+
customLocale = locale,
126+
eventSink = {
127+
when (it) {
128+
ToolCard.Event.Click -> {
129+
eventBus.post(OpenAnalyticsActionEvent(ACTION_OPEN_LESSON, tool.code, SOURCE_LESSONS))
130+
navigator.goTo(
131+
IntentScreen(
132+
tool.createToolIntent(
133+
context = context,
134+
languages = listOfNotNull(toolState.translation?.languageCode),
135+
saveLanguageSettings = true
136+
) ?: return@present
137+
)
138+
)
139+
}
140+
ToolCard.Event.OpenTool,
141+
ToolCard.Event.OpenToolDetails,
142+
ToolCard.Event.PinTool,
143+
ToolCard.Event.UnpinTool -> Unit
144+
}
145+
}
146+
)
147+
toolState
148+
}
149+
}.build()
150+
}
151+
152+
@AssistedFactory
153+
@CircuitInject(LessonsScreen::class, SingletonComponent::class)
154+
interface Factory {
155+
fun create(navigator: Navigator): LessonsPresenter
156+
}
157+
}

0 commit comments

Comments
 (0)