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

[Jetcaster] Refactor home state and fix loading progress #1499

Merged
merged 4 commits into from
Nov 18, 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 @@ -49,8 +49,7 @@ class PodcastsRepository @Inject constructor(
if (refreshingJob?.isActive == true) {
refreshingJob?.join()
} else if (force || podcastStore.isEmpty()) {

refreshingJob = scope.launch {
val job = scope.launch {
// Now fetch the podcasts, and add each to each store
podcastsFetcher(SampleFeeds)
.filter { it is PodcastRssResponse.Success }
Expand All @@ -72,6 +71,9 @@ class PodcastsRepository @Inject constructor(
}
}
}
refreshingJob = job
// We need to wait here for the job to finish, otherwise the coroutine completes ~immediatelly
job.join()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,10 @@ 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
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SearchBar
Expand Down Expand Up @@ -134,6 +134,7 @@ import kotlinx.coroutines.launch

data class HomeState(
val windowSizeClass: WindowSizeClass,
val isLoading: Boolean,
val featuredPodcasts: PersistentList<PodcastInfo>,
val selectedHomeCategory: HomeCategory,
val homeCategories: List<HomeCategory>,
Expand Down Expand Up @@ -180,10 +181,12 @@ fun calculateScaffoldDirective(
maxHorizontalPartitions = 1
verticalSpacerSize = 0.dp
}

WindowWidthSizeClass.MEDIUM -> {
maxHorizontalPartitions = 1
verticalSpacerSize = 0.dp
}

else -> {
maxHorizontalPartitions = 2
verticalSpacerSize = 24.dp
Expand Down Expand Up @@ -233,27 +236,17 @@ fun MainScreen(
viewModel: HomeViewModel = hiltViewModel()
) {
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,
)
}
}
}
val uiState = homeScreenUiState
Box {
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)
)
if (uiState.errorMessage != null) {
HomeScreenError(onRetry = viewModel::refresh)
}
}
}
Expand Down Expand Up @@ -288,7 +281,7 @@ fun HomeScreenErrorPreview() {
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
private fun HomeScreenReady(
uiState: HomeScreenUiState.Ready,
uiState: HomeScreenUiState,
windowSizeClass: WindowSizeClass,
navigateToPlayer: (EpisodeInfo) -> Unit,
viewModel: HomeViewModel = hiltViewModel()
Expand All @@ -302,6 +295,7 @@ private fun HomeScreenReady(

val homeState = HomeState(
windowSizeClass = windowSizeClass,
isLoading = uiState.isLoading,
featuredPodcasts = uiState.featuredPodcasts,
homeCategories = uiState.homeCategories,
selectedHomeCategory = uiState.selectedHomeCategory,
Expand Down Expand Up @@ -443,10 +437,19 @@ private fun HomeScreen(
) {
Scaffold(
topBar = {
HomeAppBar(
isExpanded = homeState.windowSizeClass.isCompact,
modifier = Modifier.fillMaxWidth(),
)
Column {
HomeAppBar(
isExpanded = homeState.windowSizeClass.isCompact,
modifier = Modifier.fillMaxWidth(),
)
if (homeState.isLoading) {
LinearProgressIndicator(
Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
)
}
}
},
snackbarHost = {
SnackbarHost(hostState = snackbarHostState)
Expand Down Expand Up @@ -811,6 +814,7 @@ private fun PreviewHome() {
JetcasterTheme {
val homeState = HomeState(
windowSizeClass = CompactWindowSizeClass,
isLoading = true,
featuredPodcasts = PreviewPodcasts.toPersistentList(),
homeCategories = HomeCategory.entries,
selectedHomeCategory = HomeCategory.Discover,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

package com.example.jetcaster.ui.home

import android.util.Log
import androidx.compose.runtime.Immutable
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.jetcaster.core.data.database.model.EpisodeToPodcast
Expand Down Expand Up @@ -49,7 +49,6 @@ import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.launch

@OptIn(ExperimentalCoroutinesApi::class)

@HiltViewModel
class HomeViewModel @Inject constructor(
private val podcastsRepository: PodcastsRepository,
Expand All @@ -61,14 +60,19 @@ class HomeViewModel @Inject constructor(
) : ViewModel() {
// Holds our currently selected podcast in the library
private val selectedLibraryPodcast = MutableStateFlow<PodcastInfo?>(null)

// Holds our currently selected home category
private val selectedHomeCategory = MutableStateFlow(HomeCategory.Discover)

// Holds the currently available home categories
private val homeCategories = MutableStateFlow(HomeCategory.entries)

// Holds our currently selected category
private val _selectedCategory = MutableStateFlow<CategoryInfo?>(null)

// Holds our view state which the UI collects via [state]
private val _state = MutableStateFlow<HomeScreenUiState>(HomeScreenUiState.Loading)
private val _state = MutableStateFlow(HomeScreenUiState())

// Holds the view state if the UI is refreshing for new data
private val refreshing = MutableStateFlow(false)

Expand Down Expand Up @@ -107,19 +111,15 @@ class HomeViewModel @Inject constructor(
podcastCategoryFilterResult,
libraryEpisodes ->

if (refreshing) {
Log.d("Jetcaster", "refreshing: $refreshing, podcasts $podcasts")
return@combine HomeScreenUiState.Loading
}

_selectedCategory.value = filterableCategories.selectedCategory

// Override selected home category to show 'DISCOVER' if there are no
// featured podcasts
selectedHomeCategory.value =
if (podcasts.isEmpty()) HomeCategory.Discover else homeCategory

HomeScreenUiState.Ready(
HomeScreenUiState(
isLoading = refreshing,
homeCategories = homeCategories,
selectedHomeCategory = homeCategory,
featuredPodcasts = podcasts.map { it.asExternalModel() }.toPersistentList(),
Expand All @@ -128,7 +128,12 @@ class HomeViewModel @Inject constructor(
library = libraryEpisodes.asLibrary()
)
}.catch { throwable ->
_state.value = HomeScreenUiState.Error(throwable.message)
emit(
HomeScreenUiState(
isLoading = false,
errorMessage = throwable.message
)
)
}.collect {
_state.value = it
}
Expand Down Expand Up @@ -187,21 +192,14 @@ enum class HomeCategory {
Library, Discover
}

sealed interface HomeScreenUiState {
data object Loading : HomeScreenUiState

data class Error(
val errorMessage: String? = null
) : HomeScreenUiState

data class Ready(
val featuredPodcasts: PersistentList<PodcastInfo> = persistentListOf(),
val selectedHomeCategory: HomeCategory = HomeCategory.Discover,
val homeCategories: List<HomeCategory> = emptyList(),
val filterableCategoriesModel: FilterableCategoriesModel =
FilterableCategoriesModel(),
val podcastCategoryFilterResult: PodcastCategoryFilterResult =
PodcastCategoryFilterResult(),
val library: LibraryInfo = LibraryInfo(),
) : HomeScreenUiState
}
@Immutable
data class HomeScreenUiState(
val isLoading: Boolean = true,
val errorMessage: String? = null,
val featuredPodcasts: PersistentList<PodcastInfo> = persistentListOf(),
val selectedHomeCategory: HomeCategory = HomeCategory.Discover,
val homeCategories: List<HomeCategory> = emptyList(),
val filterableCategoriesModel: FilterableCategoriesModel = FilterableCategoriesModel(),
val podcastCategoryFilterResult: PodcastCategoryFilterResult = PodcastCategoryFilterResult(),
val library: LibraryInfo = LibraryInfo(),
)