Skip to content

Commit e49584e

Browse files
authored
GT-2980 No Personalized Lessons UI (#4430)
* Add dataLoaded flag to delay rendering; Add string resources * Create and add NoPersonalizedLessons UI * Modify exclamation icon for NoPersonalizedLessons composable * Rename onClick param for easier readability and remove unused imports * Swap icon to more closely reflect Figma design * Add closer matching icon for NoPersonalizedLessons composable * Sharpen string resource name for NoPersonalizedLessons.kt file * Add @color/tintable to icon * Update tests to account for new dataLoaded flag * Update screenshot tests to account for NoPersonalizedLessons UI
1 parent bcf39f3 commit e49584e

17 files changed

Lines changed: 124 additions & 18 deletions

File tree

app/src/main/kotlin/org/cru/godtools/ui/dashboard/lessons/LessonsLayout.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,15 @@ internal fun LessonsLayout(state: UiState, modifier: Modifier = Modifier) {
7272
)
7373
}
7474

75+
if (state.mode == UiState.Mode.PERSONALIZATION && state.dataLoaded && state.lessons.isEmpty()) {
76+
item("no-personalized-lessons", "no-personalized-lessons") {
77+
NoPersonalizedLessons(
78+
onGoToAllLessons = { state.eventSink(UiEvent.ChangeMode(UiState.Mode.ALL_LESSONS)) },
79+
modifier = Modifier.padding(top = 16.dp, horizontal = MARGIN_LESSONS_LAYOUT_HORIZONTAL)
80+
)
81+
}
82+
}
83+
7584
item("spacer", "spacer") {
7685
Spacer(modifier = Modifier.height(16.dp))
7786
}

