Skip to content

Commit

Permalink
added full discover functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
DatL4g committed May 25, 2024
1 parent 27e87fe commit 11912d5
Show file tree
Hide file tree
Showing 16 changed files with 492 additions and 505 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package dev.datlag.aniflow.anilist

import com.apollographql.apollo3.ApolloClient
import com.freeletics.flowredux.dsl.FlowReduxStateMachine
import dev.datlag.aniflow.anilist.common.hasNonCacheError
import dev.datlag.aniflow.anilist.common.season
import dev.datlag.aniflow.anilist.model.PageListQuery
import dev.datlag.aniflow.anilist.model.PageMediaQuery
import dev.datlag.aniflow.anilist.model.User
import dev.datlag.aniflow.anilist.state.DiscoverAction
import dev.datlag.aniflow.anilist.state.DiscoverState
import dev.datlag.aniflow.anilist.state.DiscoverListType
import dev.datlag.aniflow.anilist.type.MediaSeason
import dev.datlag.aniflow.anilist.type.MediaType
import dev.datlag.aniflow.firebase.FirebaseFactory
Expand All @@ -19,12 +19,12 @@ import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.transform
import kotlinx.coroutines.flow.transformLatest
import kotlinx.coroutines.flow.update
import kotlinx.datetime.Clock
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toLocalDateTime

