Skip to content

Commit adc9381

Browse files
Merge pull request #640 from android/fr-undo-bookmark-removal
Add Undo snackbar on Bookmark removal
2 parents 6d87dc6 + bf74743 commit adc9381

File tree

4 files changed

+87
-10
lines changed

4 files changed

+87
-10
lines changed

feature/bookmarks/build.gradle.kts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@
1414
* limitations under the License.
1515
*/
1616

17-
import com.android.build.api.dsl.ManagedVirtualDevice
18-
1917
plugins {
2018
id("nowinandroid.android.feature")
2119
id("nowinandroid.android.library.compose")
@@ -28,4 +26,4 @@ android {
2826

2927
dependencies {
3028
implementation(libs.androidx.compose.material3.windowSizeClass)
31-
}
29+
}

feature/bookmarks/src/main/java/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreen.kt

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package com.google.samples.apps.nowinandroid.feature.bookmarks
1919
import androidx.annotation.VisibleForTesting
2020
import androidx.compose.foundation.Image
2121
import androidx.compose.foundation.layout.Arrangement
22+
import androidx.compose.foundation.layout.Box
2223
import androidx.compose.foundation.layout.Column
2324
import androidx.compose.foundation.layout.PaddingValues
2425
import androidx.compose.foundation.layout.Spacer
@@ -34,13 +35,23 @@ import androidx.compose.foundation.lazy.grid.GridCells.Adaptive
3435
import androidx.compose.foundation.lazy.grid.GridItemSpan
3536
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
3637
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
38+
import androidx.compose.material3.ExperimentalMaterial3Api
3739
import androidx.compose.material3.MaterialTheme
40+
import androidx.compose.material3.Scaffold
41+
import androidx.compose.material3.SnackbarDuration.Short
42+
import androidx.compose.material3.SnackbarHost
43+
import androidx.compose.material3.SnackbarHostState
44+
import androidx.compose.material3.SnackbarResult.ActionPerformed
3845
import androidx.compose.material3.Text
3946
import androidx.compose.runtime.Composable
47+
import androidx.compose.runtime.DisposableEffect
48+
import androidx.compose.runtime.LaunchedEffect
4049
import androidx.compose.runtime.getValue
50+
import androidx.compose.runtime.remember
4151
import androidx.compose.ui.Alignment
4252
import androidx.compose.ui.Modifier
4353
import androidx.compose.ui.graphics.ColorFilter
54+
import androidx.compose.ui.platform.LocalLifecycleOwner
4455
import androidx.compose.ui.platform.testTag
4556
import androidx.compose.ui.res.painterResource
4657
import androidx.compose.ui.res.stringResource
@@ -50,6 +61,8 @@ import androidx.compose.ui.tooling.preview.Preview
5061
import androidx.compose.ui.tooling.preview.PreviewParameter
5162
import androidx.compose.ui.unit.dp
5263
import androidx.hilt.navigation.compose.hiltViewModel
64+
import androidx.lifecycle.Lifecycle
65+
import androidx.lifecycle.LifecycleEventObserver
5366
import androidx.lifecycle.compose.collectAsStateWithLifecycle
5467
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaLoadingWheel
5568
import com.google.samples.apps.nowinandroid.core.designsystem.theme.LocalTintTheme
@@ -76,12 +89,16 @@ internal fun BookmarksRoute(
7689
onNewsResourceViewed = { viewModel.setNewsResourceViewed(it, true) },
7790
onTopicClick = onTopicClick,
7891
modifier = modifier,
92+
shouldDisplayUndoBookmark = viewModel.shouldDisplayUndoBookmark,
93+
undoBookmarkRemoval = viewModel::undoBookmarkRemoval,
94+
clearUndoState = viewModel::clearUndoState,
7995
)
8096
}
8197

