Skip to content

Commit 2ba0cdb

Browse files
Handle TopicUiState.Error without crashing TopicScreen
Replace TODO() with error UI when topic or news loading fails, and add a compose test for the error state. Fixes #2108 Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 7d45eae commit 2ba0cdb

3 files changed

Lines changed: 79 additions & 3 deletions

File tree

  • feature/topic
    • api/src/main/res/values
    • impl/src
      • androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/topic/impl
      • main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/impl

feature/topic/api/src/main/res/values/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,6 @@
1616
-->
1717
<resources>
1818
<string name="feature_topic_api_loading">Loading topic</string>
19+
<string name="feature_topic_api_error">Unable to load topic</string>
20+
<string name="feature_topic_api_news_error">Unable to load news</string>
1921
</resources>

feature/topic/impl/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/topic/impl/TopicScreenTest.kt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,13 @@ class TopicScreenTest {
4242
val composeTestRule = createAndroidComposeRule<ComponentActivity>()
4343

4444
private lateinit var topicLoading: String
45+
private lateinit var topicError: String
4546

4647
@Before
4748
fun setup() {
4849
composeTestRule.activity.apply {
4950
topicLoading = getString(R.string.feature_topic_api_loading)
51+
topicError = getString(R.string.feature_topic_api_error)
5052
}
5153
}
5254

@@ -70,6 +72,26 @@ class TopicScreenTest {
7072
.assertExists()
7173
}
7274

75+
@Test
76+
fun topicError_whenTopicIsError_isShown() {
77+
composeTestRule.setContent {
78+
TopicScreen(
79+
topicUiState = TopicUiState.Error,
80+
newsUiState = NewsUiState.Loading,
81+
showBackButton = true,
82+
onBackClick = {},
83+
onFollowClick = {},
84+
onTopicClick = {},
85+
onBookmarkChanged = { _, _ -> },
86+
onNewsResourceViewed = {},
87+
)
88+
}
89+
90+
composeTestRule
91+
.onNodeWithText(topicError)
92+
.assertExists()
93+
}
94+
7395
@Test
7496
fun topicTitle_whenTopicIsSuccess_isShown() {
7597
val testTopic = followableTopicTestData.first()

feature/topic/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/impl/TopicScreen.kt

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,17 @@ internal fun TopicScreen(
129129
)
130130
}
131131

132-
TopicUiState.Error -> TODO()
132+
TopicUiState.Error -> {
133+
item {
134+
TopicErrorToolbar(
135+
showBackButton = showBackButton,
136+
onBackClick = onBackClick,
137+
)
138+
}
139+
item {
140+
TopicErrorState()
141+
}
142+
}
133143
is TopicUiState.Success -> {
134144
item {
135145
TopicToolbar(
@@ -177,7 +187,7 @@ private fun topicItemsSize(
177187
topicUiState: TopicUiState,
178188
newsUiState: NewsUiState,
179189
) = when (topicUiState) {
180-
TopicUiState.Error -> 0 // Nothing
190+
TopicUiState.Error -> 2 // Toolbar and error message
181191
TopicUiState.Loading -> 1 // Loading bar
182192
is TopicUiState.Success -> when (newsUiState) {
183193
NewsUiState.Error -> 0 // Nothing
@@ -250,7 +260,11 @@ private fun LazyListScope.userNewsResourceCards(
250260
}
251261

252262
else -> item {
253-
Text("Error") // TODO
263+
Text(
264+
text = stringResource(id = TopicR.string.feature_topic_api_news_error),
265+
modifier = Modifier.padding(24.dp),
266+
style = MaterialTheme.typography.bodyLarge,
267+
)
254268
}
255269
}
256270
}
@@ -273,6 +287,44 @@ private fun TopicBodyPreview() {
273287
}
274288
}
275289

290+
@Composable
291+
private fun TopicErrorState(modifier: Modifier = Modifier) {
292+
Text(
293+
text = stringResource(id = TopicR.string.feature_topic_api_error),
294+
modifier = modifier
295+
.fillMaxWidth()
296+
.padding(24.dp)
297+
.testTag("topic:error"),
298+
style = MaterialTheme.typography.bodyLarge,
299+
)
300+
}
301+
302+
@Composable
303+
private fun TopicErrorToolbar(
304+
showBackButton: Boolean,
305+
onBackClick: () -> Unit,
306+
modifier: Modifier = Modifier,
307+
) {
308+
Row(
309+
horizontalArrangement = Arrangement.Start,
310+
verticalAlignment = Alignment.CenterVertically,
311+
modifier = modifier
312+
.fillMaxWidth()
313+
.padding(bottom = 32.dp),
314+
) {
315+
if (showBackButton) {
316+
IconButton(onClick = onBackClick) {
317+
Icon(
318+
imageVector = NiaIcons.ArrowBack,
319+
contentDescription = stringResource(
320+
id = UiR.string.core_ui_back,
321+
),
322+
)
323+
}
324+
}
325+
}
326+
}
327+
276328
@Composable
277329
private fun TopicToolbar(
278330
uiState: FollowableTopic,

0 commit comments

Comments
 (0)