app/src/main/kotlin/org/cru/godtools/ui/dashboard/lessons/LessonsPresenter.kt

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ class LessonsPresenter @AssistedInject internal constructor(
8181
data class UiState internal constructor(
8282
val mode: Mode = Mode.ALL_LESSONS,
8383
val isPersonalizationEnabled: Boolean = false,
84+
val dataLoaded: Boolean = true,
8485
val languageFilter: FilterMenu.UiState<Language> = FilterMenu.UiState(),
8586
val lessons: List<ToolCardPresenter.UiState> = emptyList(),
8687
internal val eventSink: (UiEvent) -> Unit = {},
@@ -108,11 +109,14 @@ class LessonsPresenter @AssistedInject internal constructor(
108109

109110
RegisterSyncTask(languageFilter.selectedItem?.code ?: appLanguage)
110111

112+
val lessons = rememberLessons(mode, languageFilter.selectedItem?.code ?: appLanguage)
113+
111114
return UiState(
112115
mode = mode,
113116
isPersonalizationEnabled = isPersonalizationEnabled,
117+
dataLoaded = lessons != null,
114118
languageFilter = languageFilter,
115-
lessons = rememberLessons(mode, languageFilter.selectedItem?.code ?: appLanguage),
119+
lessons = lessons.orEmpty(),
116120
) {
117121
when (it) {
118122
is UiEvent.ChangeMode -> mode = it.mode
@@ -181,9 +185,9 @@ class LessonsPresenter @AssistedInject internal constructor(
181185
}
182186

183187
@Composable
184-
private fun rememberLessons(mode: UiState.Mode, locale: Locale): List<ToolCardPresenter.UiState> {
185-
val lessons by remember(mode, locale) { lessonsFlowProducer.getFlow(mode, locale) }.collectAsState(emptyList())
186-
return lessons.map { tool ->
188+
private fun rememberLessons(mode: UiState.Mode, locale: Locale): List<ToolCardPresenter.UiState>? {
189+
val lessons by remember(mode, locale) { lessonsFlowProducer.getFlow(mode, locale) }.collectAsState(null)
190+
return lessons?.map { tool ->
187191
key(tool.code) {
188192
lateinit var toolState: ToolCardPresenter.UiState
189193
toolState = toolCardPresenter.present(
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package org.cru.godtools.ui.dashboard.lessons
2+
3+
import androidx.compose.foundation.layout.Column
4+
import androidx.compose.foundation.layout.fillMaxWidth
5+
import androidx.compose.foundation.layout.padding
6+
import androidx.compose.foundation.layout.size
7+
import androidx.compose.material3.Button
8+
import androidx.compose.material3.Icon
9+
import androidx.compose.material3.MaterialTheme
10+
import androidx.compose.material3.Surface
11+
import androidx.compose.material3.Text
12+
import androidx.compose.runtime.Composable
13+
import androidx.compose.ui.Alignment
14+
import androidx.compose.ui.Modifier
15+
import androidx.compose.ui.res.painterResource
16+
import androidx.compose.ui.res.stringResource
17+
import androidx.compose.ui.text.style.TextAlign
18+
import androidx.compose.ui.unit.dp
19+
import org.cru.godtools.R
20+
21+
@Composable
22+
internal fun NoPersonalizedLessons(onGoToAllLessons: () -> Unit, modifier: Modifier = Modifier) {
23+
Surface(
24+
color = MaterialTheme.colorScheme.surfaceVariant,
25+
modifier = modifier.fillMaxWidth(),
26+
) {
27+
Column(
28+
horizontalAlignment = Alignment.CenterHorizontally,
29+
modifier = Modifier.padding(24.dp),
30+
) {
31+
Icon(
32+
painter = painterResource(R.drawable.ic_priority_high),
33+
contentDescription = null,
34+
modifier = Modifier.size(64.dp),
35+
)
36+
Text(
37+
text = stringResource(R.string.dashboard_lessons_section_personalized_no_lessons_title),
38+
style = MaterialTheme.typography.titleMedium,
39+
textAlign = TextAlign.Center,
40+
modifier = Modifier.padding(top = 16.dp),
41+
)
42+
Text(
43+
text = stringResource(R.string.dashboard_lessons_section_personalized_no_lessons_description),
44+
style = MaterialTheme.typography.bodyMedium,
45+
textAlign = TextAlign.Center,
46+
modifier = Modifier.padding(top = 8.dp),
47+
)
48+
Button(
49+
onClick = onGoToAllLessons,
50+
modifier = Modifier.padding(top = 16.dp),
51+
) {
52+
Text(stringResource(R.string.dashboard_lessons_section_personalized_no_lessons_action_all_lessons))
53+
}
54+
}
55+
}
56+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="48dp"
3+
android:height="48dp"
4+
android:viewportWidth="960"
5+
android:viewportHeight="960">
6+
<path
7+
android:pathData="M479.88,796.92q-21.78,0 -37.18,-15.51 -15.39,-15.52 -15.39,-37.3 0,-21.78 15.51,-37.18 15.51,-15.39 37.3,-15.39 21.78,0 37.18,15.51 15.39,15.51 15.39,37.3 0,21.78 -15.51,37.18 -15.51,15.39 -37.3,15.39ZM432.69,600.77v-457.69h94.62v457.69h-94.62Z"
8+
android:fillColor="@color/tintable"/>
9+
</vector>

app/src/main/res/values/strings_dashboard.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ An online version can be found at https://knowgod.com/</string>
5151
<string name="dashboard_lessons_progress_in_progress">%1$d%% Complete</string>
5252
<string name="dashboard_lessons_section_personalized_localization_title">Displaying localized Lessons list</string>
5353
<string name="dashboard_lessons_section_personalized_localization_text">The lessons shown on this page are based on your Localization setting. You can alter this at any time.</string>
54+
<string name="dashboard_lessons_section_personalized_no_lessons_title">Personalization unavailable</string>
55+
<string name="dashboard_lessons_section_personalized_no_lessons_description">The lessons shown on this page are based on your Language and Localization settings. Your current selection does not yet offer personalization.</string>
56+
<string name="dashboard_lessons_section_personalized_no_lessons_action_all_lessons">Go to All Lessons</string>
5457

5558
<!-- Home -->
5659
<eat-comment />

app/src/test/kotlin/org/cru/godtools/ui/dashboard/lessons/LessonsPresenterTest.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ class LessonsPresenterTest {
185185
@Test
186186
fun `State - mode - personalization enabled`() = testScope.runTest {
187187
presenter.test {
188-
val state = awaitItem()
188+
val state = awaitItemMatching { it.dataLoaded }
189189
assertEquals(UiState.Mode.PERSONALIZATION, state.mode)
190190

191191
state.eventSink(UiEvent.ChangeMode(UiState.Mode.ALL_LESSONS))
@@ -437,7 +437,7 @@ class LessonsPresenterTest {
437437
@Test
438438
fun `SideEffect - RegisterSyncTask - Triggers initial sync`() = testScope.runTest {
439439
presenter.test {
440-
awaitItem()
440+
awaitItemMatching { it.dataLoaded }
441441
toolOrderSync.send(true)
442442
coVerifyAll { syncService.syncToolOrder(Locale.ENGLISH, "US", false) }
443443
}
@@ -447,7 +447,7 @@ class LessonsPresenterTest {
447447
fun `SideEffect - RegisterSyncTask - uses locale from language filter`() = testScope.runTest {
448448
appLangFlow.value = Locale.FRENCH
449449
presenter.test {
450-
awaitItem()
450+
awaitItemMatching { it.dataLoaded }
451451
toolOrderSync.send(true)
452452
coVerifyAll { syncService.syncToolOrder(Locale.FRENCH, "US", false) }
453453
}
@@ -471,7 +471,7 @@ class LessonsPresenterTest {
471471
@Test
472472
fun `SideEffect - RegisterSyncTask - passes force on triggered sync`() = testScope.runTest {
473473
presenter.test {
474-
awaitItem()
474+
awaitItemMatching { it.dataLoaded }
475475
toolOrderSync.send(true)
476476
coVerify { syncService.syncToolOrder(Locale.ENGLISH, "US", false) }
477477

Loading
Loading
Loading
Loading

0 commit comments

Comments
 (0)