diff --git a/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/bookmark/BookmarkDetailFragment.kt b/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/bookmark/BookmarkDetailFragment.kt new file mode 100644 index 00000000000..f7e1a5ddddc --- /dev/null +++ b/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/bookmark/BookmarkDetailFragment.kt @@ -0,0 +1,140 @@ +package au.com.shiftyjelly.pocketcasts.player.view.bookmark + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.Toast +import androidx.fragment.app.FragmentManager +import androidx.lifecycle.lifecycleScope +import au.com.shiftyjelly.pocketcasts.analytics.SourceView +import au.com.shiftyjelly.pocketcasts.compose.extensions.contentWithoutConsumedInsets +import au.com.shiftyjelly.pocketcasts.models.entity.Bookmark +import au.com.shiftyjelly.pocketcasts.repositories.playback.PlaybackManager +import au.com.shiftyjelly.pocketcasts.repositories.podcast.EpisodeManager +import au.com.shiftyjelly.pocketcasts.utils.extensions.toLocalizedFormatPattern +import au.com.shiftyjelly.pocketcasts.views.fragments.BaseDialogFragment +import com.automattic.eventhorizon.BookmarkPlayTappedEvent +import com.automattic.eventhorizon.EventHorizon +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject +import kotlinx.coroutines.launch +import au.com.shiftyjelly.pocketcasts.localization.R as LR + +@AndroidEntryPoint +class BookmarkDetailFragment : BaseDialogFragment() { + + companion object { + private const val TAG = "bookmark_detail" + private const val ARG_DISPLAY_TITLE = "display_title" + private const val ARG_AI_SUMMARY = "ai_summary" + private const val ARG_EPISODE_TITLE = "episode_title" + private const val ARG_EPISODE_UUID = "episode_uuid" + private const val ARG_PODCAST_UUID = "podcast_uuid" + private const val ARG_PODCAST_TITLE = "podcast_title" + private const val ARG_TIME_SECS = "time_secs" + private const val ARG_CREATED_AT_TEXT = "created_at_text" + private const val ARG_SOURCE_VIEW = "source_view" + + fun show( + fragmentManager: FragmentManager, + bookmark: Bookmark, + episodeTitle: String, + podcastUuid: String, + podcastTitle: String, + sourceView: SourceView, + ) { + if (!fragmentManager.isStateSaved && fragmentManager.findFragmentByTag(TAG) == null) { + newInstance(bookmark, episodeTitle, podcastUuid, podcastTitle, sourceView) + .show(fragmentManager, TAG) + } + } + + private fun newInstance( + bookmark: Bookmark, + episodeTitle: String, + podcastUuid: String, + podcastTitle: String, + sourceView: SourceView, + ) = BookmarkDetailFragment().apply { + arguments = Bundle().apply { + putString(ARG_DISPLAY_TITLE, bookmark.displayTitle) + putString(ARG_AI_SUMMARY, bookmark.aiSummary) + putString(ARG_EPISODE_TITLE, episodeTitle) + putString(ARG_EPISODE_UUID, bookmark.episodeUuid) + putString(ARG_PODCAST_UUID, podcastUuid) + putString(ARG_PODCAST_TITLE, podcastTitle) + putInt(ARG_TIME_SECS, bookmark.timeSecs) + putString( + ARG_CREATED_AT_TEXT, + bookmark.createdAt.toLocalizedFormatPattern(bookmark.createdAtDatePattern()), + ) + putString(ARG_SOURCE_VIEW, sourceView.key) + } + } + } + + @Inject + internal lateinit var playbackManager: PlaybackManager + + @Inject + internal lateinit var episodeManager: EpisodeManager + + @Inject + internal lateinit var eventHorizon: EventHorizon + + private val displayTitle: String get() = requireArguments().getString(ARG_DISPLAY_TITLE, "") + private val aiSummary: String? get() = requireArguments().getString(ARG_AI_SUMMARY) + private val episodeTitle: String get() = requireArguments().getString(ARG_EPISODE_TITLE, "") + private val episodeUuid: String get() = requireArguments().getString(ARG_EPISODE_UUID, "") + private val podcastUuid: String get() = requireArguments().getString(ARG_PODCAST_UUID, "") + private val podcastTitle: String get() = requireArguments().getString(ARG_PODCAST_TITLE, "") + private val timeSecs: Int get() = requireArguments().getInt(ARG_TIME_SECS) + private val createdAtText: String get() = requireArguments().getString(ARG_CREATED_AT_TEXT, "") + private val sourceView: SourceView + get() = SourceView.fromString(requireArguments().getString(ARG_SOURCE_VIEW)) + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ) = contentWithoutConsumedInsets { + DialogBox(fillMaxHeight = false) { + BookmarkDetailPage( + displayTitle = displayTitle, + aiSummary = aiSummary, + episodeTitle = episodeTitle, + podcastUuid = podcastUuid, + podcastTitle = podcastTitle, + timeSecs = timeSecs, + createdAtText = createdAtText, + onPlayClick = ::onPlayClick, + onClose = { dismiss() }, + ) + } + } + + private fun onPlayClick() { + lifecycleScope.launch { + val episode = episodeManager.findEpisodeByUuid(episodeUuid) + if (episode == null) { + Toast.makeText( + requireContext(), + getString(LR.string.episode_not_found), + Toast.LENGTH_SHORT, + ).show() + dismiss() + return@launch + } + playbackManager.playNowSuspend(episode, sourceView = sourceView) + playbackManager.seekToTimeMs(positionMs = timeSecs * 1000) + eventHorizon.track( + BookmarkPlayTappedEvent( + source = sourceView.analyticsValue, + episodeUuid = episodeUuid, + podcastUuid = podcastUuid, + ), + ) + dismiss() + } + } +} diff --git a/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/bookmark/BookmarkDetailPage.kt b/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/bookmark/BookmarkDetailPage.kt new file mode 100644 index 00000000000..0f75cabcb3c --- /dev/null +++ b/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/bookmark/BookmarkDetailPage.kt @@ -0,0 +1,226 @@ +package au.com.shiftyjelly.pocketcasts.player.view.bookmark + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.material.MaterialTheme +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Close +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.dp +import au.com.shiftyjelly.pocketcasts.compose.AppThemeWithBackground +import au.com.shiftyjelly.pocketcasts.compose.bookmark.BookmarkRowColors +import au.com.shiftyjelly.pocketcasts.compose.buttons.RowButton +import au.com.shiftyjelly.pocketcasts.compose.components.PodcastImage +import au.com.shiftyjelly.pocketcasts.compose.components.TextH30 +import au.com.shiftyjelly.pocketcasts.compose.components.TextH70 +import au.com.shiftyjelly.pocketcasts.compose.components.TextP40 +import au.com.shiftyjelly.pocketcasts.compose.preview.ThemePreviewParameterProvider +import au.com.shiftyjelly.pocketcasts.compose.theme +import au.com.shiftyjelly.pocketcasts.localization.helper.TimeHelper +import au.com.shiftyjelly.pocketcasts.ui.theme.Theme +import au.com.shiftyjelly.pocketcasts.images.R as IR +import au.com.shiftyjelly.pocketcasts.localization.R as LR + +@Composable +internal fun BookmarkDetailPage( + displayTitle: String, + aiSummary: String?, + episodeTitle: String, + podcastUuid: String, + podcastTitle: String, + timeSecs: Int, + createdAtText: String, + onPlayClick: () -> Unit, + onClose: () -> Unit, + modifier: Modifier = Modifier, +) { + val theme = MaterialTheme.theme + val playerColors = theme.rememberPlayerColors() + val colors = remember(theme.type, playerColors) { + if (playerColors != null) { + BookmarkRowColors.player(playerColors) + } else { + BookmarkRowColors.default(theme.colors) + } + } + val playButtonBackground = if (playerColors != null) { + playerColors.contrast01 + } else { + theme.colors.primaryInteractive01 + } + val playButtonText = if (playerColors != null) { + playerColors.background01 + } else { + theme.colors.primaryInteractive02 + } + + Column( + modifier = modifier + .fillMaxWidth() + .verticalScroll(rememberScrollState()) + .padding(bottom = 24.dp), + ) { + DragHandle( + modifier = Modifier + .align(Alignment.CenterHorizontally) + .padding(top = 12.dp), + ) + + Header( + buttonColor = colors.primaryText, + onClose = onClose, + ) + + Column( + modifier = Modifier.padding(horizontal = 20.dp), + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + ) { + PodcastImage( + uuid = podcastUuid, + imageSize = 48.dp, + elevation = null, + ) + + Spacer(modifier = Modifier.width(12.dp)) + + Column { + if (podcastTitle.isNotEmpty()) { + TextH70( + text = podcastTitle.uppercase(), + color = colors.secondaryText, + ) + Spacer(modifier = Modifier.height(4.dp)) + } + + if (episodeTitle.isNotEmpty()) { + TextH70( + text = episodeTitle, + color = colors.primaryText, + ) + } + } + } + + Spacer(modifier = Modifier.height(8.dp)) + + TextH30( + text = displayTitle, + color = colors.primaryText, + ) + + Spacer(modifier = Modifier.height(4.dp)) + + val formattedTime = TimeHelper.formattedSeconds(timeSecs.toDouble()) + TextH70( + text = formattedTime, + color = colors.secondaryText, + ) + + if (!aiSummary.isNullOrEmpty()) { + Spacer(modifier = Modifier.height(16.dp)) + TextP40( + text = aiSummary, + color = colors.secondaryText, + ) + } + + Spacer(modifier = Modifier.height(12.dp)) + + TextH70( + text = createdAtText, + color = colors.secondaryText, + ) + + Spacer(modifier = Modifier.height(20.dp)) + RowButton( + text = stringResource(LR.string.bookmark_play_from, formattedTime), + onClick = onPlayClick, + includePadding = false, + textIcon = IR.drawable.ic_play, + colors = ButtonDefaults.buttonColors( + backgroundColor = playButtonBackground, + ), + textColor = playButtonText, + ) + } + } +} + +@Composable +private fun DragHandle(modifier: Modifier = Modifier) { + Box( + modifier = modifier + .width(36.dp) + .height(4.dp) + .clip(RoundedCornerShape(2.dp)) + .background(MaterialTheme.theme.colors.primaryText01.copy(alpha = 0.3f)), + ) +} + +@Composable +private fun Header( + buttonColor: Color, + onClose: () -> Unit, + modifier: Modifier = Modifier, +) { + Row( + modifier = modifier + .fillMaxWidth() + .padding(start = 8.dp, end = 20.dp, top = 4.dp, bottom = 8.dp), + ) { + IconButton( + onClick = onClose, + modifier = Modifier.offset(x = (-4).dp), + ) { + Icon( + imageVector = Icons.Default.Close, + contentDescription = stringResource(LR.string.close), + tint = buttonColor, + ) + } + } +} + +@Preview +@Composable +private fun BookmarkDetailPagePreview( + @PreviewParameter(ThemePreviewParameterProvider::class) themeType: Theme.ThemeType, +) { + AppThemeWithBackground(themeType) { + BookmarkDetailPage( + displayTitle = "Latency vs throughput tradeoff", + aiSummary = "Why optimizing for low latency often means sacrificing batch throughput.", + episodeTitle = "Can the U.S. Rein in Prediction Markets?", + podcastUuid = "", + podcastTitle = "Hard Fork", + timeSecs = 340, + createdAtText = "May 7, 2024 - 6:40 PM", + onPlayClick = {}, + onClose = {}, + ) + } +} diff --git a/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/bookmark/BookmarksFragment.kt b/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/bookmark/BookmarksFragment.kt index b3550f1f964..bc2d3264adf 100644 --- a/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/bookmark/BookmarksFragment.kt +++ b/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/bookmark/BookmarksFragment.kt @@ -153,6 +153,16 @@ class BookmarksFragment : BaseFragment() { addFragment(fragment) } }, + onBookmarkDetailClick = { data -> + BookmarkDetailFragment.show( + fragmentManager = parentFragmentManager, + bookmark = data.bookmark, + episodeTitle = data.episodeTitle, + podcastUuid = data.podcastUuid, + podcastTitle = data.podcastTitle, + sourceView = sourceView, + ) + }, onSearchBarClearButtonClick = { bookmarksViewModel.searchBarClearButtonTapped() }, diff --git a/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/bookmark/BookmarksPage.kt b/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/bookmark/BookmarksPage.kt index 401804f55a0..d36195df26e 100644 --- a/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/bookmark/BookmarksPage.kt +++ b/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/bookmark/BookmarksPage.kt @@ -19,6 +19,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester @@ -73,6 +74,7 @@ fun BookmarksPage( openFragment: (Fragment) -> Unit, onSearchBarClearButtonClick: () -> Unit, onHeadphoneControlsButtonClick: () -> Unit, + onBookmarkDetailClick: (BookmarksViewModel.BookmarkDetailData) -> Unit, modifier: Modifier = Modifier, ) { val context = LocalContext.current @@ -127,6 +129,13 @@ fun BookmarksPage( Toast.makeText(context, string, Toast.LENGTH_SHORT).show() } } + + val currentOnBookmarkDetailClick by rememberUpdatedState(onBookmarkDetailClick) + LaunchedEffect(bookmarksViewModel) { + bookmarksViewModel.showBookmarkDetail.collect { data -> + currentOnBookmarkDetailClick(data) + } + } } @Composable diff --git a/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/viewmodel/BookmarksViewModel.kt b/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/viewmodel/BookmarksViewModel.kt index e50320024fe..a08f3a0d0ee 100644 --- a/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/viewmodel/BookmarksViewModel.kt +++ b/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/viewmodel/BookmarksViewModel.kt @@ -27,6 +27,8 @@ import au.com.shiftyjelly.pocketcasts.repositories.di.IoDispatcher import au.com.shiftyjelly.pocketcasts.repositories.playback.PlaybackManager import au.com.shiftyjelly.pocketcasts.repositories.podcast.EpisodeManager import au.com.shiftyjelly.pocketcasts.repositories.podcast.PodcastManager +import au.com.shiftyjelly.pocketcasts.utils.featureflag.Feature +import au.com.shiftyjelly.pocketcasts.utils.featureflag.FeatureFlag import au.com.shiftyjelly.pocketcasts.utils.log.LogBuffer import au.com.shiftyjelly.pocketcasts.views.multiselect.MultiSelectBookmarksHelper import au.com.shiftyjelly.pocketcasts.views.multiselect.MultiSelectHelper @@ -70,12 +72,15 @@ class BookmarksViewModel private val _uiState = MutableStateFlow(UiState.Loading) val uiState: StateFlow = _uiState - private val _showOptionsDialog = MutableSharedFlow() + private val _showOptionsDialog = MutableSharedFlow(extraBufferCapacity = 1) val showOptionsDialog = _showOptionsDialog.asSharedFlow() - private val _message = MutableSharedFlow() + private val _message = MutableSharedFlow(extraBufferCapacity = 1) val message = _message.asSharedFlow() + private val _showBookmarkDetail = MutableSharedFlow(extraBufferCapacity = 1) + val showBookmarkDetail = _showBookmarkDetail.asSharedFlow() + private var isFragmentActive: Boolean = true private var sourceView: SourceView = SourceView.UNKNOWN @@ -250,12 +255,31 @@ class BookmarksViewModel } private fun onRowClick(bookmark: Bookmark) { - if ((_uiState.value as? UiState.Loaded)?.isMultiSelecting == false) return - - if (multiSelectHelper.isSelected(bookmark)) { - multiSelectHelper.deselect(bookmark) + if ((_uiState.value as? UiState.Loaded)?.isMultiSelecting == true) { + if (multiSelectHelper.isSelected(bookmark)) { + multiSelectHelper.deselect(bookmark) + } else { + multiSelectHelper.select(bookmark) + } + } else if (FeatureFlag.isEnabled(Feature.SMART_BOOKMARKS)) { + viewModelScope.launch(ioDispatcher) { + val loadedState = _uiState.value as? UiState.Loaded ?: return@launch + val episodeTitle = loadedState.bookmarkIdAndEpisodeMap[bookmark.uuid] + ?.title.orEmpty() + val podcastTitle = bookmark.podcastTitle.ifEmpty { + podcastManager.findPodcastByUuid(bookmark.podcastUuid)?.title.orEmpty() + } + _showBookmarkDetail.emit( + BookmarkDetailData( + bookmark = bookmark, + episodeTitle = episodeTitle, + podcastUuid = bookmark.podcastUuid, + podcastTitle = podcastTitle, + ), + ) + } } else { - multiSelectHelper.select(bookmark) + play(bookmark) } } @@ -398,6 +422,13 @@ class BookmarksViewModel data object BookmarkEpisodeNotFound : BookmarkMessage() data class PlayingBookmark(val bookmarkTitle: String) : BookmarkMessage() } + + data class BookmarkDetailData( + val bookmark: Bookmark, + val episodeTitle: String, + val podcastUuid: String, + val podcastTitle: String, + ) } internal sealed class MessageViewColors { diff --git a/modules/features/player/src/test/java/au/com/shiftyjelly/pocketcasts/player/viewmodel/BookmarksViewModelTest.kt b/modules/features/player/src/test/java/au/com/shiftyjelly/pocketcasts/player/viewmodel/BookmarksViewModelTest.kt index eb534e2060b..097d15d0f2c 100644 --- a/modules/features/player/src/test/java/au/com/shiftyjelly/pocketcasts/player/viewmodel/BookmarksViewModelTest.kt +++ b/modules/features/player/src/test/java/au/com/shiftyjelly/pocketcasts/player/viewmodel/BookmarksViewModelTest.kt @@ -2,6 +2,7 @@ package au.com.shiftyjelly.pocketcasts.player.viewmodel import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.lifecycle.MutableLiveData +import app.cash.turbine.test import au.com.shiftyjelly.pocketcasts.analytics.SourceView import au.com.shiftyjelly.pocketcasts.analytics.testing.TestEventSink import au.com.shiftyjelly.pocketcasts.models.entity.Bookmark @@ -20,7 +21,10 @@ import au.com.shiftyjelly.pocketcasts.repositories.bookmark.BookmarkManager import au.com.shiftyjelly.pocketcasts.repositories.playback.PlaybackManager import au.com.shiftyjelly.pocketcasts.repositories.podcast.EpisodeManager import au.com.shiftyjelly.pocketcasts.repositories.podcast.PodcastManager +import au.com.shiftyjelly.pocketcasts.sharedtest.InMemoryFeatureFlagRule import au.com.shiftyjelly.pocketcasts.sharedtest.MainCoroutineRule +import au.com.shiftyjelly.pocketcasts.utils.featureflag.Feature +import au.com.shiftyjelly.pocketcasts.utils.featureflag.FeatureFlag import au.com.shiftyjelly.pocketcasts.views.multiselect.MultiSelectBookmarksHelper import com.automattic.eventhorizon.EventHorizon import java.time.Instant @@ -43,6 +47,7 @@ import org.mockito.junit.MockitoJUnitRunner import org.mockito.kotlin.any import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock +import org.mockito.kotlin.verify import org.mockito.kotlin.whenever @ExperimentalCoroutinesApi @@ -55,6 +60,9 @@ class BookmarksViewModelTest { @get:Rule val coroutineRule = MainCoroutineRule() + @get:Rule + val featureFlagRule = InMemoryFeatureFlagRule() + @Mock private lateinit var bookmarkManager: BookmarkManager @@ -173,4 +181,38 @@ class BookmarksViewModelTest { assertEquals("uuid1", result[0].uuid) assertEquals("uuid2", result[1].uuid) } + + @Test + fun `given not multi-selecting, when row clicked, then showBookmarkDetail emits`() = runTest { + FeatureFlag.setEnabled(Feature.SMART_BOOKMARKS, true) + val bookmark = Bookmark("uuid1", episodeUuid = episodeUuid) + whenever(bookmarkManager.findEpisodeBookmarksFlow(episode, BookmarksSortTypeDefault.TIMESTAMP)) + .thenReturn(flowOf(listOf(bookmark))) + + bookmarksViewModel.loadBookmarks(episodeUuid, SourceView.PLAYER) + + bookmarksViewModel.showBookmarkDetail.test { + val loaded = bookmarksViewModel.uiState.value as BookmarksViewModel.UiState.Loaded + loaded.onRowClick(bookmark) + assertEquals("uuid1", awaitItem().bookmark.uuid) + } + } + + @Test + fun `given multi-selecting, when row clicked, then showBookmarkDetail does not emit`() = runTest { + val bookmark = Bookmark("uuid1", episodeUuid = episodeUuid) + whenever(bookmarkManager.findEpisodeBookmarksFlow(episode, BookmarksSortTypeDefault.TIMESTAMP)) + .thenReturn(flowOf(listOf(bookmark))) + whenever(multiSelectHelper.isMultiSelectingLive) + .thenReturn(MutableLiveData().apply { value = true }) + + bookmarksViewModel.loadBookmarks(episodeUuid, SourceView.PLAYER) + + bookmarksViewModel.showBookmarkDetail.test { + val loaded = bookmarksViewModel.uiState.value as BookmarksViewModel.UiState.Loaded + loaded.onRowClick(bookmark) + expectNoEvents() + } + verify(multiSelectHelper).select(bookmark) + } } diff --git a/modules/services/compose/src/main/java/au/com/shiftyjelly/pocketcasts/compose/bookmark/BookmarkRow.kt b/modules/services/compose/src/main/java/au/com/shiftyjelly/pocketcasts/compose/bookmark/BookmarkRow.kt index 75d7458f1fc..268d4202bff 100644 --- a/modules/services/compose/src/main/java/au/com/shiftyjelly/pocketcasts/compose/bookmark/BookmarkRow.kt +++ b/modules/services/compose/src/main/java/au/com/shiftyjelly/pocketcasts/compose/bookmark/BookmarkRow.kt @@ -137,7 +137,7 @@ fun BookmarkRow( TextH70( text = bookmark.episodeTitle, color = colors.bookmarkRow.secondaryText, - maxLines = 2, + maxLines = 1, modifier = Modifier.padding(top = 8.dp), ) } @@ -153,19 +153,10 @@ fun BookmarkRow( TextH40( text = displayTitle, color = colors.bookmarkRow.primaryText, - maxLines = 2, + maxLines = 1, lineHeight = 18.sp, ) - if (!bookmark.aiSummary.isNullOrEmpty()) { - TextH70( - text = bookmark.aiSummary.orEmpty(), - color = colors.bookmarkRow.secondaryText, - maxLines = 2, - modifier = Modifier.padding(top = 2.dp), - ) - } - TextH70( text = createdAtText, color = colors.bookmarkRow.secondaryText, diff --git a/modules/services/localization/src/main/res/values/strings.xml b/modules/services/localization/src/main/res/values/strings.xml index 7410e3be44d..616fba8b9a6 100644 --- a/modules/services/localization/src/main/res/values/strings.xml +++ b/modules/services/localization/src/main/res/values/strings.xml @@ -2319,6 +2319,7 @@ Play at bookmark position %s + Play from %s Headphone settings %d bookmarks 1 bookmark diff --git a/modules/services/model/src/main/java/au/com/shiftyjelly/pocketcasts/models/db/dao/BookmarkDao.kt b/modules/services/model/src/main/java/au/com/shiftyjelly/pocketcasts/models/db/dao/BookmarkDao.kt index cf83ececc1f..ff202f86cec 100644 --- a/modules/services/model/src/main/java/au/com/shiftyjelly/pocketcasts/models/db/dao/BookmarkDao.kt +++ b/modules/services/model/src/main/java/au/com/shiftyjelly/pocketcasts/models/db/dao/BookmarkDao.kt @@ -32,7 +32,7 @@ abstract class BookmarkDao { @Transaction open suspend fun deleteAll(uuids: Collection) { uuids.chunked(AppDatabase.SQLITE_BIND_ARG_LIMIT).forEach { chunk -> - deleteAllUnsafe(uuids) + deleteAllUnsafe(chunk) } } @@ -239,7 +239,7 @@ abstract class BookmarkDao { @Transaction open suspend fun getAll(uuids: Collection): List { return uuids.chunked(999).flatMap { chunk -> - getAllUnsafe(uuids) + getAllUnsafe(chunk) } }