Skip to content

Commit

Permalink
[Jetcaster] Implement 'Your Library' (#1279)
Browse files Browse the repository at this point in the history
  • Loading branch information
arriolac authored Mar 5, 2024
2 parents b14eb5b + f674d9b commit fc22eb5
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 74 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class EpisodeStore(
fun episodesInPodcast(
podcastUri: String,
limit: Int = Integer.MAX_VALUE
): Flow<List<Episode>> {
): Flow<List<EpisodeToPodcast>> {
return episodesDao.episodesForPodcastUri(podcastUri, limit)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ abstract class EpisodesDao : BaseDao<Episode> {
abstract fun episodesForPodcastUri(
podcastUri: String,
limit: Int
): Flow<List<Episode>>
): Flow<List<EpisodeToPodcast>>

@Transaction
@Query(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,12 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import coil.compose.AsyncImage
import com.example.jetcaster.R
import com.example.jetcaster.data.Category
import com.example.jetcaster.data.EpisodeToPodcast
import com.example.jetcaster.data.PodcastWithExtraInfo
import com.example.jetcaster.ui.home.category.PodcastCategoryViewState
import com.example.jetcaster.ui.home.discover.DiscoverViewState
import com.example.jetcaster.ui.home.discover.discoverItems
import com.example.jetcaster.ui.home.library.libraryItems
import com.example.jetcaster.ui.theme.JetcasterTheme
import com.example.jetcaster.ui.theme.Keyline1
import com.example.jetcaster.ui.theme.MinContrastOfPrimaryVsSurface
Expand Down Expand Up @@ -108,6 +110,7 @@ fun Home(
selectedHomeCategory = viewState.selectedHomeCategory,
discoverViewState = viewState.discoverViewState,
podcastCategoryViewState = viewState.podcastCategoryViewState,
libraryEpisodes = viewState.libraryEpisodes,
onHomeCategorySelected = viewModel::onHomeCategorySelected,
onCategorySelected = viewModel::onCategorySelected,
onPodcastUnfollowed = viewModel::onPodcastUnfollowed,
Expand Down Expand Up @@ -173,6 +176,7 @@ fun Home(
homeCategories: List<HomeCategory>,
discoverViewState: DiscoverViewState,
podcastCategoryViewState: PodcastCategoryViewState,
libraryEpisodes: List<EpisodeToPodcast>,
modifier: Modifier = Modifier,
onPodcastUnfollowed: (String) -> Unit,
onHomeCategorySelected: (HomeCategory) -> Unit,
Expand Down Expand Up @@ -239,6 +243,7 @@ fun Home(
homeCategories = homeCategories,
discoverViewState = discoverViewState,
podcastCategoryViewState = podcastCategoryViewState,
libraryEpisodes = libraryEpisodes,
scrimColor = scrimColor,
pagerState = pagerState,
onPodcastUnfollowed = onPodcastUnfollowed,
Expand All @@ -260,6 +265,7 @@ private fun HomeContent(
homeCategories: List<HomeCategory>,
discoverViewState: DiscoverViewState,
podcastCategoryViewState: PodcastCategoryViewState,
libraryEpisodes: List<EpisodeToPodcast>,
scrimColor: Color,
pagerState: PagerState,
modifier: Modifier = Modifier,
Expand Down Expand Up @@ -303,7 +309,10 @@ private fun HomeContent(

when (selectedHomeCategory) {
HomeCategory.Library -> {
// TODO
libraryItems(
episodes = libraryEpisodes,
navigateToPlayer = navigateToPlayer
)
}

HomeCategory.Discover -> {
Expand Down Expand Up @@ -504,6 +513,7 @@ fun PreviewHomeContent() {
topPodcasts = PreviewPodcastsWithExtraInfo,
episodes = PreviewEpisodeToPodcasts,
),
libraryEpisodes = emptyList(),
onCategorySelected = {},
onPodcastUnfollowed = {},
navigateToPlayer = {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import androidx.lifecycle.viewModelScope
import com.example.jetcaster.Graph
import com.example.jetcaster.data.Category
import com.example.jetcaster.data.CategoryStore
import com.example.jetcaster.data.EpisodeStore
import com.example.jetcaster.data.EpisodeToPodcast
import com.example.jetcaster.data.PodcastStore
import com.example.jetcaster.data.PodcastWithExtraInfo
import com.example.jetcaster.data.PodcastsRepository
Expand All @@ -43,7 +45,8 @@ import kotlinx.coroutines.launch
class HomeViewModel(
private val podcastsRepository: PodcastsRepository = Graph.podcastRepository,
private val categoryStore: CategoryStore = Graph.categoryStore,
private val podcastStore: PodcastStore = Graph.podcastStore
private val podcastStore: PodcastStore = Graph.podcastStore,
private val episodeStore: EpisodeStore = Graph.episodeStore
) : ViewModel() {
// Holds our currently selected home category
private val selectedHomeCategory = MutableStateFlow(HomeCategory.Discover)
Expand All @@ -56,6 +59,18 @@ class HomeViewModel(
// Holds the view state if the UI is refreshing for new data
private val refreshing = MutableStateFlow(false)

@OptIn(ExperimentalCoroutinesApi::class)
private val libraryEpisodes = podcastStore.followedPodcastsSortedByLastEpisode()
.flatMapLatest { followedPodcasts ->
combine(
followedPodcasts.map { p ->
episodeStore.episodesInPodcast(p.podcast.uri, 5)
}
) { allEpisodes ->
allEpisodes.toList().flatten().sortedByDescending { it.episode.published }
}
}

private val discover = combine(
categoryStore.categoriesSortedByPodcastCount()
.onEach { categories ->
Expand Down Expand Up @@ -110,20 +125,23 @@ class HomeViewModel(
podcastStore.followedPodcastsSortedByLastEpisode(limit = 20),
refreshing,
discover,
podcastCategory
podcastCategory,
libraryEpisodes
) { homeCategories,
selectedHomeCategory,
podcasts,
refreshing,
discoverViewState,
podcastCategoryViewState ->
podcastCategoryViewState,
libraryEpisodes ->
HomeViewState(
homeCategories = homeCategories,
selectedHomeCategory = selectedHomeCategory,
featuredPodcasts = podcasts.toPersistentList(),
refreshing = refreshing,
discoverViewState = discoverViewState,
podcastCategoryViewState = podcastCategoryViewState,
libraryEpisodes = libraryEpisodes,
errorMessage = null, /* TODO */
)
}.catch { throwable ->
Expand Down Expand Up @@ -181,5 +199,6 @@ data class HomeViewState(
val homeCategories: List<HomeCategory> = emptyList(),
val discoverViewState: DiscoverViewState = DiscoverViewState(),
val podcastCategoryViewState: PodcastCategoryViewState = PodcastCategoryViewState(),
val libraryEpisodes: List<EpisodeToPodcast> = emptyList(),
val errorMessage: String? = null
)
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ import com.example.jetcaster.util.ToggleFollowPodcastIconButton
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle

data class PodcastCategoryViewState(
val topPodcasts: List<PodcastWithExtraInfo> = emptyList(),
val episodes: List<EpisodeToPodcast> = emptyList()
)
fun LazyListScope.podcastCategory(
topPodcasts: List<PodcastWithExtraInfo>,
episodes: List<EpisodeToPodcast>,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.example.jetcaster.ui.home.library

import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.items
import androidx.compose.ui.Modifier
import com.example.jetcaster.data.EpisodeToPodcast
import com.example.jetcaster.ui.home.category.EpisodeListItem

fun LazyListScope.libraryItems(
episodes: List<EpisodeToPodcast>,
navigateToPlayer: (String) -> Unit
) {
if (episodes.isEmpty()) {
// TODO: Empty state
return
}

items(episodes, key = { it.episode.uri }) { item ->
EpisodeListItem(
episode = item.episode,
podcast = item.podcast,
onClick = navigateToPlayer,
modifier = Modifier.fillParentMaxWidth()
)
}
}
43 changes: 43 additions & 0 deletions Jetcaster/app/src/main/java/com/example/jetcaster/util/Flows.kt
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,46 @@ fun <T1, T2, T3, T4, T5, T6, R> combine(
args[5] as T6,
)
}

/**
* Combines seven flows into a single flow by combining their latest values using the provided transform function.
*
* @param flow The first flow.
* @param flow2 The second flow.
* @param flow3 The third flow.
* @param flow4 The fourth flow.
* @param flow5 The fifth flow.
* @param flow6 The sixth flow.
* @param flow7 The seventh flow.
* @param transform The transform function to combine the latest values of the seven flows.
* @return A flow that emits the results of the transform function applied to the latest values of the seven flows.
*/
fun <T1, T2, T3, T4, T5, T6, T7, R> combine(
flow: Flow<T1>,
flow2: Flow<T2>,
flow3: Flow<T3>,
flow4: Flow<T4>,
flow5: Flow<T5>,
flow6: Flow<T6>,
flow7: Flow<T7>,
transform: suspend (T1, T2, T3, T4, T5, T6, T7) -> R
): Flow<R> =
kotlinx.coroutines.flow.combine(
flow,
flow2,
flow3,
flow4,
flow5,
flow6,
flow7
) { args: Array<*> ->
transform(
args[0] as T1,
args[1] as T2,
args[2] as T3,
args[3] as T4,
args[4] as T5,
args[5] as T6,
args[6] as T7,
)
}

0 comments on commit fc22eb5

Please sign in to comment.