8298
/**
8399
* Displays the user's bookmarked articles. Includes support for loading and empty states.
84100
*/
101+
@OptIn(ExperimentalMaterial3Api::class)
85102
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
86103
@Composable
87104
internal fun BookmarksScreen(
@@ -90,13 +107,51 @@ internal fun BookmarksScreen(
90107
onNewsResourceViewed: (String) -> Unit,
91108
onTopicClick: (String) -> Unit,
92109
modifier: Modifier = Modifier,
110+
shouldDisplayUndoBookmark: Boolean = false,
111+
undoBookmarkRemoval: () -> Unit = {},
112+
clearUndoState: () -> Unit = {},
93113
) {
94-
when (feedState) {
95-
Loading -> LoadingState(modifier)
96-
is Success -> if (feedState.feed.isNotEmpty()) {
97-
BookmarksGrid(feedState, removeFromBookmarks, onNewsResourceViewed, onTopicClick, modifier)
98-
} else {
99-
EmptyState(modifier)
114+
val bookmarkRemovedMessage = stringResource(id = R.string.bookmark_removed)
115+
val undoText = stringResource(id = R.string.undo)
116+
val snackbarHostState = remember { SnackbarHostState() }
117+
118+
LaunchedEffect(shouldDisplayUndoBookmark) {
119+
if (shouldDisplayUndoBookmark) {
120+
val snackBarResult = snackbarHostState.showSnackbar(
121+
message = bookmarkRemovedMessage,
122+
actionLabel = undoText,
123+
duration = Short,
124+
)
125+
when (snackBarResult) {
126+
ActionPerformed -> { undoBookmarkRemoval() }
127+
else -> { clearUndoState() }
128+
}
129+
}
130+
}
131+
132+
val lifecycleOwner = LocalLifecycleOwner.current
133+
DisposableEffect(lifecycleOwner) {
134+
val observer = LifecycleEventObserver { _, event ->
135+
if (event == Lifecycle.Event.ON_STOP) {
136+
clearUndoState()
137+
}
138+
}
139+
lifecycleOwner.lifecycle.addObserver(observer)
140+
onDispose { lifecycleOwner.lifecycle.removeObserver(observer) }
141+
}
142+
143+
Scaffold(snackbarHost = { SnackbarHost(hostState = snackbarHostState) }) {
144+
Box(
145+
modifier = Modifier.padding(it).fillMaxSize(),
146+
) {
147+
when (feedState) {
148+
Loading -> LoadingState(modifier)
149+
is Success -> if (feedState.feed.isNotEmpty()) {
150+
BookmarksGrid(feedState, removeFromBookmarks, onNewsResourceViewed, onTopicClick, modifier)
151+
} else {
152+
EmptyState(modifier)
153+
}
154+
}
100155
}
101156
}
102157
TrackScreenViewEvent(screenName = "Saved")

feature/bookmarks/src/main/java/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksViewModel.kt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616

1717
package com.google.samples.apps.nowinandroid.feature.bookmarks
1818

19+
import androidx.compose.runtime.getValue
20+
import androidx.compose.runtime.mutableStateOf
21+
import androidx.compose.runtime.setValue
1922
import androidx.lifecycle.ViewModel
2023
import androidx.lifecycle.viewModelScope
2124
import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository
@@ -38,6 +41,9 @@ class BookmarksViewModel @Inject constructor(
3841
userNewsResourceRepository: UserNewsResourceRepository,
3942
) : ViewModel() {
4043

44+
var shouldDisplayUndoBookmark by mutableStateOf(false)
45+
private var lastRemovedBookmarkId: String? = null
46+
4147
val feedUiState: StateFlow<NewsFeedUiState> =
4248
userNewsResourceRepository.observeAllBookmarked()
4349
.map<List<UserNewsResource>, NewsFeedUiState>(NewsFeedUiState::Success)
@@ -50,6 +56,8 @@ class BookmarksViewModel @Inject constructor(
5056

5157
fun removeFromSavedResources(newsResourceId: String) {
5258
viewModelScope.launch {
59+
shouldDisplayUndoBookmark = true
60+
lastRemovedBookmarkId = newsResourceId
5361
userDataRepository.updateNewsResourceBookmark(newsResourceId, false)
5462
}
5563
}
@@ -59,4 +67,18 @@ class BookmarksViewModel @Inject constructor(
5967
userDataRepository.setNewsResourceViewed(newsResourceId, viewed)
6068
}
6169
}
70+
71+
fun undoBookmarkRemoval() {
72+
viewModelScope.launch {
73+
lastRemovedBookmarkId?.let {
74+
userDataRepository.updateNewsResourceBookmark(it, true)
75+
}
76+
}
77+
clearUndoState()
78+
}
79+
80+
fun clearUndoState() {
81+
shouldDisplayUndoBookmark = false
82+
lastRemovedBookmarkId = null
83+
}
6284
}

feature/bookmarks/src/main/res/values/strings.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,6 @@
2222
<string name="top_app_bar_action_menu">Menu</string>
2323
<string name="bookmarks_empty_error">No saved updates</string>
2424
<string name="bookmarks_empty_description">Updates you save will be stored here\nto read later</string>
25-
</resources>
25+
<string name="bookmark_removed">Bookmark removed</string>
26+
<string name="undo">UNDO</string>
27+
</resources>

0 commit comments

Comments
 (0)