From c19b8b93191c0a627d4c9e030d2b5cc2f62ab8be Mon Sep 17 00:00:00 2001 From: Mohsen Rzna Date: Sun, 20 Nov 2022 09:37:57 +0100 Subject: [PATCH] New empty bookmarks handling (#443) * Added the png file for error state This should be removed later - couldn't convert the design to vector from svg - there is a problem with the ` --- .../designsystem/component/LoadingWheel.kt | 4 +- .../feature/bookmarks/BookmarksScreen.kt | 155 ++++++++++++++---- .../main/res/drawable/img_empty_bookmarks.xml | 31 ++++ .../bookmarks/src/main/res/values/strings.xml | 3 +- 4 files changed, 155 insertions(+), 38 deletions(-) create mode 100644 feature/bookmarks/src/main/res/drawable/img_empty_bookmarks.xml diff --git a/core/designsystem/src/main/java/com/google/samples/apps/nowinandroid/core/designsystem/component/LoadingWheel.kt b/core/designsystem/src/main/java/com/google/samples/apps/nowinandroid/core/designsystem/component/LoadingWheel.kt index ba3f7c2b0..c32637b69 100644 --- a/core/designsystem/src/main/java/com/google/samples/apps/nowinandroid/core/designsystem/component/LoadingWheel.kt +++ b/core/designsystem/src/main/java/com/google/samples/apps/nowinandroid/core/designsystem/component/LoadingWheel.kt @@ -42,6 +42,7 @@ import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.graphics.drawscope.rotate import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.dp @@ -56,7 +57,8 @@ fun NiaLoadingWheel( val infiniteTransition = rememberInfiniteTransition() // Specifies the float animation for slowly drawing out the lines on entering - val floatAnimValues = (0 until NUM_OF_LINES).map { remember { Animatable(1F) } } + val startValue = if (LocalInspectionMode.current) 0F else 1F + val floatAnimValues = (0 until NUM_OF_LINES).map { remember { Animatable(startValue) } } LaunchedEffect(floatAnimValues) { (0 until NUM_OF_LINES).map { index -> launch { diff --git a/feature/bookmarks/src/main/java/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreen.kt b/feature/bookmarks/src/main/java/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreen.kt index 560378370..06a87bc9e 100644 --- a/feature/bookmarks/src/main/java/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreen.kt +++ b/feature/bookmarks/src/main/java/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreen.kt @@ -16,14 +16,17 @@ package com.google.samples.apps.nowinandroid.feature.bookmarks +import androidx.annotation.VisibleForTesting +import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.safeDrawing import androidx.compose.foundation.layout.windowInsetsBottomHeight import androidx.compose.foundation.layout.wrapContentSize @@ -31,19 +34,29 @@ import androidx.compose.foundation.lazy.grid.GridCells.Adaptive import androidx.compose.foundation.lazy.grid.GridItemSpan import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.rememberLazyGridState +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.ExperimentalLifecycleComposeApi import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaLoadingWheel +import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme +import com.google.samples.apps.nowinandroid.core.domain.model.SaveableNewsResource +import com.google.samples.apps.nowinandroid.core.model.data.previewNewsResources import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState +import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState.Loading +import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState.Success import com.google.samples.apps.nowinandroid.core.ui.TrackScrollJank import com.google.samples.apps.nowinandroid.core.ui.newsFeed @@ -61,8 +74,39 @@ internal fun BookmarksRoute( ) } +/** + * Displays the user's bookmarked articles. Includes support for loading and empty states. + */ +@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) +@Composable +internal fun BookmarksScreen( + feedState: NewsFeedUiState, + removeFromBookmarks: (String) -> Unit, + modifier: Modifier = Modifier +) { + when (feedState) { + Loading -> LoadingState(modifier) + is Success -> if (feedState.feed.isNotEmpty()) { + BookmarksGrid(feedState, removeFromBookmarks, modifier) + } else { + EmptyState(modifier) + } + } +} + @Composable -fun BookmarksScreen( +private fun LoadingState(modifier: Modifier = Modifier) { + NiaLoadingWheel( + modifier = modifier + .fillMaxWidth() + .wrapContentSize() + .testTag("forYou:loading"), + contentDesc = stringResource(id = R.string.saved_loading), + ) +} + +@Composable +private fun BookmarksGrid( feedState: NewsFeedUiState, removeFromBookmarks: (String) -> Unit, modifier: Modifier = Modifier @@ -79,41 +123,80 @@ fun BookmarksScreen( .fillMaxSize() .testTag("bookmarks:feed") ) { + newsFeed( + feedState = feedState, + onNewsResourcesCheckedChanged = { id, _ -> removeFromBookmarks(id) }, + ) + item(span = { GridItemSpan(maxLineSpan) }) { + Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.safeDrawing)) + } + } +} - when (feedState) { - is NewsFeedUiState.Loading -> { - item(span = { GridItemSpan(maxLineSpan) }) { - NiaLoadingWheel( - modifier = Modifier - .fillMaxWidth() - .wrapContentSize() - .testTag("forYou:loading"), - contentDesc = stringResource(id = R.string.saved_loading), - ) - } - } +@Composable +private fun EmptyState(modifier: Modifier = Modifier) { + Column( + modifier = modifier + .padding(16.dp) + .fillMaxSize() + .testTag("bookmarks:empty"), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Image( + modifier = Modifier.fillMaxWidth(), + painter = painterResource(id = R.drawable.img_empty_bookmarks), + contentDescription = null + ) + + Spacer(modifier = Modifier.height(16.dp)) + + Text( + text = stringResource(id = R.string.bookmarks_empty_error), + modifier = Modifier.fillMaxWidth(), + textAlign = TextAlign.Center, + style = MaterialTheme.typography.titleMedium, + fontWeight = FontWeight.Bold + ) - is NewsFeedUiState.Success -> { - if (feedState.feed.isNotEmpty()) { - newsFeed( - feedState = feedState, - onNewsResourcesCheckedChanged = { id, _ -> removeFromBookmarks(id) }, - ) - item(span = { GridItemSpan(maxLineSpan) }) { - Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.safeDrawing)) - } - } else item(span = { GridItemSpan(maxLineSpan) }) { - Box( - modifier = Modifier - .fillMaxHeight() - .wrapContentSize() - .testTag("bookmarks:empty"), - contentAlignment = Alignment.Center - ) { - Text(text = stringResource(id = R.string.bookmarks_empty)) - } + Spacer(modifier = Modifier.height(8.dp)) + + Text( + text = stringResource(id = R.string.bookmarks_empty_description), + modifier = Modifier.fillMaxWidth(), + textAlign = TextAlign.Center, + style = MaterialTheme.typography.bodyMedium + ) + } +} + +@Preview +@Composable +private fun LoadingStatePreview() { + NiaTheme { + LoadingState() + } +} + +@Preview +@Composable +private fun BookmarksGridPreview() { + NiaTheme { + BookmarksGrid( + feedState = Success( + previewNewsResources.map { + SaveableNewsResource(it, false) } - } - } + ), + removeFromBookmarks = {} + ) + } +} + +@Preview +@Composable +fun EmptyStatePreview() { + NiaTheme { + EmptyState() } } diff --git a/feature/bookmarks/src/main/res/drawable/img_empty_bookmarks.xml b/feature/bookmarks/src/main/res/drawable/img_empty_bookmarks.xml new file mode 100644 index 000000000..b9e2f2963 --- /dev/null +++ b/feature/bookmarks/src/main/res/drawable/img_empty_bookmarks.xml @@ -0,0 +1,31 @@ + + + + + + diff --git a/feature/bookmarks/src/main/res/values/strings.xml b/feature/bookmarks/src/main/res/values/strings.xml index 965b78333..61781ad6e 100644 --- a/feature/bookmarks/src/main/res/values/strings.xml +++ b/feature/bookmarks/src/main/res/values/strings.xml @@ -20,5 +20,6 @@ Saved Search Menu - No bookmarks yet! + No saved updates + Updates you save will be stored here\nto read later \ No newline at end of file