Skip to content

Commit fe8dbd9

Browse files
frettclaude
andcommitted
Add LessonsFlowProducer
Loads lessons based on the current mode: PERSONALIZATION uses getPersonalizedLessonsFlow with the country from Settings, falling back to language-only when no country-specific results exist; ALL_LESSONS uses getLessonsFlowByLanguage. Both modes filter hidden lessons. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent c969eb7 commit fe8dbd9

2 files changed

Lines changed: 153 additions & 0 deletions

File tree

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package org.cru.godtools.ui.dashboard.lessons
2+
3+
import java.util.Locale
4+
import javax.inject.Inject
5+
import kotlinx.coroutines.ExperimentalCoroutinesApi
6+
import kotlinx.coroutines.flow.Flow
7+
import kotlinx.coroutines.flow.combine
8+
import kotlinx.coroutines.flow.distinctUntilChanged
9+
import kotlinx.coroutines.flow.flatMapLatest
10+
import kotlinx.coroutines.flow.map
11+
import org.cru.godtools.base.Settings
12+
import org.cru.godtools.db.repository.ToolsRepository
13+
import org.cru.godtools.model.Tool
14+
import org.cru.godtools.ui.dashboard.lessons.LessonsPresenter.UiState.Mode
15+
16+
internal class LessonsFlowProducer @Inject constructor(
17+
private val settings: Settings,
18+
private val toolsRepository: ToolsRepository,
19+
) {
20+
@OptIn(ExperimentalCoroutinesApi::class)
21+
fun getFlow(mode: Mode, locale: Locale): Flow<List<Tool>> {
22+
val baseFlow = when (mode) {
23+
Mode.PERSONALIZATION -> settings.getPersonalizationCountryFlow()
24+
.flatMapLatest { toolsRepository.getPersonalizedLessonsFlow(locale, it) }
25+
.combine(toolsRepository.getPersonalizedLessonsFlow(locale, null)) { lessons, fallback ->
26+
lessons.ifEmpty { fallback }
27+
}
28+
.distinctUntilChanged()
29+
30+
Mode.ALL_LESSONS -> toolsRepository.getLessonsFlowByLanguage(locale).map { it.sortedBy { it.defaultOrder } }
31+
}
32+
33+
return baseFlow.map { it.filterNot { it.isHidden } }
34+
}
35+
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package org.cru.godtools.ui.dashboard.lessons
2+
3+
import app.cash.turbine.test
4+
import io.mockk.every
5+
import io.mockk.mockk
6+
import io.mockk.verifyAll
7+
import java.util.Locale
8+
import kotlin.test.Test
9+
import kotlin.test.assertEquals
10+
import kotlinx.coroutines.flow.MutableStateFlow
11+
import kotlinx.coroutines.flow.first
12+
import kotlinx.coroutines.flow.flowOf
13+
import kotlinx.coroutines.test.runTest
14+
import org.cru.godtools.base.Settings
15+
import org.cru.godtools.db.repository.ToolsRepository
16+
import org.cru.godtools.model.Tool
17+
import org.cru.godtools.model.randomTool
18+
import org.cru.godtools.ui.dashboard.lessons.LessonsPresenter.UiState.Mode
19+
20+
@Suppress("UnusedFlow")
21+
class LessonsFlowProducerTest {
22+
private val countryFlow = MutableStateFlow<String?>(null)
23+
24+
private val settings: Settings = mockk {
25+
every { getPersonalizationCountryFlow() } returns countryFlow
26+
}
27+
private val toolsRepository: ToolsRepository = mockk {
28+
every { getLessonsFlowByLanguage(any()) } returns flowOf(emptyList())
29+
every { getPersonalizedLessonsFlow(any(), any()) } returns flowOf(emptyList())
30+
}
31+
32+
private val producer = LessonsFlowProducer(settings = settings, toolsRepository = toolsRepository)
33+
34+
// region ALL_LESSONS mode
35+
@Test
36+
fun `getFlow - All Lessons - uses getLessonsFlowByLanguage`() = runTest {
37+
producer.getFlow(mode = Mode.ALL_LESSONS, locale = Locale.ENGLISH).first()
38+
verifyAll {
39+
toolsRepository.getLessonsFlowByLanguage(Locale.ENGLISH)
40+
}
41+
}
42+
43+
@Test
44+
fun `getFlow - All Lessons - excludes hidden lessons`() = runTest {
45+
val hidden = createLesson(isHidden = true)
46+
val visible = createLesson(isHidden = false)
47+
every { toolsRepository.getLessonsFlowByLanguage(any()) } returns flowOf(listOf(hidden, visible))
48+
49+
assertEquals(listOf(visible), producer.getFlow(mode = Mode.ALL_LESSONS, locale = Locale.ENGLISH).first())
50+
}
51+
52+
@Test
53+
fun `getFlow - All Lessons - sorted by defaultOrder`() = runTest {
54+
val lessons = List(5) { createLesson(defaultOrder = it) }
55+
every { toolsRepository.getLessonsFlowByLanguage(any()) } returns flowOf(lessons.shuffled())
56+
57+
assertEquals(lessons, producer.getFlow(mode = Mode.ALL_LESSONS, locale = Locale.ENGLISH).first())
58+
}
59+
// endregion ALL_LESSONS mode
60+
61+
// region PERSONALIZATION mode
62+
@Test
63+
fun `getFlow - Personalization - uses getPersonalizedLessonsFlow`() = runTest {
64+
countryFlow.value = "US"
65+
producer.getFlow(mode = Mode.PERSONALIZATION, locale = Locale.GERMAN).first()
66+
verifyAll {
67+
toolsRepository.getPersonalizedLessonsFlow(Locale.GERMAN, "US")
68+
toolsRepository.getPersonalizedLessonsFlow(Locale.GERMAN, null)
69+
}
70+
}
71+
72+
@Test
73+
fun `getFlow - Personalization - returns personalized lessons when non-empty`() = runTest {
74+
val lesson = createLesson()
75+
val fallback = createLesson()
76+
countryFlow.value = "US"
77+
every { toolsRepository.getPersonalizedLessonsFlow(Locale.ENGLISH, "US") } returns flowOf(listOf(lesson))
78+
every { toolsRepository.getPersonalizedLessonsFlow(Locale.ENGLISH, null) } returns flowOf(listOf(fallback))
79+
80+
assertEquals(listOf(lesson), producer.getFlow(mode = Mode.PERSONALIZATION, locale = Locale.ENGLISH).first())
81+
}
82+
83+
@Test
84+
fun `getFlow - Personalization - falls back to language only when no country-specific lessons`() = runTest {
85+
val fallback = createLesson()
86+
countryFlow.value = "US"
87+
every { toolsRepository.getPersonalizedLessonsFlow(Locale.ENGLISH, "US") } returns flowOf(emptyList())
88+
every { toolsRepository.getPersonalizedLessonsFlow(Locale.ENGLISH, null) } returns flowOf(listOf(fallback))
89+
90+
assertEquals(listOf(fallback), producer.getFlow(mode = Mode.PERSONALIZATION, locale = Locale.ENGLISH).first())
91+
}
92+
93+
@Test
94+
fun `getFlow - Personalization - excludes hidden lessons`() = runTest {
95+
val hidden = createLesson(isHidden = true)
96+
val visible = createLesson(isHidden = false)
97+
every { toolsRepository.getPersonalizedLessonsFlow(any(), any()) } returns flowOf(listOf(hidden, visible))
98+
99+
assertEquals(listOf(visible), producer.getFlow(mode = Mode.PERSONALIZATION, locale = Locale.ENGLISH).first())
100+
}
101+
102+
@Test
103+
fun `getFlow - Personalization - updates when country changes`() = runTest {
104+
val usLesson = createLesson()
105+
every { toolsRepository.getPersonalizedLessonsFlow(Locale.ENGLISH, "US") } returns flowOf(listOf(usLesson))
106+
107+
producer.getFlow(mode = Mode.PERSONALIZATION, locale = Locale.ENGLISH).test {
108+
assertEquals(emptyList(), awaitItem())
109+
110+
countryFlow.value = "US"
111+
assertEquals(listOf(usLesson), awaitItem())
112+
}
113+
}
114+
// endregion PERSONALIZATION mode
115+
116+
private fun createLesson(defaultOrder: Int = 0, isHidden: Boolean = false) =
117+
randomTool(type = Tool.Type.LESSON, defaultOrder = defaultOrder, isHidden = isHidden)
118+
}

0 commit comments

Comments
 (0)