Skip to content

Commit

Permalink
[Jetcaster] Remove HomeState and reduces lambdas into HomeAction (#1500)
Browse files Browse the repository at this point in the history
[Merge after #1499]
- it served only as a wrapper for parameters without any additional
logic
- it was unstable and creating new instance each composition, therefore
no skipping
- Reduces amount of passing and potentially making mistake
  • Loading branch information
mlykotom authored Nov 21, 2024
2 parents 2a30765 + 3868e6a commit 60708b1
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 111 deletions.
165 changes: 60 additions & 105 deletions Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/home/Home.kt
Original file line number Diff line number Diff line change
Expand Up @@ -106,13 +106,11 @@ import com.example.jetcaster.R
import com.example.jetcaster.core.domain.testing.PreviewCategories
import com.example.jetcaster.core.domain.testing.PreviewPodcastEpisodes
import com.example.jetcaster.core.domain.testing.PreviewPodcasts
import com.example.jetcaster.core.model.CategoryInfo
import com.example.jetcaster.core.model.EpisodeInfo
import com.example.jetcaster.core.model.FilterableCategoriesModel
import com.example.jetcaster.core.model.LibraryInfo
import com.example.jetcaster.core.model.PodcastCategoryFilterResult
import com.example.jetcaster.core.model.PodcastInfo
import com.example.jetcaster.core.player.model.PlayerEpisode
import com.example.jetcaster.designsystem.component.PodcastImage
import com.example.jetcaster.ui.home.discover.discoverItems
import com.example.jetcaster.ui.home.library.libraryItems
Expand All @@ -132,29 +130,6 @@ import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.toPersistentList
import kotlinx.coroutines.launch

data class HomeState(
val windowSizeClass: WindowSizeClass,
val isLoading: Boolean,
val featuredPodcasts: PersistentList<PodcastInfo>,
val selectedHomeCategory: HomeCategory,
val homeCategories: List<HomeCategory>,
val filterableCategoriesModel: FilterableCategoriesModel,
val podcastCategoryFilterResult: PodcastCategoryFilterResult,
val library: LibraryInfo,
val modifier: Modifier = Modifier,
val onPodcastUnfollowed: (PodcastInfo) -> Unit,
val onHomeCategorySelected: (HomeCategory) -> Unit,
val onCategorySelected: (CategoryInfo) -> Unit,
val navigateToPodcastDetails: (PodcastInfo) -> Unit,
val navigateToPlayer: (EpisodeInfo) -> Unit,
val onTogglePodcastFollowed: (PodcastInfo) -> Unit,
val onLibraryPodcastSelected: (PodcastInfo?) -> Unit,
val onQueueEpisode: (PlayerEpisode) -> Unit,
)

private val HomeState.showHomeCategoryTabs: Boolean
get() = featuredPodcasts.isNotEmpty() && homeCategories.isNotEmpty()

@OptIn(ExperimentalMaterial3AdaptiveApi::class)
private fun <T> ThreePaneScaffoldNavigator<T>.isMainPaneHidden(): Boolean {
return scaffoldValue[SupportingPaneScaffoldRole.Main] == PaneAdaptedValue.Hidden
Expand Down Expand Up @@ -293,34 +268,25 @@ private fun HomeScreenReady(
navigator.navigateBack()
}

val homeState = HomeState(
windowSizeClass = windowSizeClass,
isLoading = uiState.isLoading,
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,
navigateToPodcastDetails = {
navigator.navigateTo(SupportingPaneScaffoldRole.Supporting, it.uri)
},
navigateToPlayer = navigateToPlayer,
onTogglePodcastFollowed = viewModel::onTogglePodcastFollowed,
onLibraryPodcastSelected = viewModel::onLibraryPodcastSelected,
onQueueEpisode = viewModel::onQueueEpisode
)

Surface {
SupportingPaneScaffold(
value = navigator.scaffoldValue,
directive = navigator.scaffoldDirective,
mainPane = {
HomeScreen(
homeState = homeState,
windowSizeClass = windowSizeClass,
isLoading = uiState.isLoading,
featuredPodcasts = uiState.featuredPodcasts,
homeCategories = uiState.homeCategories,
selectedHomeCategory = uiState.selectedHomeCategory,
filterableCategoriesModel = uiState.filterableCategoriesModel,
podcastCategoryFilterResult = uiState.podcastCategoryFilterResult,
library = uiState.library,
onHomeAction = viewModel::onHomeAction,
navigateToPodcastDetails = {
navigator.navigateTo(SupportingPaneScaffoldRole.Supporting, it.uri)
},
navigateToPlayer = navigateToPlayer,
modifier = Modifier.fillMaxSize()
)
},
Expand Down Expand Up @@ -420,13 +386,23 @@ private fun HomeScreenBackground(

@Composable
private fun HomeScreen(
homeState: HomeState,
windowSizeClass: WindowSizeClass,
isLoading: Boolean,
featuredPodcasts: PersistentList<PodcastInfo>,
selectedHomeCategory: HomeCategory,
homeCategories: List<HomeCategory>,
filterableCategoriesModel: FilterableCategoriesModel,
podcastCategoryFilterResult: PodcastCategoryFilterResult,
library: LibraryInfo,
onHomeAction: (HomeAction) -> Unit,
navigateToPodcastDetails: (PodcastInfo) -> Unit,
navigateToPlayer: (EpisodeInfo) -> Unit,
modifier: Modifier = Modifier
) {
// Effect that changes the home category selection when there are no subscribed podcasts
LaunchedEffect(key1 = homeState.featuredPodcasts) {
if (homeState.featuredPodcasts.isEmpty()) {
homeState.onHomeCategorySelected(HomeCategory.Discover)
LaunchedEffect(key1 = featuredPodcasts) {
if (featuredPodcasts.isEmpty()) {
onHomeAction(HomeAction.HomeCategorySelected(HomeCategory.Discover))
}
}

Expand All @@ -439,10 +415,10 @@ private fun HomeScreen(
topBar = {
Column {
HomeAppBar(
isExpanded = homeState.windowSizeClass.isCompact,
isExpanded = windowSizeClass.isCompact,
modifier = Modifier.fillMaxWidth(),
)
if (homeState.isLoading) {
if (isLoading) {
LinearProgressIndicator(
Modifier
.fillMaxWidth()
Expand All @@ -458,28 +434,26 @@ private fun HomeScreen(
) { contentPadding ->
// Main Content
val snackBarText = stringResource(id = R.string.episode_added_to_your_queue)
val showHomeCategoryTabs = featuredPodcasts.isNotEmpty() && homeCategories.isNotEmpty()
HomeContent(
showHomeCategoryTabs = homeState.showHomeCategoryTabs,
featuredPodcasts = homeState.featuredPodcasts,
selectedHomeCategory = homeState.selectedHomeCategory,
homeCategories = homeState.homeCategories,
filterableCategoriesModel = homeState.filterableCategoriesModel,
podcastCategoryFilterResult = homeState.podcastCategoryFilterResult,
library = homeState.library,
showHomeCategoryTabs = showHomeCategoryTabs,
featuredPodcasts = featuredPodcasts,
selectedHomeCategory = selectedHomeCategory,
homeCategories = homeCategories,
filterableCategoriesModel = filterableCategoriesModel,
podcastCategoryFilterResult = podcastCategoryFilterResult,
library = library,
modifier = Modifier.padding(contentPadding),
onPodcastUnfollowed = homeState.onPodcastUnfollowed,
onHomeCategorySelected = homeState.onHomeCategorySelected,
onCategorySelected = homeState.onCategorySelected,
navigateToPodcastDetails = homeState.navigateToPodcastDetails,
navigateToPlayer = homeState.navigateToPlayer,
onTogglePodcastFollowed = homeState.onTogglePodcastFollowed,
onLibraryPodcastSelected = homeState.onLibraryPodcastSelected,
onQueueEpisode = {
coroutineScope.launch {
snackbarHostState.showSnackbar(snackBarText)
onHomeAction = { action ->
if (action is HomeAction.QueueEpisode) {
coroutineScope.launch {
snackbarHostState.showSnackbar(snackBarText)
}
}
homeState.onQueueEpisode(it)
}
onHomeAction(action)
},
navigateToPodcastDetails = navigateToPodcastDetails,
navigateToPlayer = navigateToPlayer,
)
}
}
Expand All @@ -495,21 +469,16 @@ private fun HomeContent(
podcastCategoryFilterResult: PodcastCategoryFilterResult,
library: LibraryInfo,
modifier: Modifier = Modifier,
onPodcastUnfollowed: (PodcastInfo) -> Unit,
onHomeCategorySelected: (HomeCategory) -> Unit,
onCategorySelected: (CategoryInfo) -> Unit,
onHomeAction: (HomeAction) -> Unit,
navigateToPodcastDetails: (PodcastInfo) -> Unit,
navigateToPlayer: (EpisodeInfo) -> Unit,
onTogglePodcastFollowed: (PodcastInfo) -> Unit,
onLibraryPodcastSelected: (PodcastInfo?) -> Unit,
onQueueEpisode: (PlayerEpisode) -> Unit,
) {
val pagerState = rememberPagerState { featuredPodcasts.size }
LaunchedEffect(pagerState, featuredPodcasts) {
snapshotFlow { pagerState.currentPage }
.collect {
val podcast = featuredPodcasts.getOrNull(it)
onLibraryPodcastSelected(podcast)
onHomeAction(HomeAction.LibraryPodcastSelected(podcast))
}
}

Expand All @@ -523,13 +492,9 @@ private fun HomeContent(
podcastCategoryFilterResult = podcastCategoryFilterResult,
library = library,
modifier = modifier,
onPodcastUnfollowed = onPodcastUnfollowed,
onHomeCategorySelected = onHomeCategorySelected,
onCategorySelected = onCategorySelected,
onHomeAction = onHomeAction,
navigateToPodcastDetails = navigateToPodcastDetails,
navigateToPlayer = navigateToPlayer,
onTogglePodcastFollowed = onTogglePodcastFollowed,
onQueueEpisode = onQueueEpisode,
)
}

Expand All @@ -544,13 +509,9 @@ private fun HomeContentGrid(
podcastCategoryFilterResult: PodcastCategoryFilterResult,
library: LibraryInfo,
modifier: Modifier = Modifier,
onHomeCategorySelected: (HomeCategory) -> Unit,
onPodcastUnfollowed: (PodcastInfo) -> Unit,
onCategorySelected: (CategoryInfo) -> Unit,
onHomeAction: (HomeAction) -> Unit,
navigateToPodcastDetails: (PodcastInfo) -> Unit,
navigateToPlayer: (EpisodeInfo) -> Unit,
onTogglePodcastFollowed: (PodcastInfo) -> Unit,
onQueueEpisode: (PlayerEpisode) -> Unit,
) {
LazyVerticalGrid(
columns = GridCells.Adaptive(362.dp),
Expand All @@ -561,7 +522,7 @@ private fun HomeContentGrid(
FollowedPodcastItem(
pagerState = pagerState,
items = featuredPodcasts,
onPodcastUnfollowed = onPodcastUnfollowed,
onPodcastUnfollowed = { onHomeAction(HomeAction.PodcastUnfollowed(it)) },
navigateToPodcastDetails = navigateToPodcastDetails,
modifier = Modifier
.fillMaxWidth()
Expand All @@ -576,7 +537,7 @@ private fun HomeContentGrid(
categories = homeCategories,
selectedCategory = selectedHomeCategory,
showHorizontalLine = false,
onCategorySelected = onHomeCategorySelected,
onCategorySelected = { onHomeAction(HomeAction.HomeCategorySelected(it)) },
modifier = Modifier.width(240.dp)
)
}
Expand All @@ -588,7 +549,7 @@ private fun HomeContentGrid(
libraryItems(
library = library,
navigateToPlayer = navigateToPlayer,
onQueueEpisode = onQueueEpisode
onQueueEpisode = { onHomeAction(HomeAction.QueueEpisode(it)) }
)
}

Expand All @@ -598,9 +559,11 @@ private fun HomeContentGrid(
podcastCategoryFilterResult = podcastCategoryFilterResult,
navigateToPodcastDetails = navigateToPodcastDetails,
navigateToPlayer = navigateToPlayer,
onCategorySelected = onCategorySelected,
onTogglePodcastFollowed = onTogglePodcastFollowed,
onQueueEpisode = onQueueEpisode
onCategorySelected = { onHomeAction(HomeAction.CategorySelected(it)) },
onTogglePodcastFollowed = {
onHomeAction(HomeAction.TogglePodcastFollowed(it))
},
onQueueEpisode = { onHomeAction(HomeAction.QueueEpisode(it)) },
)
}
}
Expand Down Expand Up @@ -812,7 +775,7 @@ private val CompactWindowSizeClass = WindowSizeClass.compute(360f, 780f)
@Composable
private fun PreviewHome() {
JetcasterTheme {
val homeState = HomeState(
HomeScreen(
windowSizeClass = CompactWindowSizeClass,
isLoading = true,
featuredPodcasts = PreviewPodcasts.toPersistentList(),
Expand All @@ -827,17 +790,9 @@ private fun PreviewHome() {
episodes = PreviewPodcastEpisodes
),
library = LibraryInfo(),
onCategorySelected = {},
onPodcastUnfollowed = {},
onHomeAction = {},
navigateToPodcastDetails = {},
navigateToPlayer = {},
onHomeCategorySelected = {},
onTogglePodcastFollowed = {},
onLibraryPodcastSelected = {},
onQueueEpisode = {}
)
HomeScreen(
homeState = homeState,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,31 +154,42 @@ class HomeViewModel @Inject constructor(
}
}

fun onCategorySelected(category: CategoryInfo) {
fun onHomeAction(action: HomeAction) {
when (action) {
is HomeAction.CategorySelected -> onCategorySelected(action.category)
is HomeAction.HomeCategorySelected -> onHomeCategorySelected(action.category)
is HomeAction.LibraryPodcastSelected -> onLibraryPodcastSelected(action.podcast)
is HomeAction.PodcastUnfollowed -> onPodcastUnfollowed(action.podcast)
is HomeAction.QueueEpisode -> onQueueEpisode(action.episode)
is HomeAction.TogglePodcastFollowed -> onTogglePodcastFollowed(action.podcast)
}
}

private fun onCategorySelected(category: CategoryInfo) {
_selectedCategory.value = category
}

fun onHomeCategorySelected(category: HomeCategory) {
private fun onHomeCategorySelected(category: HomeCategory) {
selectedHomeCategory.value = category
}

fun onPodcastUnfollowed(podcast: PodcastInfo) {
private fun onPodcastUnfollowed(podcast: PodcastInfo) {
viewModelScope.launch {
podcastStore.unfollowPodcast(podcast.uri)
}
}

fun onTogglePodcastFollowed(podcast: PodcastInfo) {
private fun onTogglePodcastFollowed(podcast: PodcastInfo) {
viewModelScope.launch {
podcastStore.togglePodcastFollowed(podcast.uri)
}
}

fun onLibraryPodcastSelected(podcast: PodcastInfo?) {
private fun onLibraryPodcastSelected(podcast: PodcastInfo?) {
selectedLibraryPodcast.value = podcast
}

fun onQueueEpisode(episode: PlayerEpisode) {
private fun onQueueEpisode(episode: PlayerEpisode) {
episodePlayer.addToQueue(episode)
}
}
Expand All @@ -192,6 +203,16 @@ enum class HomeCategory {
Library, Discover
}

@Immutable
sealed interface HomeAction {
data class CategorySelected(val category: CategoryInfo) : HomeAction
data class HomeCategorySelected(val category: HomeCategory) : HomeAction
data class PodcastUnfollowed(val podcast: PodcastInfo) : HomeAction
data class TogglePodcastFollowed(val podcast: PodcastInfo) : HomeAction
data class LibraryPodcastSelected(val podcast: PodcastInfo?) : HomeAction
data class QueueEpisode(val episode: PlayerEpisode) : HomeAction
}

@Immutable
data class HomeScreenUiState(
val isLoading: Boolean = true,
Expand Down

0 comments on commit 60708b1

Please sign in to comment.