Skip to content

Commit

Permalink
use immutable collection to have stable classes
Browse files Browse the repository at this point in the history
  • Loading branch information
DatL4g committed May 25, 2024
1 parent 52bc90f commit bf096cd
Show file tree
Hide file tree
Showing 36 changed files with 230 additions and 91 deletions.
1 change: 1 addition & 0 deletions anilist/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ kotlin {
implementation(libs.datetime)
implementation(libs.serialization)
implementation(libs.tooling)
api(libs.immutable)

implementation(project(":model"))
implementation(project(":firebase"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import dev.datlag.aniflow.anilist.state.ListState
import dev.datlag.aniflow.anilist.type.MediaListStatus
import dev.datlag.aniflow.anilist.type.MediaType
import dev.datlag.aniflow.firebase.FirebaseFactory
import kotlinx.collections.immutable.persistentSetOf
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
Expand Down Expand Up @@ -92,7 +93,7 @@ class ListStateMachine(
p to r
}.runningFold(initial = currentState) { accumulator, (p, r) ->
return@runningFold (if (p <= 0) {
ListState.fromResponse(emptyList(), r)
ListState.fromResponse(persistentSetOf(), r)
} else {
ListState.fromResponse(accumulator, r)
}).also { state ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import dev.datlag.aniflow.anilist.state.HomeAiringState
import dev.datlag.aniflow.anilist.state.HomeDefaultState
import dev.datlag.aniflow.anilist.state.ListState
import dev.datlag.aniflow.anilist.state.SearchState
import kotlinx.collections.immutable.persistentSetOf

internal object StateSaver {
var airingState: HomeAiringState = HomeAiringState.Loading
Expand All @@ -16,7 +17,7 @@ internal object StateSaver {
var searchState: SearchState = SearchState.None
var searchQuery: String? = null

var listState: ListState = ListState.Loading(emptyList())
var listState: ListState = ListState.Loading(persistentSetOf())

var discoverState: DiscoverState = DiscoverState.Recommended.Loading.WatchList
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import dev.datlag.aniflow.anilist.type.MediaListSort
import dev.datlag.aniflow.anilist.type.MediaListStatus
import dev.datlag.aniflow.anilist.type.MediaSeason
import dev.datlag.aniflow.anilist.type.MediaType
import kotlinx.collections.immutable.persistentListOf

fun ApolloResponse<*>.hasNonCacheError(): Boolean {
return when (exception) {
Expand Down Expand Up @@ -62,4 +63,4 @@ fun <V> Optional.Companion.presentIfNot(predicate: Boolean, value: V) = if (pred
present(value)
}

fun <V> Optional.Companion.presentAsList(vararg value: V) = present(listOf(*value))
fun <V> Optional.Companion.presentAsList(vararg value: V) = present(persistentListOf(*value))
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ import dev.datlag.aniflow.anilist.common.toLocalDate
import dev.datlag.aniflow.anilist.type.*
import dev.datlag.aniflow.model.ifValue
import dev.datlag.aniflow.model.toInt
import kotlinx.collections.immutable.ImmutableCollection
import kotlinx.collections.immutable.ImmutableSet
import kotlinx.collections.immutable.persistentSetOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.toImmutableSet
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlinx.datetime.LocalDate
Expand All @@ -26,7 +31,7 @@ data class Medium(
val avgEpisodeDurationInMin: Int = -1,
val format: MediaFormat = MediaFormat.UNKNOWN__,
private val _isAdult: Boolean = false,
val genres: Set<String> = emptySet(),
val genres: ImmutableSet<String> = persistentSetOf(),
val countryOfOrigin: String? = null,
val averageScore: Int = -1,
val title: Title = Title(
Expand All @@ -43,8 +48,8 @@ data class Medium(
color = null
),
val nextAiringEpisode: NextAiring? = null,
val ranking: Set<Ranking> = emptySet(),
private val _characters: Set<Character> = emptySet(),
val ranking: ImmutableCollection<Ranking> = persistentSetOf(),
private val _characters: ImmutableCollection<Character> = persistentSetOf(),
val entry: Entry? = null,
val trailer: Trailer? = null,
val isFavorite: Boolean = false,
Expand All @@ -64,7 +69,7 @@ data class Medium(
avgEpisodeDurationInMin = trending.duration ?: -1,
format = trending.format ?: MediaFormat.UNKNOWN__,
_isAdult = trending.isAdult ?: false,
genres = trending.genresFilterNotNull()?.toSet() ?: emptySet(),
genres = trending.genresFilterNotNull()?.toImmutableSet() ?: persistentSetOf(),
countryOfOrigin = trending.countryOfOrigin?.toString()?.ifBlank { null },
averageScore = trending.averageScore ?: -1,
title = Title(
Expand All @@ -81,8 +86,8 @@ data class Medium(
extraLarge = trending.coverImage?.extraLarge?.ifBlank { null }
),
nextAiringEpisode = trending.nextAiringEpisode?.let(::NextAiring),
ranking = trending.rankingsFilterNotNull()?.map(::Ranking)?.toSet() ?: emptySet(),
_characters = trending.characters?.nodesFilterNotNull()?.mapNotNull(Character::invoke)?.toSet() ?: emptySet(),
ranking = trending.rankingsFilterNotNull()?.map(::Ranking)?.toImmutableSet() ?: persistentSetOf(),
_characters = trending.characters?.nodesFilterNotNull()?.mapNotNull(Character::invoke)?.toImmutableSet() ?: persistentSetOf(),
entry = trending.mediaListEntry?.let(::Entry),
trailer = trending.trailer?.let {
val site = it.site?.ifBlank { null }
Expand Down Expand Up @@ -116,7 +121,7 @@ data class Medium(
avgEpisodeDurationInMin = airing.duration ?: -1,
format = airing.format ?: MediaFormat.UNKNOWN__,
_isAdult = airing.isAdult ?: false,
genres = airing.genresFilterNotNull()?.toSet() ?: emptySet(),
genres = airing.genresFilterNotNull()?.toImmutableSet() ?: persistentSetOf(),
countryOfOrigin = airing.countryOfOrigin?.toString()?.ifBlank { null },
averageScore = airing.averageScore ?: -1,
title = Title(
Expand All @@ -133,8 +138,8 @@ data class Medium(
extraLarge = airing.coverImage?.extraLarge?.ifBlank { null }
),
nextAiringEpisode = airing.nextAiringEpisode?.let(::NextAiring),
ranking = airing.rankingsFilterNotNull()?.map(::Ranking)?.toSet() ?: emptySet(),
_characters = airing.characters?.nodesFilterNotNull()?.mapNotNull(Character::invoke)?.toSet() ?: emptySet(),
ranking = airing.rankingsFilterNotNull()?.map(::Ranking)?.toImmutableSet() ?: persistentSetOf(),
_characters = airing.characters?.nodesFilterNotNull()?.mapNotNull(Character::invoke)?.toImmutableSet() ?: persistentSetOf(),
entry = airing.mediaListEntry?.let(::Entry),
trailer = airing.trailer?.let {
val site = it.site?.ifBlank { null }
Expand Down Expand Up @@ -168,7 +173,7 @@ data class Medium(
avgEpisodeDurationInMin = query.duration ?: -1,
format = query.format ?: MediaFormat.UNKNOWN__,
_isAdult = query.isAdult ?: false,
genres = query.genresFilterNotNull()?.toSet() ?: emptySet(),
genres = query.genresFilterNotNull()?.toImmutableSet() ?: persistentSetOf(),
countryOfOrigin = query.countryOfOrigin?.toString()?.ifBlank { null },
averageScore = query.averageScore ?: -1,
title = Title(
Expand All @@ -185,8 +190,8 @@ data class Medium(
extraLarge = query.coverImage?.extraLarge?.ifBlank { null }
),
nextAiringEpisode = query.nextAiringEpisode?.let(::NextAiring),
ranking = query.rankingsFilterNotNull()?.map(::Ranking)?.toSet() ?: emptySet(),
_characters = query.characters?.nodesFilterNotNull()?.mapNotNull(Character::invoke)?.toSet() ?: emptySet(),
ranking = query.rankingsFilterNotNull()?.map(::Ranking)?.toImmutableSet() ?: persistentSetOf(),
_characters = query.characters?.nodesFilterNotNull()?.mapNotNull(Character::invoke)?.toImmutableSet() ?: persistentSetOf(),
entry = query.mediaListEntry?.let(::Entry),
trailer = query.trailer?.let {
val site = it.site?.ifBlank { null }
Expand Down Expand Up @@ -220,7 +225,7 @@ data class Medium(
avgEpisodeDurationInMin = media.duration ?: -1,
format = media.format ?: MediaFormat.UNKNOWN__,
_isAdult = media.isAdult ?: false,
genres = media.genresFilterNotNull()?.toSet() ?: emptySet(),
genres = media.genresFilterNotNull()?.toImmutableSet() ?: persistentSetOf(),
countryOfOrigin = media.countryOfOrigin?.toString()?.ifBlank { null },
averageScore = media.averageScore ?: -1,
title = Title(
Expand All @@ -237,8 +242,8 @@ data class Medium(
extraLarge = media.coverImage?.extraLarge?.ifBlank { null }
),
nextAiringEpisode = media.nextAiringEpisode?.let(::NextAiring),
ranking = media.rankingsFilterNotNull()?.map(::Ranking)?.toSet() ?: emptySet(),
_characters = media.characters?.nodesFilterNotNull()?.mapNotNull(Character::invoke)?.toSet() ?: emptySet(),
ranking = media.rankingsFilterNotNull()?.map(::Ranking)?.toImmutableSet() ?: persistentSetOf(),
_characters = media.characters?.nodesFilterNotNull()?.mapNotNull(Character::invoke)?.toImmutableSet() ?: persistentSetOf(),
entry = list?.let(::Entry),
trailer = media.trailer?.let {
val site = it.site?.ifBlank { null }
Expand Down Expand Up @@ -268,7 +273,7 @@ data class Medium(
}

@Transient
val characters: Set<Character> = _characters.filterNot { it.id == 36309 }.toSet()
val characters: ImmutableSet<Character> = _characters.filterNot { it.id == 36309 }.toImmutableSet()

@Transient
val isFavoriteBlocked: Boolean = _isFavoriteBlocked || type == MediaType.UNKNOWN__
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import dev.datlag.aniflow.anilist.type.MediaSort
import dev.datlag.aniflow.anilist.type.MediaType
import dev.datlag.tooling.safeSubList
import dev.datlag.tooling.safeSubSet
import kotlinx.collections.immutable.ImmutableCollection
import kotlinx.collections.immutable.toImmutableSet
import kotlinx.datetime.Clock
import dev.datlag.aniflow.anilist.PageMediaQuery as PageMediaGraphQL

Expand Down Expand Up @@ -98,15 +100,15 @@ sealed interface PageMediaQuery {
}

data class Recommendation(
val wantedGenres: Collection<String>,
val preventIds: Collection<Int>,
val wantedGenres: ImmutableCollection<String>,
val preventIds: ImmutableCollection<Int>,
val type: MediaType,
val nsfw: Boolean
) : PageMediaQuery {

constructor(
nsfw: Boolean,
collection: Collection<Medium>,
collection: ImmutableCollection<Medium>,
type: MediaType = collection.let { c ->
val allTypes = c.map {
it.type
Expand Down Expand Up @@ -140,8 +142,8 @@ sealed interface PageMediaQuery {
allGenres.groupingBy { g -> g }.eachCount().toList().sortedByDescending { p ->
p.second
}.safeSubSet(0, 5).toMap().keys.safeSubSet(0, 5)
},
preventIds = collection.map { it.id },
}.toImmutableSet(),
preventIds = collection.map { it.id }.toImmutableSet(),
type = type,
nsfw = nsfw
)
Expand All @@ -151,8 +153,8 @@ sealed interface PageMediaQuery {
type = Optional.presentMediaType(type),
sort = Optional.presentAsList(MediaSort.TRENDING_DESC),
preventGenres = Optional.presentIfNot(nsfw, AdultContent.Genre.allTags),
wantedGenres = Optional.present(wantedGenres.toList()),
preventIds = Optional.present(preventIds.toList()),
wantedGenres = Optional.presentAsList(*wantedGenres.toTypedArray()),
preventIds = Optional.presentAsList(*preventIds.toTypedArray()),
onList = Optional.present(false),
statusVersion = 2,
html = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import dev.datlag.aniflow.anilist.model.Medium
import dev.datlag.aniflow.anilist.model.PageListQuery
import dev.datlag.aniflow.anilist.model.PageMediaQuery
import dev.datlag.aniflow.anilist.type.MediaSeason
import kotlinx.collections.immutable.ImmutableCollection
import kotlinx.collections.immutable.persistentSetOf
import kotlinx.collections.immutable.toImmutableList
import dev.datlag.aniflow.anilist.PageMediaQuery as PageMediaGraphQL

sealed interface DiscoverState {
Expand Down Expand Up @@ -47,7 +50,7 @@ sealed interface DiscoverState {
Matching(
query = PageMediaQuery.Recommendation(
nsfw = nsfw,
collection = mediumList
collection = mediumList.toImmutableList()
)
)
}
Expand All @@ -70,7 +73,7 @@ sealed interface DiscoverState {
Failure(response.exception)
} else {
Success(
collection = mediumList.map(::Medium).distinctBy { it.id }
collection = mediumList.map(::Medium).distinctBy { it.id }.toImmutableList()
)
}
}
Expand Down Expand Up @@ -100,7 +103,7 @@ sealed interface DiscoverState {
Failure(throwable = response.exception)
} else {
Success(
collection = mediumList.map(::Medium).distinctBy { it.id }
collection = mediumList.map(::Medium).distinctBy { it.id }.toImmutableList()
)
}
}
Expand All @@ -111,7 +114,7 @@ sealed interface DiscoverState {
private sealed interface PostLoading : DiscoverState

data class Success(
val collection: Collection<Medium>
val collection: ImmutableCollection<Medium>
) : PostLoading

data class Failure(
Expand Down Expand Up @@ -154,7 +157,7 @@ sealed interface DiscoverListType {
}

companion object {
val entries = setOf(
val entries = persistentSetOf(
DiscoverListType.Recommendation,
DiscoverListType.Spring,
DiscoverListType.Summer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import dev.datlag.aniflow.anilist.AdultContent
import dev.datlag.aniflow.anilist.AiringQuery
import dev.datlag.aniflow.anilist.common.hasNonCacheError
import dev.datlag.aniflow.anilist.model.PageAiringQuery
import kotlinx.collections.immutable.ImmutableCollection
import kotlinx.collections.immutable.toImmutableList

import dev.datlag.aniflow.anilist.AiringQuery as PageAiringGraphQL

Expand All @@ -22,7 +24,7 @@ sealed interface HomeAiringState {
private sealed interface PostLoading : HomeAiringState

data class Success(
val collection: Collection<PageAiringGraphQL.AiringSchedule>
val collection: ImmutableCollection<PageAiringGraphQL.AiringSchedule>
) : PostLoading

data class Failure(
Expand Down Expand Up @@ -55,7 +57,7 @@ sealed interface HomeAiringState {
if (airingList.isNullOrEmpty()) {
Failure(throwable = response.exception)
} else {
Success(airingList)
Success(airingList.toImmutableList())
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import com.apollographql.apollo3.api.ApolloResponse
import dev.datlag.aniflow.anilist.PageMediaQuery
import dev.datlag.aniflow.anilist.common.hasNonCacheError
import dev.datlag.aniflow.anilist.model.Medium
import kotlinx.collections.immutable.ImmutableCollection
import kotlinx.collections.immutable.toImmutableList

sealed interface HomeDefaultState {

Expand All @@ -17,7 +19,7 @@ sealed interface HomeDefaultState {

private sealed interface PostLoading : HomeDefaultState

data class Success(val collection: Collection<Medium>) : PostLoading
data class Success(val collection: ImmutableCollection<Medium>) : PostLoading

data class Failure(
internal val throwable: Throwable?
Expand All @@ -39,7 +41,7 @@ sealed interface HomeDefaultState {
if (mediumList.isNullOrEmpty()) {
Failure(response.exception)
} else {
Success(mediumList.map(::Medium))
Success(mediumList.map(::Medium).toImmutableList())
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ import com.apollographql.apollo3.api.ApolloResponse
import dev.datlag.aniflow.anilist.ListQuery
import dev.datlag.aniflow.anilist.common.hasNonCacheError
import dev.datlag.aniflow.anilist.model.Medium
import dev.datlag.aniflow.anilist.model.PageListQuery
import dev.datlag.aniflow.anilist.type.MediaListStatus
import kotlinx.collections.immutable.ImmutableCollection
import kotlinx.collections.immutable.toImmutableList

sealed interface ListState {

val hasNextPage: Boolean
val collection: Collection<Medium>
val collection: ImmutableCollection<Medium>

data class Loading(
override val collection: Collection<Medium>
override val collection: ImmutableCollection<Medium>
) : ListState {
override val hasNextPage: Boolean = false
}
Expand All @@ -22,12 +22,12 @@ sealed interface ListState {

data class Success(
override val hasNextPage: Boolean,
override val collection: Collection<Medium>
override val collection: ImmutableCollection<Medium>
) : PostLoading

data class Failure(
internal val throwable: Throwable?,
override val collection: Collection<Medium>
override val collection: ImmutableCollection<Medium>
) : PostLoading {
override val hasNextPage: Boolean = false
}
Expand All @@ -43,7 +43,7 @@ sealed interface ListState {
)

fun fromResponse(
previousCollection: Collection<Medium>,
previousCollection: ImmutableCollection<Medium>,
response: ApolloResponse<ListQuery.Data>
): ListState {
val data = response.data
Expand Down Expand Up @@ -73,7 +73,7 @@ sealed interface ListState {
media = it.media ?: return@mapNotNull null,
list = it
)
}).distinctBy { it.id }
}).distinctBy { it.id }.toImmutableList()
)
}
}
Expand Down
Loading

0 comments on commit bf096cd

Please sign in to comment.