Skip to content

Commit

Permalink
Navigate to Episode page from Player view when clicking on episode ti…
Browse files Browse the repository at this point in the history
…tle (#85)

* Remove redundant destinations

* Navigate to the corresponding episode On title click from player view
  • Loading branch information
mr3y-the-programmer authored Sep 5, 2024
1 parent 5b5d8a1 commit 7d9b771
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 24 deletions.
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

0 comments on commit 7d9b771

Please sign in to comment.