Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Navigate to Episode page from Player view when clicking on episode title #85

Merged
merged 2 commits into from
Sep 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,19 @@ sealed interface Destinations {
data object Explore : Destinations

@Serializable
sealed class PodcastDetails(val podcastId: Long) : Destinations
data class PodcastDetailsSubscriptionsGraph(val id: Long) : Destinations

@Serializable
data class PodcastDetailsSubscriptionsGraph(val id: Long) : PodcastDetails(id)
data class PodcastDetailsExploreGraph(val id: Long) : Destinations

@Serializable
data class PodcastDetailsExploreGraph(val id: Long) : PodcastDetails(id)
data class EpisodeDetailsSubscriptionsGraph(val id: Long, val artworkUrl: String) : Destinations

@Serializable
sealed class EpisodeDetails(val episodeId: Long, val podcastArtworkUrl: String) : Destinations
data class EpisodeDetailsExploreGraph(val id: Long, val artworkUrl: String) : Destinations

@Serializable
data class EpisodeDetailsSubscriptionsGraph(val id: Long, val artworkUrl: String) : EpisodeDetails(id, artworkUrl)

@Serializable
data class EpisodeDetailsExploreGraph(val id: Long, val artworkUrl: String) : EpisodeDetails(id, artworkUrl)

@Serializable
data class EpisodeDetailsLibraryGraph(val id: Long, val artworkUrl: String) : EpisodeDetails(id, artworkUrl)
data class EpisodeDetailsLibraryGraph(val id: Long, val artworkUrl: String) : Destinations

@Serializable
data object Library : Destinations
Expand Down
10 changes: 5 additions & 5 deletions app/src/main/kotlin/com/mr3y/podcaster/ui/navigation/NavGraph.kt
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ fun PodcasterNavGraph(
contentPadding = contentPadding,
excludedWindowInsets = excludedWindowInsets,
viewModel = hiltViewModel<PodcastDetailsViewModel, PodcastDetailsViewModel.Factory>(
creationCallback = { factory -> factory.create(navBackStackEntry.toRoute<Destinations.PodcastDetailsSubscriptionsGraph>().podcastId) },
creationCallback = { factory -> factory.create(navBackStackEntry.toRoute<Destinations.PodcastDetailsSubscriptionsGraph>().id) },
),
)
}
Expand All @@ -80,7 +80,7 @@ fun PodcasterNavGraph(
viewModel = hiltViewModel<EpisodeDetailsViewModel, EpisodeDetailsViewModel.Factory>(
creationCallback = { factory ->
val destination = navBackStackEntry.toRoute<Destinations.EpisodeDetailsSubscriptionsGraph>()
factory.create(destination.episodeId, destination.podcastArtworkUrl)
factory.create(destination.id, destination.artworkUrl)
},
),
)
Expand Down Expand Up @@ -108,7 +108,7 @@ fun PodcasterNavGraph(
contentPadding = contentPadding,
excludedWindowInsets = excludedWindowInsets,
viewModel = hiltViewModel<PodcastDetailsViewModel, PodcastDetailsViewModel.Factory>(
creationCallback = { factory -> factory.create(navBackStackEntry.toRoute<Destinations.PodcastDetailsExploreGraph>().podcastId) },
creationCallback = { factory -> factory.create(navBackStackEntry.toRoute<Destinations.PodcastDetailsExploreGraph>().id) },
),
)
}
Expand All @@ -123,7 +123,7 @@ fun PodcasterNavGraph(
viewModel = hiltViewModel<EpisodeDetailsViewModel, EpisodeDetailsViewModel.Factory>(
creationCallback = { factory ->
val destination = navBackStackEntry.toRoute<Destinations.EpisodeDetailsExploreGraph>()
factory.create(destination.episodeId, destination.podcastArtworkUrl)
factory.create(destination.id, destination.artworkUrl)
},
),
)
Expand Down Expand Up @@ -174,7 +174,7 @@ fun PodcasterNavGraph(
viewModel = hiltViewModel<EpisodeDetailsViewModel, EpisodeDetailsViewModel.Factory>(
creationCallback = { factory ->
val destination = navBackStackEntry.toRoute<Destinations.EpisodeDetailsLibraryGraph>()
factory.create(destination.episodeId, destination.podcastArtworkUrl)
factory.create(destination.id, destination.artworkUrl)
},
),
)
Expand Down
35 changes: 28 additions & 7 deletions app/src/main/kotlin/com/mr3y/podcaster/ui/screens/HomeScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.compose.LifecycleStartEffect
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavDestination
import androidx.navigation.NavDestination.Companion.hasRoute
import androidx.navigation.NavDestination.Companion.hierarchy
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.NavHostController
Expand Down Expand Up @@ -234,6 +235,22 @@ fun HomeScreen(
onToggleFavoriteStatus = {
appState.toggleEpisodeFavoriteStatus(isFavorite = it, episode = activeEpisode.episode)
},
onEpisodeClick = {
scope.launch { state.animateTo(PlayerViewState.Collapsed) }

val isOnExploreTab = currentDestination?.hierarchy?.any { it.route == createRoutePattern<Destinations.ExploreGraph>() } == true
if (!isOnExploreTab) {
navController.navigateToGraph(Destinations.ExploreGraph)
}
// Avoid having multiple copies of the same episode stacked on top of each other similar to `launchSingleTop`
// but the problem with `launchSingleTop` is it will prevent navigating to the same destination that has a different arguments
if (
currentDestination?.hasRoute(route = Destinations.EpisodeDetailsExploreGraph::class) == false ||
navBackStackEntry?.arguments?.getLong("id") != activeEpisode.episode.id
) {
navController.navigate(Destinations.EpisodeDetailsExploreGraph(activeEpisode.episode.id, activeEpisode.episode.artworkUrl))
}
},
onBack = { scope.launch { state.animateTo(PlayerViewState.Collapsed) } },
containerColor = containerColor,
)
Expand Down Expand Up @@ -313,13 +330,7 @@ private fun BottomBar(
selected = isSelected,
onClick = {
if (!isSelected) {
navController.navigate(tab.destination) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
navController.navigateToGraph(tab.destination)
} else if (currentDestination?.route !in bottomBarTabs.map { it.route }) {
currentDestination?.parent?.findStartDestination()?.id?.let {
navController.popBackStackOnce(it)
Expand All @@ -338,6 +349,16 @@ private fun BottomBar(
}
}

private fun NavHostController.navigateToGraph(destinationGraph: Destinations) {
navigate(destinationGraph) {
popUpTo(graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
}

/**
* Idempotent version of [NavHostController.popBackStack] function.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import androidx.compose.animation.slideOutHorizontally
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.background
import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
Expand Down Expand Up @@ -94,6 +96,7 @@ fun ExpandedPlayerView(
onSeekToNext: () -> Unit,
onSeekToPrevious: () -> Unit,
onToggleFavoriteStatus: (Boolean) -> Unit,
onEpisodeClick: () -> Unit,
onBack: () -> Unit,
containerColor: Color,
modifier: Modifier = Modifier,
Expand Down Expand Up @@ -137,6 +140,8 @@ fun ExpandedPlayerView(
modifier = Modifier
.size(360.dp),
)

val strings = LocalStrings.current
Text(
text = targetEpisode.title,
color = MaterialTheme.colorScheme.onSurface,
Expand All @@ -145,9 +150,14 @@ fun ExpandedPlayerView(
maxLines = 3,
overflow = TextOverflow.Ellipsis,
textAlign = TextAlign.Center,
modifier = Modifier.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null,
onClickLabel = strings.navigate_to_episode_a11y_label(targetEpisode.title),
onClick = onEpisodeClick
)
)

val strings = LocalStrings.current
Text(
text = if (playingStatus == PlayingStatus.Loading) {
strings.buffering_playback
Expand Down Expand Up @@ -445,6 +455,7 @@ fun ExpandedPlayerViewPreview(
progress = 1150,
onSeeking = {},
onToggleFavoriteStatus = {},
onEpisodeClick = {},
onBack = {},
containerColor = MaterialTheme.colorScheme.surface,
modifier = Modifier.fillMaxSize(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ val EnStrings = PodcasterStrings(
retry_label = "Retry",
currently_playing = "Currently Playing",
buffering_playback = "Buffering...",
navigate_to_episode_a11y_label = { "Navigate to $it details page" },
search_for_podcast_placeholder = "Search for a podcast or add RSS Url",
recent_searches_label = "Recent Searches",
close_label = "CLOSE",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ data class PodcasterStrings(
val retry_label: String,
val currently_playing: String,
val buffering_playback: String,
val navigate_to_episode_a11y_label: (String) -> String,
val search_for_podcast_placeholder: String,
val recent_searches_label: String,
val close_label: String,
Expand Down
Loading