Skip to content

Commit

Permalink
Add Library Tab and move Downloads to it (#70)
Browse files Browse the repository at this point in the history
  • Loading branch information
mr3y-the-programmer authored Jul 8, 2024
1 parent eecec29 commit a066990
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,16 @@ sealed interface Destinations : Destination {
data object SubscriptionsGraph : Destinations

@Serializable
data object Subscriptions : Destinations
data object ExploreGraph : Destinations

@Serializable
data object ExploreGraph : Destinations
data object LibraryGraph : Destinations

@Serializable
data object SettingsGraph : Destinations

@Serializable
data object Subscriptions : Destinations

@Serializable
data object Explore : Destinations
Expand All @@ -36,10 +42,10 @@ sealed interface Destinations : Destination {
data class EpisodeDetailsExploreGraph(val id: Long, val artworkUrl: String) : EpisodeDetails(id, artworkUrl)

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

@Serializable
data object SettingsGraph : Destinations
data object Library : Destinations

@Serializable
data object Settings : Destinations
Expand Down
52 changes: 31 additions & 21 deletions app/src/main/kotlin/com/mr3y/podcaster/ui/navigation/NavGraph.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import com.mr3y.podcaster.ui.screens.DownloadsScreen
import com.mr3y.podcaster.ui.screens.EpisodeDetailsScreen
import com.mr3y.podcaster.ui.screens.ExploreScreen
import com.mr3y.podcaster.ui.screens.ImportExportScreen
import com.mr3y.podcaster.ui.screens.LibraryScreen
import com.mr3y.podcaster.ui.screens.LicensesScreen
import com.mr3y.podcaster.ui.screens.PodcastDetailsScreen
import com.mr3y.podcaster.ui.screens.SettingsScreen
Expand Down Expand Up @@ -109,52 +110,61 @@ fun PodcasterNavGraph(
)
}
}

navigation<Destinations.SettingsGraph>(
startDestination = createRoutePattern<Destinations.Settings>(),
navigation<Destinations.LibraryGraph>(
startDestination = createRoutePattern<Destinations.Library>()
) {
composable<Destinations.Settings> {
SettingsScreen(
userPreferences = userPreferences,
composable<Destinations.Library> {
LibraryScreen(
onDownloadsClick = { navController.navigate(Destinations.Downloads) },
externalContentPadding = contentPadding,
excludedWindowInsets = excludedWindowInsets,
onDownloadsClick = { navController.navigate(Destinations.Downloads) },
onImportExportClick = { navController.navigate(Destinations.ImportExport) },
onLicensesClick = { navController.navigate(Destinations.Licenses) },
)
}
composable<Destinations.Downloads> {
DownloadsScreen(
onEpisodeClick = { episodeId, podcastArtworkUrl -> navController.navigate(Destinations.EpisodeDetailsDownloadsGraph(episodeId, podcastArtworkUrl)) },
onEpisodeClick = { episodeId, podcastArtworkUrl -> navController.navigate(Destinations.EpisodeDetailsLibraryGraph(episodeId, podcastArtworkUrl)) },
onNavigateUp = navController::navigateUpOnce,
appState = appState,
contentPadding = contentPadding,
excludedWindowInsets = excludedWindowInsets,
)
}
composable<Destinations.Licenses> {
LicensesScreen(
composable<Destinations.EpisodeDetailsLibraryGraph> {
EpisodeDetailsScreen(
onNavigateUp = navController::navigateUpOnce,
appState = appState,
contentPadding = contentPadding,
excludedWindowInsets = excludedWindowInsets,
viewModel = hiltViewModel<EpisodeDetailsViewModel, EpisodeDetailsViewModel.Factory>(
creationCallback = { factory -> factory.create(episodeId, podcastArtworkUrl) },
),
)
}
}
navigation<Destinations.SettingsGraph>(
startDestination = createRoutePattern<Destinations.Settings>(),
) {
composable<Destinations.Settings> {
SettingsScreen(
userPreferences = userPreferences,
externalContentPadding = contentPadding,
excludedWindowInsets = excludedWindowInsets,
onImportExportClick = { navController.navigate(Destinations.ImportExport) },
onLicensesClick = { navController.navigate(Destinations.Licenses) },
)
}
composable<Destinations.ImportExport> {
ImportExportScreen(
composable<Destinations.Licenses> {
LicensesScreen(
onNavigateUp = navController::navigateUpOnce,
contentPadding = contentPadding,
externalContentPadding = contentPadding,
excludedWindowInsets = excludedWindowInsets,
)
}
composable<Destinations.EpisodeDetailsDownloadsGraph> {
EpisodeDetailsScreen(
composable<Destinations.ImportExport> {
ImportExportScreen(
onNavigateUp = navController::navigateUpOnce,
appState = appState,
contentPadding = contentPadding,
excludedWindowInsets = excludedWindowInsets,
viewModel = hiltViewModel<EpisodeDetailsViewModel, EpisodeDetailsViewModel.Factory>(
creationCallback = { factory -> factory.create(episodeId, podcastArtworkUrl) },
),
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.LibraryAdd
import androidx.compose.material.icons.outlined.Search
import androidx.compose.material.icons.outlined.Settings
import androidx.compose.material3.Icon
Expand Down Expand Up @@ -267,6 +268,12 @@ private fun BottomBar(
createRoutePattern<Destinations.ExploreGraph>(),
Destinations.ExploreGraph,
),
BottomBarTab(
strings.tab_library_label,
Icons.Outlined.LibraryAdd,
createRoutePattern<Destinations.LibraryGraph>(),
Destinations.LibraryGraph,
),
BottomBarTab(
strings.tab_settings_label,
Icons.Outlined.Settings,
Expand Down
129 changes: 129 additions & 0 deletions app/src/main/kotlin/com/mr3y/podcaster/ui/screens/LibraryScreen.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package com.mr3y.podcaster.ui.screens

import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.exclude
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.FileDownload
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.ScaffoldDefaults
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import com.mr3y.podcaster.LocalStrings
import com.mr3y.podcaster.ui.components.TopBar
import com.mr3y.podcaster.ui.components.plus
import com.mr3y.podcaster.ui.preview.DynamicColorsParameterProvider
import com.mr3y.podcaster.ui.preview.PodcasterPreview
import com.mr3y.podcaster.ui.theme.PodcasterTheme
import com.mr3y.podcaster.ui.theme.isAppThemeDark
import com.mr3y.podcaster.ui.theme.setStatusBarAppearanceLight

@Composable
fun LibraryScreen(
onDownloadsClick: () -> Unit,
externalContentPadding: PaddingValues,
excludedWindowInsets: WindowInsets?,
modifier: Modifier = Modifier,
) {
val isDarkTheme = isAppThemeDark()
val context = LocalContext.current
LaunchedEffect(key1 = isDarkTheme) {
context.setStatusBarAppearanceLight(isAppearanceLight = !isDarkTheme)
}
val strings = LocalStrings.current
Scaffold(
topBar = {
TopBar(
onUpButtonClick = null,
title = {
Text(
text = strings.library_label,
color = MaterialTheme.colorScheme.onSurface,
fontWeight = FontWeight.Normal,
)
},
modifier = Modifier.fillMaxWidth(),
)
},
contentWindowInsets = if (excludedWindowInsets != null) ScaffoldDefaults.contentWindowInsets.exclude(excludedWindowInsets) else ScaffoldDefaults.contentWindowInsets,
containerColor = MaterialTheme.colorScheme.surface,
modifier = modifier,
) { contentPadding ->
Column(
modifier = Modifier
.padding(contentPadding + externalContentPadding)
.fillMaxSize()
.verticalScroll(rememberScrollState())
) {
HorizontalDivider(modifier = Modifier.padding(horizontal = 8.dp))
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.padding(vertical = 8.dp, horizontal = 16.dp)
.fillMaxWidth()
.heightIn(min = 48.dp)
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null,
onClickLabel = null,
role = Role.Button,
onClick = onDownloadsClick,
),
) {
Icon(
imageVector = Icons.Outlined.FileDownload,
contentDescription = null,
)
Spacer(modifier = Modifier.width(8.dp))
Text(
text = strings.downloads_label,
style = MaterialTheme.typography.headlineSmall,
color = MaterialTheme.colorScheme.onSurface,
modifier = Modifier.fillMaxWidth(),
)
}
HorizontalDivider(modifier = Modifier.padding(horizontal = 8.dp))
}
}
}

@PodcasterPreview
@Composable
fun LibraryScreenPreview(
@PreviewParameter(DynamicColorsParameterProvider::class) isDynamicColorsOn: Boolean,
) {
PodcasterTheme(dynamicColor = isDynamicColorsOn) {
LibraryScreen(
onDownloadsClick = {},
externalContentPadding = PaddingValues(0.dp),
excludedWindowInsets = null,
modifier = Modifier.fillMaxSize()
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ fun SettingsScreen(
userPreferences: UserPreferences,
externalContentPadding: PaddingValues,
excludedWindowInsets: WindowInsets?,
onDownloadsClick: () -> Unit,
onImportExportClick: () -> Unit,
onLicensesClick: () -> Unit,
modifier: Modifier = Modifier,
Expand All @@ -86,7 +85,6 @@ fun SettingsScreen(
userPreferences.disableDynamicColor()
}
},
onDownloadsClick = onDownloadsClick,
onImportExportClick = onImportExportClick,
onLicensesClick = onLicensesClick,
onFeedbackClick = {
Expand All @@ -107,7 +105,6 @@ fun SettingsScreen(
onSelectingTheme: (Theme) -> Unit,
isDynamicColorsOn: Boolean,
onToggleDynamicColor: (Boolean) -> Unit,
onDownloadsClick: () -> Unit,
onImportExportClick: () -> Unit,
onLicensesClick: () -> Unit,
onFeedbackClick: () -> Unit,
Expand Down Expand Up @@ -158,13 +155,6 @@ fun SettingsScreen(
onToggleDynamicColor,
)
HorizontalDivider(modifier = Modifier.padding(horizontal = 8.dp))
SettingsButton(
text = strings.downloads_label,
icon = Icons.Outlined.FileDownload,
onClick = onDownloadsClick,
modifier = Modifier.padding(vertical = 8.dp, horizontal = 16.dp),
)
HorizontalDivider(modifier = Modifier.padding(horizontal = 8.dp))
SettingsButton(
text = strings.import_export_label,
icon = Icons.Outlined.ImportExport,
Expand Down Expand Up @@ -398,7 +388,6 @@ fun SettingsScreenPreview(
{},
{},
{},
{},
PaddingValues(0.dp),
null,
modifier = Modifier.fillMaxSize(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import cafe.adriel.lyricist.LyricistStrings
val EnStrings = PodcasterStrings(
tab_subscriptions_label = "Subscriptions",
tab_explore_label = "Explore",
tab_library_label = "Library",
tab_settings_label = "Settings",
subscriptions_refresh_result_error = "Couldn't refresh feeds",
subscriptions_refresh_result_mixed = "Couldn't refresh some feeds",
Expand Down Expand Up @@ -44,6 +45,7 @@ val EnStrings = PodcasterStrings(
download_work_notification_message = "Downloading episode/s currently in progress...",
downloads_label = "Downloads",
downloads_empty_list = "You have no episodes downloaded or still downloading",
library_label = "Library",
settings_label = "Settings",
appearance_label = "Appearance",
theme_heading = "Theme",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import androidx.compose.ui.text.AnnotatedString
data class PodcasterStrings(
val tab_subscriptions_label: String,
val tab_explore_label: String,
val tab_library_label: String,
val tab_settings_label: String,
val subscriptions_refresh_result_error: String,
val subscriptions_refresh_result_mixed: String,
Expand Down Expand Up @@ -39,6 +40,7 @@ data class PodcasterStrings(
val download_work_notification_message: String,
val downloads_label: String,
val downloads_empty_list: String,
val library_label: String,
val settings_label: String,
val appearance_label: String,
val theme_heading: String,
Expand Down

0 comments on commit a066990

Please sign in to comment.