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
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ fun NavController.findNavigation(route: Navigation) {
is Navigation.WebViewRoute -> navigateToWebView(route)
is Navigation.ActionMenuRoute.Media -> openDefaultActionMenuModal(route)
Navigation.JellyseerrRequestsRoute -> navigateToRequests()
Navigation.DiscoverRoute -> navigateToDiscover()
is Navigation.DiscoverRoute -> navigateToDiscover(route)
is Navigation.MediaPosterRoute -> navigateToPoster(route)
is Navigation.MediaListsRoute -> navigateToMediaLists(route)
is Navigation.SeasonRoute -> navigateToSeason(route)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.divinelink.core.commons.util

import kotlinx.serialization.json.Json

inline fun <reified T> T.encodeToString() = Json.encodeToString<T>(this)
inline fun <reified T> String.decodeFromString() = Json.decodeFromString<T>(this)
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.divinelink.core.data

import com.divinelink.core.model.Genre
import com.divinelink.core.model.details.Keyword
import com.divinelink.core.model.discover.DiscoverFilter
import com.divinelink.core.model.locale.Country
import com.divinelink.core.model.locale.Language
Expand All @@ -10,108 +11,176 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow

class FilterRepository {
private val _selectedGenres = MutableStateFlow<Map<MediaType, List<Genre>>>(
mapOf(
MediaType.MOVIE to emptyList(),
MediaType.TV to emptyList(),
),
)
val selectedGenres: StateFlow<Map<MediaType, List<Genre>>> = _selectedGenres.asStateFlow()
private val filterStates = mutableMapOf<String, FilterState>()

private val _selectedLanguage = MutableStateFlow<Map<MediaType, Language?>>(
mapOf(
MediaType.MOVIE to null,
MediaType.TV to null,
private data class FilterState(
val selectedGenres: MutableStateFlow<Map<MediaType, List<Genre>>> = MutableStateFlow(
mapOf(
MediaType.MOVIE to emptyList(),
MediaType.TV to emptyList(),
),
),
)
val selectedLanguage: StateFlow<Map<MediaType, Language?>> = _selectedLanguage.asStateFlow()

private val _voteAverage = MutableStateFlow<Map<MediaType, DiscoverFilter.VoteAverage?>>(
mapOf(
MediaType.MOVIE to null,
MediaType.TV to null,
val selectedLanguage: MutableStateFlow<Map<MediaType, Language?>> = MutableStateFlow(
mapOf(
MediaType.MOVIE to null,
MediaType.TV to null,
),
),
)
val voteAverage: StateFlow<Map<MediaType, DiscoverFilter.VoteAverage?>> = _voteAverage
.asStateFlow()

private val _selectedCountry = MutableStateFlow<Map<MediaType, Country?>>(
mapOf(
MediaType.MOVIE to null,
MediaType.TV to null,
val voteAverage: MutableStateFlow<Map<MediaType, DiscoverFilter.VoteAverage?>> =
MutableStateFlow(
mapOf(
MediaType.MOVIE to null,
MediaType.TV to null,
),
),
val selectedCountry: MutableStateFlow<Map<MediaType, Country?>> = MutableStateFlow(
mapOf(
MediaType.MOVIE to null,
MediaType.TV to null,
),
),
)
val selectedCountry: StateFlow<Map<MediaType, Country?>> = _selectedCountry.asStateFlow()

private val _minimumVotes = MutableStateFlow<Map<MediaType, Int?>>(
mapOf(
MediaType.MOVIE to null,
MediaType.TV to null,
val minimumVotes: MutableStateFlow<Map<MediaType, Int?>> = MutableStateFlow(
mapOf(
MediaType.MOVIE to null,
MediaType.TV to null,
),
),
)
val minimumVotes: StateFlow<Map<MediaType, Int?>> = _minimumVotes.asStateFlow()

private val _year = MutableStateFlow<Map<MediaType, DiscoverFilter.Year?>>(
mapOf(
MediaType.MOVIE to null,
MediaType.TV to null,
val year: MutableStateFlow<Map<MediaType, DiscoverFilter.Year?>> = MutableStateFlow(
mapOf(
MediaType.MOVIE to null,
MediaType.TV to null,
),
),
val keywords: MutableStateFlow<Map<MediaType, List<Keyword>>> = MutableStateFlow(
mapOf(
MediaType.MOVIE to emptyList(),
MediaType.TV to emptyList(),
),
),
)
val year: StateFlow<Map<MediaType, DiscoverFilter.Year?>> = _year.asStateFlow()

private fun getFilterState(uuid: String): FilterState = filterStates.getOrPut(uuid) {
FilterState()
}

fun clearFilterState(uuid: String) {
filterStates.remove(uuid)
}

fun selectedGenres(uuid: String): StateFlow<Map<MediaType, List<Genre>>> =
getFilterState(uuid).selectedGenres.asStateFlow()

fun selectedLanguage(uuid: String): StateFlow<Map<MediaType, Language?>> =
getFilterState(uuid).selectedLanguage.asStateFlow()

fun voteAverage(uuid: String): StateFlow<Map<MediaType, DiscoverFilter.VoteAverage?>> =
getFilterState(uuid).voteAverage.asStateFlow()

fun selectedCountry(uuid: String): StateFlow<Map<MediaType, Country?>> =
getFilterState(uuid).selectedCountry.asStateFlow()

fun minimumVotes(uuid: String): StateFlow<Map<MediaType, Int?>> =
getFilterState(uuid).minimumVotes.asStateFlow()

fun year(uuid: String): StateFlow<Map<MediaType, DiscoverFilter.Year?>> =
getFilterState(uuid).year.asStateFlow()

fun keywords(uuid: String): StateFlow<Map<MediaType, List<Keyword>>> =
getFilterState(uuid).keywords.asStateFlow()

fun updateSelectedGenres(
uuid: String,
mediaType: MediaType,
genres: List<Genre>,
) {
_selectedGenres.value += mediaType to genres
getFilterState(uuid).selectedGenres.value += mediaType to genres
}

fun updateLanguage(
uuid: String,
mediaType: MediaType,
language: Language?,
) {
_selectedLanguage.value += mediaType to language
getFilterState(uuid).selectedLanguage.value += mediaType to language
}

fun updateCountry(
uuid: String,
mediaType: MediaType,
country: Country?,
) {
_selectedCountry.value += mediaType to country
getFilterState(uuid).selectedCountry.value += mediaType to country
}

fun updateVoteAverage(
uuid: String,
mediaType: MediaType,
voteAverage: DiscoverFilter.VoteAverage?,
) {
_voteAverage.value += mediaType to voteAverage
getFilterState(uuid).voteAverage.value += mediaType to voteAverage
}

fun updateMinimumVotes(
uuid: String,
mediaType: MediaType,
votes: Int,
) {
_minimumVotes.value += mediaType to votes
getFilterState(uuid).minimumVotes.value += mediaType to votes
}

fun updateYear(
uuid: String,
mediaType: MediaType,
year: DiscoverFilter.Year?,
) {
_year.value += mediaType to year
getFilterState(uuid).year.value += mediaType to year
}

fun updateKeyword(
uuid: String,
mediaType: MediaType,
keyword: Keyword,
) {
val filterState = getFilterState(uuid)
val currentKeywords = filterState.keywords.value[mediaType] ?: emptyList()

val keywords = if (keyword in currentKeywords) {
currentKeywords - keyword
} else {
currentKeywords + keyword
}

filterState.keywords.value += mediaType to keywords
}

fun clearKeywords(
uuid: String,
mediaType: MediaType,
) {
getFilterState(uuid).keywords.value += mediaType to emptyList()
}

fun clearRatings(mediaType: MediaType) {
_voteAverage.value += mediaType to null
_minimumVotes.value += mediaType to null
fun clearRatings(
uuid: String,
mediaType: MediaType,
) {
getFilterState(uuid).voteAverage.value += mediaType to null
getFilterState(uuid).minimumVotes.value += mediaType to null
}

fun clear(mediaType: MediaType) {
_selectedGenres.value += mediaType to emptyList()
_selectedLanguage.value += mediaType to null
_selectedCountry.value += mediaType to null
_voteAverage.value += mediaType to null
_minimumVotes.value += mediaType to null
_year.value += mediaType to null
fun clear(
uuid: String,
mediaType: MediaType,
) {
with(getFilterState(uuid)) {
selectedGenres.value += mediaType to emptyList()
keywords.value += mediaType to emptyList()
selectedLanguage.value += mediaType to null
selectedCountry.value += mediaType to null
voteAverage.value += mediaType to null
minimumVotes.value += mediaType to null
year.value += mediaType to null
}
clearFilterState(uuid)
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
@file:Suppress("TooManyFunctions")

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.Episode
import com.divinelink.core.model.details.Keyword
import com.divinelink.core.model.details.Season
import com.divinelink.core.model.details.SeasonDetails
import com.divinelink.core.model.discover.DiscoverFilter
Expand Down Expand Up @@ -54,6 +57,8 @@ interface MediaRepository {

fun fetchFavorites(mediaType: MediaType): Flow<Result<PaginationData<MediaItem>>>

suspend fun fetchSearchKeywords(request: SearchRequestApi): Result<PaginationData<Keyword>>

/**
* Request movies through a search query. Uses pagination.
* Uses [Flow] in order to observe changes to our movies list.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ 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.Episode
import com.divinelink.core.model.details.Keyword
import com.divinelink.core.model.details.Season
import com.divinelink.core.model.details.SeasonDetails
import com.divinelink.core.model.discover.DiscoverFilter
Expand Down Expand Up @@ -395,4 +396,17 @@ class ProdMediaRepository(
override fun clearAllEpisodeRatings(): Result<Unit> = runCatching {
dao.clearAllEpisodeRatings()
}

override suspend fun fetchSearchKeywords(
request: SearchRequestApi,
): Result<PaginationData<Keyword>> = remote
.searchKeywords(request)
.map { response ->
PaginationData(
page = response.page,
totalPages = response.totalPages,
totalResults = response.totalResults,
list = response.results.map { it.map() },
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ class DiscoverMediaUseCase(
voteAverage?.let { add(voteAverage) }
year?.let { add(year) }
add(DiscoverFilter.MinimumVotes(votes ?: 10))
if (keywords.isNotEmpty()) {
add(DiscoverFilter.Keywords(keywords.map { it.id }))
}
}
}.mapNotNull { it }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ import com.divinelink.core.fixtures.model.details.KeywordFactory
import com.divinelink.core.fixtures.model.details.MediaDetailsFactory
import com.divinelink.core.fixtures.model.media.MediaItemFactory
import com.divinelink.core.model.details.media.DetailsData
import com.divinelink.core.model.media.MediaType

object DetailsDataFactory {

object Empty {
fun about() = DetailsData.About(
mediaType = MediaType.UNKNOWN,
overview = null,
tagline = null,
genres = null,
Expand Down Expand Up @@ -41,6 +43,7 @@ object DetailsDataFactory {

object Movie {
fun about() = DetailsData.About(
mediaType = MediaType.MOVIE,
overview = MediaDetailsFactory.FightClub().overview,
tagline = MediaDetailsFactory.FightClub().tagline,
genres = MediaDetailsFactory.FightClub().genres,
Expand All @@ -66,6 +69,7 @@ object DetailsDataFactory {

object Tv {
fun about() = DetailsData.About(
mediaType = MediaType.TV,
overview = MediaDetailsFactory.TheOffice().overview,
tagline = MediaDetailsFactory.TheOffice().tagline,
genres = MediaDetailsFactory.TheOffice().genres,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.divinelink.core.model

import kotlinx.serialization.Serializable

@Serializable
data class Genre(
val id: Int,
val name: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.divinelink.core.model.details

import kotlinx.serialization.Serializable

@Serializable
data class Keyword(
val id: Long,
val name: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import com.divinelink.core.model.details.Person
import com.divinelink.core.model.details.Season
import com.divinelink.core.model.details.review.Review
import com.divinelink.core.model.media.MediaItem
import com.divinelink.core.model.media.MediaType

sealed interface DetailsData {
data class About(
val mediaType: MediaType,
val overview: String?,
val tagline: String?,
val genres: List<Genre>?,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ sealed interface DiscoverFilter {

data class MinimumVotes(val votes: Int) : DiscoverFilter

data class Keywords(val ids: List<Long>) : DiscoverFilter

sealed interface Year : DiscoverFilter {
data class Single(val year: Int) : Year

Expand Down
Loading