@OptIn(ExperimentalCoroutinesApi::class)
class DiscoverStateMachine(
Expand All @@ -34,8 +34,6 @@ class DiscoverStateMachine(
private val nsfw: Flow<Boolean> = flowOf(false),
private val viewManga: Flow<Boolean> = flowOf(false),
private val crashlytics: FirebaseFactory.Crashlytics?,
) : FlowReduxStateMachine<DiscoverState, DiscoverAction>(
initialState = currentState
) {

var currentState: DiscoverState
Expand All @@ -44,6 +42,9 @@ class DiscoverStateMachine(
Companion.currentState = value
}

val currentListType: DiscoverListType
get() = _listType.value

private val _type: MutableStateFlow<MediaType> = MutableStateFlow(MediaType.UNKNOWN__)
val type = _type.transformLatest {
return@transformLatest if (it == MediaType.UNKNOWN__) {
Expand All @@ -59,6 +60,18 @@ class DiscoverStateMachine(
}
}.distinctUntilChanged()

private val _listType: MutableStateFlow<DiscoverListType> = MutableStateFlow(DiscoverListType.Recommendation)
val listType = combine(
_listType,
user.map { it?.id }.distinctUntilChanged()
) { l, u ->
if (u == null) {
DiscoverListType.Season.fromSeason(Clock.System.now().season)
} else {
l
}
}.distinctUntilChanged()

private val recommendationQuery = combine(
type,
user.mapNotNull { it?.id }.distinctUntilChanged()
Expand All @@ -69,140 +82,105 @@ class DiscoverStateMachine(
)
}.distinctUntilChanged()

private val _season: MutableStateFlow<MediaSeason> = MutableStateFlow(MediaSeason.UNKNOWN__)
val season = _season.transformLatest {
return@transformLatest if (it == MediaSeason.UNKNOWN__) {
emit(Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()).month.season)
private val fallbackRecommendationResponse = combine(
recommendationQuery.transformLatest {
return@transformLatest emitAll(fallbackClient.query(it.toGraphQL()).toFlow())
},
nsfw.distinctUntilChanged()
) { r, n ->
DiscoverState.Recommended.Loading.fromList(n, r)
}.transformLatest {
return@transformLatest when (it) {
is DiscoverState.Recommended.Loading.Matching -> {
emitAll(fallbackClient.query(it.query.toGraphQL()).toFlow().mapLatest { r ->
DiscoverState.Recommended.Loading.fromMatching(it, r)
})
}
else -> emit(it)
}
}

private val recommendationResponse = combine(
recommendationQuery.transformLatest {
return@transformLatest emitAll(client.query(it.toGraphQL()).toFlow())
},
nsfw.distinctUntilChanged()
) { r, n ->
DiscoverState.Recommended.Loading.fromList(n, r)
}.transformLatest {
return@transformLatest if (it.isFailure) {
emitAll(fallbackRecommendationResponse)
} else {
emit(it)
when (it) {
is DiscoverState.Recommended.Loading.Matching -> {
emitAll(client.query(it.query.toGraphQL()).toFlow().mapLatest { r ->
DiscoverState.Recommended.Loading.fromMatching(it, r)
})
}
else -> emit(it)
}
}
}.distinctUntilChanged()
}

private val seasonQuery = combine(
type,
season,
nsfw.distinctUntilChanged(),
) { t, s, n ->
private fun seasonQuery(season: MediaSeason) = nsfw.distinctUntilChanged().mapLatest { n ->
PageMediaQuery.Season(
type = t,
season = s,
season = season,
nsfw = n
)
}.distinctUntilChanged()

init {
spec {
inState<DiscoverState> {
onEnterEffect {
currentState = it
}
onActionEffect<DiscoverAction.Type> { action, _ ->
when (action) {
is DiscoverAction.Type.Anime -> _type.update { MediaType.ANIME }
is DiscoverAction.Type.Manga -> _type.update { MediaType.MANGA }
is DiscoverAction.Type.Toggle -> _type.update {
if (it == MediaType.MANGA) {
MediaType.ANIME
} else {
MediaType.MANGA
}
}
}
}
on<DiscoverAction.ListType.Recommendation> { _, state ->
if (state is DiscoverState.Recommended) {
state.noChange()
} else {
state.override {
DiscoverState.Recommended.None
}
}
}
}
private fun fallbackSeasonResponse(season: MediaSeason) = seasonQuery(season).transformLatest {
return@transformLatest emitAll(fallbackClient.query(it.toGraphQL()).toFlow())
}

inState<DiscoverState.Recommended> {
on<DiscoverAction.ListType.Season> { action, state ->
state.override {
DiscoverState.Season.None(
wanted = action.mediaSeason
)
}
}
collectWhileInState(recommendationQuery) { q, state ->
state.override {
DiscoverState.Recommended.Loading.WatchList(
query = q,
fallback = false
)
}
}
}
inState<DiscoverState.Recommended.Loading.WatchList> {
collectWhileInState(
flowBuilder = {
val usedClient = if (it.fallback) {
fallbackClient
} else {
client
}

combine(
nsfw.distinctUntilChanged(),
usedClient.query(it.query.toGraphQL()).toFlow()
) { n, r ->
n to r
}
}
) { (adult, response), state ->
state.override {
fromGraphQL(adult, response)
}
}
}
inState<DiscoverState.Recommended.Loading.Matching> {
collectWhileInState(
flowBuilder = {
val usedClient = if (it.fallback) {
fallbackClient
} else {
client
}

usedClient.query(it.query.toGraphQL()).toFlow()
}
) { response, state ->
state.override {
fromGraphQL(response)
}
}
}
private fun seasonResponse(season: MediaSeason) = seasonQuery(season).transformLatest {
return@transformLatest emitAll(client.query(it.toGraphQL()).toFlow())
}.transformLatest {
return@transformLatest if (it.hasNonCacheError()) {
emitAll(fallbackSeasonResponse(season))
} else {
emit(it)
}
}.mapLatest { result ->
DiscoverState.Season.fromSeasonResponse(result)
}

inState<DiscoverState.Season.Loading> {
collectWhileInState(
flowBuilder = {
val usedClient = if (it.fallback) {
fallbackClient
} else {
client
}

usedClient.query(it.query.toGraphQL()).toFlow()
}
) { response, state ->
state.override {
fromGraphQL(response)
}
}
val state = listType.transformLatest { type ->
return@transformLatest when (type) {
is DiscoverListType.Recommendation -> emitAll(recommendationResponse)
is DiscoverListType.Season -> emitAll(seasonResponse(type.mediaSeason))
}
}.mapLatest { m ->
m.also { state ->
(state as? DiscoverState.Failure)?.throwable?.let {
crashlytics?.log(it)
}
currentState = state
}
}

inState<DiscoverState.Error> {
onEnterEffect {
crashlytics?.log(it.throwable)
}
fun viewAnime() {
_type.update { MediaType.ANIME }
}

fun viewManga() {
_type.update { MediaType.MANGA }
}

fun toggleType() {
_type.update {
if (it == MediaType.MANGA) {
MediaType.ANIME
} else {
MediaType.MANGA
}
}
}

fun listType(type: DiscoverListType) {
_listType.update { type }
}

companion object {
var currentState: DiscoverState
get() = StateSaver.discoverState
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ internal object StateSaver {

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

var discoverState: DiscoverState = DiscoverState.Recommended.None
var discoverState: DiscoverState = DiscoverState.Recommended.Loading.WatchList
}
Original file line number Diff line number Diff line change
Expand Up @@ -161,13 +161,12 @@ sealed interface PageMediaQuery {

data class Season(
val season: MediaSeason,
val type: MediaType,
val nsfw: Boolean
) : PageMediaQuery {
override fun toGraphQL() = PageMediaGraphQL(
season = Optional.presentMediaSeason(season),
adultContent = Optional.presentIfNot(nsfw),
type = Optional.presentMediaType(type),
type = Optional.presentMediaType(MediaType.ANIME),
preventGenres = Optional.presentIfNot(nsfw, AdultContent.Genre.allTags),
statusVersion = 2,
html = true
Expand Down
Loading

0 comments on commit 11912d5

Please sign in to comment.