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
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ kotlin {
implementation(projects.feature.requestMedia)
implementation(projects.feature.requests)
implementation(projects.feature.search)
implementation(projects.feature.season)
implementation(projects.feature.settings)
implementation(projects.feature.tmdbAuth)
implementation(projects.feature.userData)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import com.divinelink.feature.onboarding.navigation.modalOnboarding
import com.divinelink.feature.profile.navigation.profileScreen
import com.divinelink.feature.requests.ui.navigation.requestsScreen
import com.divinelink.feature.search.navigation.searchScreen
import com.divinelink.feature.season.ui.navigation.seasonScreen
import com.divinelink.feature.settings.navigation.about.aboutSettingsScreen
import com.divinelink.feature.settings.navigation.account.accountSettingsScreen
import com.divinelink.feature.settings.navigation.account.jellyseerrSettingsScreen
Expand Down Expand Up @@ -288,6 +289,14 @@ val navigationModule = module {
}
}

single<NavGraphExtension>(named<Navigation.SeasonRoute>()) {
{ navController, _ ->
seasonScreen(
onNavigate = navController::findNavigation,
)
}
}

single<List<NavGraphExtension>> {
getAll<NavGraphExtension>()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import com.divinelink.feature.profile.ProfileViewModel
import com.divinelink.feature.request.media.RequestMediaViewModel
import com.divinelink.feature.requests.RequestsViewModel
import com.divinelink.feature.search.ui.SearchViewModel
import com.divinelink.feature.season.SeasonViewModel
import com.divinelink.feature.settings.app.account.AccountSettingsViewModel
import com.divinelink.feature.settings.app.account.jellyseerr.JellyseerrSettingsViewModel
import com.divinelink.feature.settings.app.appearance.AppearanceSettingsViewModel
Expand Down Expand Up @@ -55,6 +56,7 @@ val appViewModelModule = module {
viewModelOf(::DiscoverViewModel)
viewModelOf(::SelectFilterViewModel)
viewModelOf(::MediaListsViewModel)
viewModelOf(::SeasonViewModel)

// Components
viewModelOf(::SwitchViewButtonViewModel)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import com.divinelink.core.navigation.route.navigateToPoster
import com.divinelink.core.navigation.route.navigateToRequests
import com.divinelink.core.navigation.route.navigateToSearchFromHome
import com.divinelink.core.navigation.route.navigateToSearchFromTab
import com.divinelink.core.navigation.route.navigateToSeason
import com.divinelink.core.navigation.route.navigateToTMDBAuth
import com.divinelink.core.navigation.route.navigateToUserData
import com.divinelink.core.navigation.route.navigateToWebView
Expand Down Expand Up @@ -62,6 +63,7 @@ fun NavController.findNavigation(route: Navigation) {
Navigation.DiscoverRoute -> navigateToDiscover()
is Navigation.MediaPosterRoute -> navigateToPoster(route)
is Navigation.MediaListsRoute -> navigateToMediaLists(route)
is Navigation.SeasonRoute -> navigateToSeason(route)

// This is from top level navigation
Navigation.HomeRoute -> Unit
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.divinelink.scenepeek.feature.season

import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.PreviewParameter
import com.divinelink.core.ui.Previews
import com.divinelink.feature.season.SeasonUiState
import com.divinelink.feature.season.ui.SeasonContentPreview
import com.divinelink.feature.season.ui.provider.SeasonUiStateParameterProvider

@Previews
@Composable
fun SeasonContentScreenshots(
@PreviewParameter(SeasonUiStateParameterProvider::class) uiState: SeasonUiState,
) {
SeasonContentPreview(uiState)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package com.divinelink.core.data.media
import app.cash.turbine.test
import com.divinelink.core.data.media.repository.MediaRepository
import com.divinelink.core.data.media.repository.ProdMediaRepository
import com.divinelink.core.database.person.ProdPersonDao
import com.divinelink.core.fixtures.core.commons.ClockFactory
import com.divinelink.core.fixtures.model.GenreFactory
import com.divinelink.core.fixtures.model.media.MediaItemFactory
import com.divinelink.core.fixtures.model.media.MediaItemFactory.toWizard
Expand All @@ -13,6 +15,7 @@ import com.divinelink.core.network.Resource
import com.divinelink.core.network.media.model.GenresListResponse
import com.divinelink.core.testing.MainDispatcherRule
import com.divinelink.core.testing.dao.TestMediaDao
import com.divinelink.core.testing.database.TestDatabaseFactory
import com.divinelink.core.testing.factories.api.media.GenreResponseFactory
import com.divinelink.core.testing.service.TestMediaService
import io.kotest.matchers.shouldBe
Expand All @@ -28,19 +31,25 @@ class ProdMediaRepositoryTest {
withFavorite(true)
}

private var mediaDao = TestMediaDao()
private var mediaService = TestMediaService()

@get:Rule
val mainDispatcherRule = MainDispatcherRule()
private val testDispatcher = mainDispatcherRule.testDispatcher

private var mediaDao = TestMediaDao()
private var personDao = ProdPersonDao(
clock = ClockFactory.augustFifteenth2021(),
database = TestDatabaseFactory.createInMemoryDatabase(),
dispatcher = testDispatcher,
)
private var mediaService = TestMediaService()

private lateinit var repository: MediaRepository

@Before
fun setUp() {
repository = ProdMediaRepository(
dao = mediaDao.mock,
personDao = personDao,
remote = mediaService.mock,
dispatcher = testDispatcher,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.divinelink.core.data.media.repository
import com.divinelink.core.model.Genre
import com.divinelink.core.model.PaginationData
import com.divinelink.core.model.details.Season
import com.divinelink.core.model.details.SeasonDetails
import com.divinelink.core.model.discover.DiscoverFilter
import com.divinelink.core.model.home.MediaListRequest
import com.divinelink.core.model.media.MediaItem
Expand Down Expand Up @@ -68,6 +69,11 @@ interface MediaRepository {

fun fetchTvSeasons(id: Int): Flow<Result<List<Season>>>

fun fetchSeason(
showId: Int,
seasonNumber: Int,
): Flow<Result<Season>>

/**
* Add favorite [media] to local storage.
*/
Expand All @@ -87,4 +93,9 @@ interface MediaRepository {
): Result<Boolean>

suspend fun fetchGenres(mediaType: MediaType): Flow<Resource<List<Genre>>>

fun fetchSeasonDetails(
showId: Int,
season: Int,
): Flow<Resource<SeasonDetails?>>
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ package com.divinelink.core.data.media.repository
import com.divinelink.core.commons.data
import com.divinelink.core.commons.domain.DispatcherProvider
import com.divinelink.core.database.media.dao.MediaDao
import com.divinelink.core.database.media.mapper.map
import com.divinelink.core.database.person.PersonDao
import com.divinelink.core.model.Genre
import com.divinelink.core.model.PaginationData
import com.divinelink.core.model.details.Season
import com.divinelink.core.model.details.SeasonDetails
import com.divinelink.core.model.discover.DiscoverFilter
import com.divinelink.core.model.home.MediaListRequest
import com.divinelink.core.model.media.MediaItem
Expand All @@ -14,6 +17,7 @@ import com.divinelink.core.model.search.MultiSearch
import com.divinelink.core.model.sort.SortOption
import com.divinelink.core.model.user.data.UserDataResponse
import com.divinelink.core.network.Resource
import com.divinelink.core.network.media.mapper.details.map
import com.divinelink.core.network.media.mapper.map
import com.divinelink.core.network.media.model.GenresListResponse
import com.divinelink.core.network.media.model.movie.map
Expand All @@ -33,6 +37,7 @@ import kotlinx.coroutines.flow.map
class ProdMediaRepository(
private val remote: MediaService,
private val dao: MediaDao,
private val personDao: PersonDao,
private val dispatcher: DispatcherProvider,
) : MediaRepository {

Expand Down Expand Up @@ -273,6 +278,15 @@ class ProdMediaRepository(
Result.success(it)
}

override fun fetchSeason(
showId: Int,
seasonNumber: Int,
): Flow<Result<Season>> = dao
.fetchSeason(
showId = showId,
seasonNumber = seasonNumber,
).map { Result.success(it) }

override suspend fun fetchGenres(mediaType: MediaType): Flow<Resource<List<Genre>>> =
networkBoundResource(
query = { dao.fetchGenres(mediaType) },
Expand All @@ -282,4 +296,49 @@ class ProdMediaRepository(
},
shouldFetch = { it.isEmpty() },
)

override fun fetchSeasonDetails(
showId: Int,
season: Int,
): Flow<Resource<SeasonDetails?>> = networkBoundResource(
query = {
combine(
dao.fetchEpisodes(showId = showId, season = season),
dao.fetchSeasonDetails(showId = showId, season = season),
personDao.fetchGuestStars(showId = showId, season = season),
) { episodes, details, guestStars ->
details?.map(
episodes = episodes,
guestStars = guestStars,
)
}
},
fetch = {
remote.fetchSeason(
showId = showId,
season = season,
).map { it.map() }
},
saveFetchResult = { result ->
val data = result.data

dao.insertSeasonDetails(
seasonDetails = data,
showId = showId,
seasonNumber = season,
)

dao.insertEpisodes(data.episodes)

data.episodes.forEach { episode ->
personDao.insertGuestStars(
season = season,
showId = showId,
episode = episode.number,
guestStars = data.guestStars,
)
}
},
shouldFetch = { true },
)
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
@file:Suppress("TooManyFunctions")

package com.divinelink.core.database.media.dao

import com.divinelink.core.database.MediaItemEntity
import com.divinelink.core.database.season.SeasonDetailsEntity
import com.divinelink.core.model.Genre
import com.divinelink.core.model.details.Episode
import com.divinelink.core.model.details.Season
import com.divinelink.core.model.details.SeasonDetails
import com.divinelink.core.model.jellyseerr.media.SeasonRequest
import com.divinelink.core.model.media.MediaItem
import com.divinelink.core.model.media.MediaReference
Expand Down Expand Up @@ -41,6 +46,11 @@ interface MediaDao {

fun fetchSeasons(id: Int): Flow<List<Season>>

fun fetchSeason(
showId: Int,
seasonNumber: Int,
): Flow<Season>

fun addToFavorites(
mediaId: Int,
mediaType: MediaType,
Expand All @@ -62,4 +72,28 @@ interface MediaDao {
mediaType: MediaType,
genres: List<Genre>,
)

fun insertEpisodes(episodes: List<Episode>)

fun fetchEpisode(
showId: Int,
episodeNumber: Int,
seasonNumber: Int,
): Episode

fun fetchEpisodes(
showId: Int,
season: Int,
): Flow<List<Episode>>

fun insertSeasonDetails(
seasonDetails: SeasonDetails,
showId: Int,
seasonNumber: Int,
)

fun fetchSeasonDetails(
season: Int,
showId: Int,
): Flow<SeasonDetailsEntity?>
}
Loading