feat: display and edit bookmark notes on BookmarksScreen

Co-Authored-By: Claude <noreply@anthropic.com>
pull/2125/head
Rohit Karadkar 2 weeks ago
parent 9d6657449c
commit ccd12a78d1

@ -18,6 +18,7 @@ package com.google.samples.apps.nowinandroid.feature.bookmarks.impl
import androidx.annotation.VisibleForTesting
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@ -38,21 +39,28 @@ import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridItemSpan
import androidx.compose.foundation.lazy.staggeredgrid.items
import androidx.compose.foundation.lazy.staggeredgrid.rememberLazyStaggeredGridState
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
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.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
@ -60,6 +68,7 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.compose.LifecycleEventEffect
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.google.samples.apps.nowinandroid.core.analytics.LocalAnalyticsHelper
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaLoadingWheel
import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.DraggableScrollbar
import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.rememberDraggableScroller
@ -67,13 +76,16 @@ import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollba
import com.google.samples.apps.nowinandroid.core.designsystem.theme.LocalTintTheme
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource
import com.google.samples.apps.nowinandroid.core.ui.BookmarkNoteDialog
import com.google.samples.apps.nowinandroid.core.ui.NewsResourceCardExpanded
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.TrackScreenViewEvent
import com.google.samples.apps.nowinandroid.core.ui.TrackScrollJank
import com.google.samples.apps.nowinandroid.core.ui.UserNewsResourcePreviewParameterProvider
import com.google.samples.apps.nowinandroid.core.ui.newsFeed
import com.google.samples.apps.nowinandroid.core.ui.launchCustomChromeTab
import com.google.samples.apps.nowinandroid.core.ui.logNewsResourceOpened
import com.google.samples.apps.nowinandroid.feature.bookmarks.api.R
@Composable
@ -84,6 +96,8 @@ internal fun BookmarksScreen(
viewModel: BookmarksViewModel = hiltViewModel(),
) {
val feedState by viewModel.feedUiState.collectAsStateWithLifecycle()
var editingNoteId by remember { mutableStateOf<String?>(null) }
BookmarksScreen(
feedState = feedState,
onShowSnackbar = onShowSnackbar,
@ -94,7 +108,19 @@ internal fun BookmarksScreen(
shouldDisplayUndoBookmark = viewModel.shouldDisplayUndoBookmark,
undoBookmarkRemoval = viewModel::undoBookmarkRemoval,
clearUndoState = viewModel::clearUndoState,
onEditNote = { editingNoteId = it },
)
editingNoteId?.let { id ->
val currentNote = (feedState as? Success)?.feed?.find { it.id == id }?.bookmarkNote ?: ""
BookmarkNoteDialog(
initialNote = currentNote,
onDismiss = { note ->
viewModel.updateNote(id, note)
editingNoteId = null
},
)
}
}
/**
@ -112,6 +138,7 @@ internal fun BookmarksScreen(
shouldDisplayUndoBookmark: Boolean = false,
undoBookmarkRemoval: () -> Unit = {},
clearUndoState: () -> Unit = {},
onEditNote: (String) -> Unit = {},
) {
val bookmarkRemovedMessage = stringResource(id = R.string.feature_bookmarks_api_removed)
val undoText = stringResource(id = R.string.feature_bookmarks_api_undo)
@ -139,6 +166,7 @@ internal fun BookmarksScreen(
removeFromBookmarks,
onNewsResourceViewed,
onTopicClick,
onEditNote,
modifier,
)
} else {
@ -166,6 +194,7 @@ private fun BookmarksGrid(
removeFromBookmarks: (String) -> Unit,
onNewsResourceViewed: (String) -> Unit,
onTopicClick: (String) -> Unit,
onEditNote: (String) -> Unit,
modifier: Modifier = Modifier,
) {
val scrollableState = rememberLazyStaggeredGridState()
@ -184,12 +213,55 @@ private fun BookmarksGrid(
.fillMaxSize()
.testTag("bookmarks:feed"),
) {
newsFeed(
feedState = feedState,
onNewsResourcesCheckedChanged = { id, _ -> removeFromBookmarks(id) },
onNewsResourceViewed = onNewsResourceViewed,
onTopicClick = onTopicClick,
)
if (feedState is Success) {
items(
items = feedState.feed,
key = { it.id },
contentType = { "newsFeedItem" },
) { userNewsResource ->
val context = LocalContext.current
val analyticsHelper = LocalAnalyticsHelper.current
val backgroundColor =
MaterialTheme.colorScheme.background.toArgb()
Column(
modifier = Modifier
.padding(horizontal = 8.dp)
.animateItem(),
) {
NewsResourceCardExpanded(
userNewsResource = userNewsResource,
isBookmarked = userNewsResource.isSaved,
onClick = {
analyticsHelper.logNewsResourceOpened(
newsResourceId = userNewsResource.id,
)
launchCustomChromeTab(
context,
android.net.Uri.parse(userNewsResource.url),
backgroundColor,
)
onNewsResourceViewed(userNewsResource.id)
},
hasBeenViewed = userNewsResource.hasBeenViewed,
onToggleBookmark = { removeFromBookmarks(userNewsResource.id) },
onTopicClick = onTopicClick,
)
val note = userNewsResource.bookmarkNote
if (note != null) {
Text(
text = note,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier
.fillMaxWidth()
.clickable { onEditNote(userNewsResource.id) },
)
}
}
}
}
item(span = StaggeredGridItemSpan.FullLine) {
Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.safeDrawing))
}
@ -275,6 +347,7 @@ private fun BookmarksGridPreview(
removeFromBookmarks = {},
onNewsResourceViewed = {},
onTopicClick = {},
onEditNote = {},
)
}
}

@ -81,4 +81,14 @@ class BookmarksViewModel @Inject constructor(
shouldDisplayUndoBookmark = false
lastRemovedBookmarkId = null
}
fun updateNote(newsResourceId: String, note: String) {
viewModelScope.launch {
if (note.isNotBlank()) {
userDataRepository.setBookmarkNote(newsResourceId, note)
} else {
userDataRepository.removeBookmarkNote(newsResourceId)
}
}
}
}

Loading…
Cancel
Save