Skip to content
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
2 changes: 1 addition & 1 deletion Jetcaster/designsystem/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ android {

defaultConfig {
minSdk = libs.versions.minSdk.get().toInt()

vectorDrawables.useSupportLibrary = true
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
Expand All @@ -32,10 +31,12 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.res.painterResource
import coil.compose.AsyncImagePainter
import coil.compose.rememberAsyncImagePainter
import coil.request.ImageRequest
import com.example.jetcaster.designsystem.R

@Composable
fun PodcastImage(
Expand All @@ -45,6 +46,11 @@ fun PodcastImage(
contentScale: ContentScale = ContentScale.Crop,
placeholderBrush: Brush = thumbnailPlaceholderDefaultBrush(),
) {
if (LocalInspectionMode.current) {
Box(modifier = modifier.background(MaterialTheme.colorScheme.primary))
return
}

var imagePainterState by remember {
mutableStateOf<AsyncImagePainter.State>(AsyncImagePainter.State.Empty)
}
Expand All @@ -63,14 +69,15 @@ fun PodcastImage(
contentAlignment = Alignment.Center
) {
when (imagePainterState) {
is AsyncImagePainter.State.Loading -> {
CircularProgressIndicator(
is AsyncImagePainter.State.Loading,
is AsyncImagePainter.State.Error -> {
Image(
painter = painterResource(id = R.drawable.img_empty),
contentDescription = null,
modifier = Modifier
.size(48.dp)
.align(Alignment.Center)
.fillMaxSize()
)
}

else -> {
Box(
modifier = Modifier
Expand Down
61 changes: 61 additions & 0 deletions Jetcaster/designsystem/src/main/res/drawable/img_empty.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="180dp"
android:height="180dp"
android:viewportWidth="180"
android:viewportHeight="180">
<path
android:pathData="M0,0h180v180h-180z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="90"
android:startY="0"
android:endX="90"
android:endY="180"
android:type="linear">
<item android:offset="0" android:color="@color/surface_bright"/>
<item android:offset="1" android:color="@color/background"/>
</gradient>
</aapt:attr>
</path>
<group>
<clip-path
android:pathData="M56.67,123.52l66.85,-0l0,-66.85l-66.85,-0z"/>
<path
android:pathData="M80.54,108.49C77.92,108.49 75.77,107.33 75.77,105.9V59.26C75.77,57.83 77.92,56.67 80.54,56.67C83.17,56.67 85.32,57.83 85.32,59.26V105.9C85.32,107.33 83.17,108.49 80.54,108.49Z"
android:fillColor="#1A120A"/>
<path
android:pathData="M61.44,92.95C58.82,92.95 56.67,91.78 56.67,90.35V74.81C56.67,73.38 58.82,72.22 61.44,72.22C64.07,72.22 66.22,73.38 66.22,74.81V90.35C66.22,91.78 64.07,92.95 61.44,92.95Z"
android:fillColor="#1A120A"/>
<path
android:pathData="M99.64,98.13C97.02,98.13 94.87,96.96 94.87,95.54V69.62C94.87,68.2 97.02,67.03 99.64,67.03C102.27,67.03 104.42,68.2 104.42,69.62V95.54C104.42,96.96 102.27,98.13 99.64,98.13Z"
android:fillColor="#1A120A"/>
<path
android:pathData="M118.74,90.35C116.11,90.35 113.97,89.19 113.97,87.76V77.4C113.97,75.97 116.11,74.81 118.74,74.81C121.37,74.81 123.52,75.97 123.52,77.4V87.76C123.52,89.19 121.37,90.35 118.74,90.35Z"
android:fillColor="#1A120A"/>
<path
android:pathData="M80.54,115.46C77.92,115.46 75.77,114.29 75.77,112.86V66.22C75.77,64.8 77.92,63.63 80.54,63.63C83.17,63.63 85.32,64.8 85.32,66.22V112.86C85.32,114.29 83.17,115.46 80.54,115.46Z"
android:fillColor="#1A120A"/>
<path
android:pathData="M61.44,99.91C58.82,99.91 56.67,98.74 56.67,97.32V81.77C56.67,80.34 58.82,79.18 61.44,79.18C64.07,79.18 66.22,80.34 66.22,81.77V97.32C66.22,98.74 64.07,99.91 61.44,99.91Z"
android:fillColor="#1A120A"/>
<path
android:pathData="M99.64,105.09C97.02,105.09 94.87,103.92 94.87,102.5V76.59C94.87,75.16 97.02,74 99.64,74C102.27,74 104.42,75.16 104.42,76.59V102.5C104.42,103.92 102.27,105.09 99.64,105.09Z"
android:fillColor="#1A120A"/>
<path
android:pathData="M118.74,97.32C116.11,97.32 113.97,96.15 113.97,94.73V84.36C113.97,82.93 116.11,81.77 118.74,81.77C121.37,81.77 123.52,82.93 123.52,84.36V94.73C123.52,96.15 121.37,97.32 118.74,97.32Z"
android:fillColor="#1A120A"/>
<path
android:pathData="M80.54,123.52C77.91,123.52 75.76,122.48 75.76,121.21V79.55C75.76,78.28 77.91,77.24 80.54,77.24C83.16,77.24 85.31,78.28 85.31,79.55V121.21C85.31,122.48 83.16,123.52 80.54,123.52Z"
android:fillColor="#1A120A"/>
<path
android:pathData="M61.44,109.64C58.81,109.64 56.67,108.59 56.67,107.32V93.44C56.67,92.16 58.81,91.12 61.44,91.12C64.07,91.12 66.21,92.16 66.21,93.44V107.32C66.21,108.59 64.07,109.64 61.44,109.64Z"
android:fillColor="#1A120A"/>
<path
android:pathData="M99.63,114.26C97.01,114.26 94.86,113.22 94.86,111.95V88.81C94.86,87.54 97.01,86.49 99.63,86.49C102.26,86.49 104.41,87.54 104.41,88.81V111.95C104.41,113.22 102.26,114.26 99.63,114.26Z"
android:fillColor="#1A120A"/>
<path
android:pathData="M118.73,107.32C116.11,107.32 113.96,106.28 113.96,105.01V95.75C113.96,94.48 116.11,93.44 118.73,93.44C121.36,93.44 123.5,94.48 123.5,95.75V105.01C123.5,106.28 121.36,107.32 118.73,107.32Z"
android:fillColor="#1A120A"/>
</group>
</vector>
5 changes: 5 additions & 0 deletions Jetcaster/designsystem/src/main/res/values-night/colors.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="background">#FF1A120A</color>
<color name="surface_bright">#FF42372D</color>
</resources>
5 changes: 5 additions & 0 deletions Jetcaster/designsystem/src/main/res/values/colors.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="background">#FFFFF8F4</color>
<color name="surface_bright">#FFFFF8F4</color>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AccountCircle
import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
Expand Down Expand Up @@ -129,7 +131,6 @@ import kotlinx.coroutines.launch
data class HomeState(
val windowSizeClass: WindowSizeClass,
val featuredPodcasts: PersistentList<PodcastInfo>,
val isRefreshing: Boolean,
val selectedHomeCategory: HomeCategory,
val homeCategories: List<HomeCategory>,
val filterableCategoriesModel: FilterableCategoriesModel,
Expand Down Expand Up @@ -230,14 +231,73 @@ private fun getExcludedVerticalBounds(posture: Posture, hingePolicy: HingePolicy
}
}

@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun MainScreen(
windowSizeClass: WindowSizeClass,
navigateToPlayer: (EpisodeInfo) -> Unit,
viewModel: HomeViewModel = hiltViewModel()
) {
val viewState by viewModel.state.collectAsStateWithLifecycle()
val homeScreenUiState by viewModel.state.collectAsStateWithLifecycle()
when (val uiState = homeScreenUiState) {
is HomeScreenUiState.Loading -> HomeScreenLoading()
is HomeScreenUiState.Error -> HomeScreenError(onRetry = viewModel::refresh)
is HomeScreenUiState.Ready -> {
HomeScreenReady(
uiState = uiState,
windowSizeClass = windowSizeClass,
navigateToPlayer = navigateToPlayer,
viewModel = viewModel,
)
}
}
}

@Composable
private fun HomeScreenLoading(modifier: Modifier = Modifier) {
Surface(modifier.fillMaxSize()) {
Box {
CircularProgressIndicator(
modifier = Modifier.align(Alignment.Center)
)
}
}
}

@Composable
private fun HomeScreenError(onRetry: () -> Unit, modifier: Modifier = Modifier) {
Surface(modifier = modifier) {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxSize(),
) {
Text(
text = stringResource(id = R.string.an_error_has_occurred),
modifier = Modifier.padding(16.dp)
)
Button(onClick = onRetry) {
Text(text = stringResource(id = R.string.retry_label))
}
}
}
}

@Preview
@Composable
fun HomeScreenErrorPreview() {
JetcasterTheme {
HomeScreenError(onRetry = {})
}
}

@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
private fun HomeScreenReady(
uiState: HomeScreenUiState.Ready,
windowSizeClass: WindowSizeClass,
navigateToPlayer: (EpisodeInfo) -> Unit,
viewModel: HomeViewModel = hiltViewModel()
) {
val navigator = rememberSupportingPaneScaffoldNavigator<String>(
scaffoldDirective = calculateScaffoldDirective(currentWindowAdaptiveInfo())
)
Expand All @@ -247,13 +307,12 @@ fun MainScreen(

val homeState = HomeState(
windowSizeClass = windowSizeClass,
featuredPodcasts = viewState.featuredPodcasts,
isRefreshing = viewState.refreshing,
homeCategories = viewState.homeCategories,
selectedHomeCategory = viewState.selectedHomeCategory,
filterableCategoriesModel = viewState.filterableCategoriesModel,
podcastCategoryFilterResult = viewState.podcastCategoryFilterResult,
library = viewState.library,
featuredPodcasts = uiState.featuredPodcasts,
homeCategories = uiState.homeCategories,
selectedHomeCategory = uiState.selectedHomeCategory,
filterableCategoriesModel = uiState.filterableCategoriesModel,
podcastCategoryFilterResult = uiState.podcastCategoryFilterResult,
library = uiState.library,
onHomeCategorySelected = viewModel::onHomeCategorySelected,
onCategorySelected = viewModel::onCategorySelected,
onPodcastUnfollowed = viewModel::onPodcastUnfollowed,
Expand Down Expand Up @@ -403,7 +462,6 @@ private fun HomeScreen(
showGrid = showGrid,
showHomeCategoryTabs = homeState.showHomeCategoryTabs,
featuredPodcasts = homeState.featuredPodcasts,
isRefreshing = homeState.isRefreshing,
selectedHomeCategory = homeState.selectedHomeCategory,
homeCategories = homeState.homeCategories,
filterableCategoriesModel = homeState.filterableCategoriesModel,
Expand Down Expand Up @@ -433,7 +491,6 @@ private fun HomeContent(
showGrid: Boolean,
showHomeCategoryTabs: Boolean,
featuredPodcasts: PersistentList<PodcastInfo>,
isRefreshing: Boolean,
selectedHomeCategory: HomeCategory,
homeCategories: List<HomeCategory>,
filterableCategoriesModel: FilterableCategoriesModel,
Expand Down Expand Up @@ -467,7 +524,6 @@ private fun HomeContent(
pagerState = pagerState,
showHomeCategoryTabs = showHomeCategoryTabs,
featuredPodcasts = featuredPodcasts,
isRefreshing = isRefreshing,
selectedHomeCategory = selectedHomeCategory,
homeCategories = homeCategories,
filterableCategoriesModel = filterableCategoriesModel,
Expand All @@ -487,7 +543,6 @@ private fun HomeContent(
pagerState = pagerState,
showHomeCategoryTabs = showHomeCategoryTabs,
featuredPodcasts = featuredPodcasts,
isRefreshing = isRefreshing,
selectedHomeCategory = selectedHomeCategory,
homeCategories = homeCategories,
filterableCategoriesModel = filterableCategoriesModel,
Expand All @@ -511,7 +566,6 @@ private fun HomeContentColumn(
showHomeCategoryTabs: Boolean,
pagerState: PagerState,
featuredPodcasts: PersistentList<PodcastInfo>,
isRefreshing: Boolean,
selectedHomeCategory: HomeCategory,
homeCategories: List<HomeCategory>,
filterableCategoriesModel: FilterableCategoriesModel,
Expand Down Expand Up @@ -542,10 +596,6 @@ private fun HomeContentColumn(
}
}

if (isRefreshing) {
// TODO show a progress indicator or similar
}

if (showHomeCategoryTabs) {
item {
HomeCategoryTabs(
Expand Down Expand Up @@ -586,7 +636,6 @@ private fun HomeContentGrid(
showHomeCategoryTabs: Boolean,
pagerState: PagerState,
featuredPodcasts: PersistentList<PodcastInfo>,
isRefreshing: Boolean,
selectedHomeCategory: HomeCategory,
homeCategories: List<HomeCategory>,
filterableCategoriesModel: FilterableCategoriesModel,
Expand Down Expand Up @@ -618,10 +667,6 @@ private fun HomeContentGrid(
}
}

if (isRefreshing) {
// TODO show a progress indicator or similar
}

if (showHomeCategoryTabs) {
fullWidthItem {
Row {
Expand Down Expand Up @@ -868,7 +913,6 @@ private fun PreviewHomeContent() {
val homeState = HomeState(
windowSizeClass = CompactWindowSizeClass,
featuredPodcasts = PreviewPodcasts.toPersistentList(),
isRefreshing = false,
homeCategories = HomeCategory.entries,
selectedHomeCategory = HomeCategory.Discover,
filterableCategoriesModel = FilterableCategoriesModel(
Expand Down Expand Up @@ -905,7 +949,6 @@ private fun PreviewHomeContentExpanded() {
val homeState = HomeState(
windowSizeClass = CompactWindowSizeClass,
featuredPodcasts = PreviewPodcasts.toPersistentList(),
isRefreshing = false,
homeCategories = HomeCategory.entries,
selectedHomeCategory = HomeCategory.Discover,
filterableCategoriesModel = FilterableCategoriesModel(
Expand Down
Loading