Skip to content

Commit

Permalink
added list filter
Browse files Browse the repository at this point in the history
  • Loading branch information
DatL4g committed May 17, 2024
1 parent a7e357d commit f8f111d
Show file tree
Hide file tree
Showing 8 changed files with 186 additions and 19 deletions.
3 changes: 2 additions & 1 deletion anilist/src/commonMain/graphql/ListQuery.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ query ListQuery(
$type: MediaType,
$userId: Int,
$page: Int,
$status: MediaListStatus,
$html: Boolean,
$statusVersion: Int,
$sort: [MediaListSort]
Expand All @@ -10,7 +11,7 @@ query ListQuery(
pageInfo {
hasNextPage
},
mediaList(type: $type, userId: $userId, sort: $sort) {
mediaList(type: $type, userId: $userId, sort: $sort, status: $status) {
status,
score(format: POINT_5),
progress,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.apollographql.apollo3.api.Optional
import dev.datlag.aniflow.anilist.model.Medium
import dev.datlag.aniflow.anilist.model.User
import dev.datlag.aniflow.anilist.type.MediaListSort
import dev.datlag.aniflow.anilist.type.MediaListStatus
import dev.datlag.aniflow.anilist.type.MediaType
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.*
Expand All @@ -19,9 +20,10 @@ class ListRepository(
private val page = MutableStateFlow(0)
private val _type = MutableStateFlow(MediaType.UNKNOWN__)
private val sort = MutableStateFlow(MediaListSort.UPDATED_TIME_DESC)
val status = MutableStateFlow(MediaListStatus.UNKNOWN__)

@OptIn(ExperimentalCoroutinesApi::class)
private val type = _type.transformLatest {
val type = _type.transformLatest {
return@transformLatest if (it == MediaType.UNKNOWN__) {
emitAll(viewManga.map { m ->
if (m) {
Expand All @@ -39,13 +41,15 @@ class ListRepository(
page,
type,
sort,
status,
user.filterNotNull().distinctUntilChanged(),
) { p, t, s, u ->
) { p, t, s, l, u ->
Query(
page = p,
type = t,
userId = u.id,
sort = s
sort = s,
status = l
)
}

Expand Down Expand Up @@ -87,11 +91,33 @@ class ListRepository(
}
}

fun setType(type: MediaType) {
_type.update { type }
}

fun viewAnime() = setType(MediaType.ANIME)
fun viewManga() = setType(MediaType.MANGA)

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

fun setStatus(status: MediaListStatus) {
this.status.update { status }
}

private data class Query(
val page: Int,
val type: MediaType,
val userId: Int,
val sort: MediaListSort
val sort: MediaListSort,
val status: MediaListStatus
) {
fun toGraphQL() = ListQuery(
page = Optional.present(page),
Expand All @@ -105,6 +131,11 @@ class ListRepository(
Optional.absent()
} else {
Optional.present(listOf(sort))
},
status = if (status == MediaListStatus.UNKNOWN__) {
Optional.absent()
} else {
Optional.present(status)
}
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,27 +159,33 @@ fun SearchResponse.Result.AniList.asMedium(): Medium {
)
}

fun MediaListStatus.icon() = when (this) {
fun MediaListStatus.icon(fallback: ImageVector = Icons.Rounded.Add) = when (this) {
MediaListStatus.CURRENT -> Icons.Rounded.PlayArrow
MediaListStatus.COMPLETED -> Icons.Rounded.Check
MediaListStatus.PAUSED -> Icons.Rounded.Pause
MediaListStatus.DROPPED -> Icons.Rounded.Close
MediaListStatus.PLANNING -> Icons.Rounded.WatchLater
MediaListStatus.REPEATING -> Icons.Rounded.Replay
else -> Icons.Rounded.Add
else -> fallback
}

fun MediaListStatus.stringRes(isManga: Boolean) = when (this) {
fun MediaListStatus.stringRes(
isManga: Boolean,
fallback: StringResource = SharedRes.strings.add
) = when (this) {
MediaListStatus.CURRENT -> if (isManga) SharedRes.strings.reading else SharedRes.strings.watching
MediaListStatus.COMPLETED -> SharedRes.strings.completed
MediaListStatus.PAUSED -> SharedRes.strings.paused
MediaListStatus.DROPPED -> SharedRes.strings.dropped
MediaListStatus.PLANNING -> SharedRes.strings.planning
MediaListStatus.REPEATING -> SharedRes.strings.repeating
else -> SharedRes.strings.add
else -> fallback
}

fun MediaListStatus.stringRes(type: MediaType) = this.stringRes(type == MediaType.MANGA)
fun MediaListStatus.stringRes(
type: MediaType,
fallback: StringResource = SharedRes.strings.add
) = this.stringRes(type == MediaType.MANGA, fallback)

fun MediaType.stringRes() = when (this) {
MediaType.MANGA -> SharedRes.strings.manga
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package dev.datlag.aniflow.ui.navigation.screen.favorites

import dev.datlag.aniflow.anilist.ListRepository
import dev.datlag.aniflow.anilist.model.Medium
import dev.datlag.aniflow.anilist.type.MediaListStatus
import dev.datlag.aniflow.anilist.type.MediaType
import dev.datlag.aniflow.settings.model.TitleLanguage
import dev.datlag.aniflow.ui.navigation.Component
import kotlinx.coroutines.flow.Flow
Expand All @@ -10,9 +12,15 @@ import kotlinx.coroutines.flow.StateFlow
interface FavoritesComponent : Component {
val listState: StateFlow<ListRepository.State>
val titleLanguage: Flow<TitleLanguage?>
val type: Flow<MediaType>
val status: Flow<MediaListStatus>

fun viewDiscover()
fun viewHome()

fun details(medium: Medium)
fun increase(medium: Medium, progress: Int)

fun toggleView()
fun setStatus(status: MediaListStatus)
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,36 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.rounded.MenuBook
import androidx.compose.material.icons.rounded.FilterList
import androidx.compose.material.icons.rounded.PlayArrow
import androidx.compose.material.icons.rounded.SwapVert
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.maxkeppeker.sheets.core.models.base.Header
import com.maxkeppeker.sheets.core.models.base.IconSource
import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState
import com.maxkeppeler.sheets.option.OptionDialog
import com.maxkeppeler.sheets.option.models.DisplayMode
import com.maxkeppeler.sheets.option.models.Option
import com.maxkeppeler.sheets.option.models.OptionConfig
import com.maxkeppeler.sheets.option.models.OptionSelection
import dev.chrisbanes.haze.haze
import dev.chrisbanes.haze.hazeChild
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
import dev.chrisbanes.haze.materials.HazeMaterials
import dev.datlag.aniflow.LocalHaze
import dev.datlag.aniflow.SharedRes
import dev.datlag.aniflow.anilist.ListRepository
import dev.datlag.aniflow.anilist.type.MediaListStatus
import dev.datlag.aniflow.anilist.type.MediaType
import dev.datlag.aniflow.common.*
import dev.datlag.aniflow.ui.navigation.screen.component.HidingNavigationBar
import dev.datlag.aniflow.ui.navigation.screen.component.NavigationBarState
Expand All @@ -35,6 +48,7 @@ import kotlinx.coroutines.flow.flowOf
@Composable
fun FavoritesScreen(component: FavoritesComponent) {
val listState = rememberLazyListState()
val type by component.type.collectAsStateWithLifecycle(MediaType.UNKNOWN__)

Scaffold(
topBar = {
Expand All @@ -44,6 +58,22 @@ fun FavoritesScreen(component: FavoritesComponent) {
},
actions = {

IconButton(
onClick = component::toggleView,
enabled = type != MediaType.UNKNOWN__
) {
if (type == MediaType.ANIME) {
Icon(
imageVector = Icons.AutoMirrored.Rounded.MenuBook,
contentDescription = null
)
} else {
Icon(
imageVector = Icons.Rounded.PlayArrow,
contentDescription = null
)
}
}
},
colors = TopAppBarDefaults.largeTopAppBarColors(
containerColor = Color.Transparent,
Expand All @@ -58,17 +88,51 @@ fun FavoritesScreen(component: FavoritesComponent) {
)
},
floatingActionButton = {
val status by component.status.collectAsStateWithLifecycle(MediaListStatus.UNKNOWN__)
val statusDialog = rememberUseCaseState()
val options = remember {
setOf(
MediaListStatus.UNKNOWN__,
*MediaListStatus.entries.toTypedArray()
).toList()
}

OptionDialog(
state = statusDialog,
selection = OptionSelection.Single(
options = options.map {
Option(
icon = IconSource(it.icon(Icons.Rounded.FilterList)),
titleText = stringResource(it.stringRes(type, SharedRes.strings.all)),
selected = status == it
)
},
onSelectOption = { option, _ ->
component.setStatus(options[option])
}
),
config = OptionConfig(
mode = DisplayMode.LIST
),
header = Header.Default(
icon = IconSource(Icons.Rounded.FilterList),
title = stringResource(SharedRes.strings.filter)
)
)

ExtendedFloatingActionButton(
onClick = {},
onClick = {
statusDialog.show()
},
expanded = listState.scrollUpVisible(),
icon = {
Icon(
imageVector = Icons.Rounded.SwapVert,
imageVector = status.icon(Icons.Rounded.FilterList),
contentDescription = null
)
},
text = {
Text(text = "Sort")
Text(text = stringResource(status.stringRes(type, SharedRes.strings.all)))
}
)
},
Expand Down Expand Up @@ -113,7 +177,8 @@ fun FavoritesScreen(component: FavoritesComponent) {
medium = it,
titleLanguage = titleLanguage,
modifier = Modifier.fillParentMaxWidth().height(150.dp),
onClick = component::details
onClick = component::details,
onIncrease = component::increase
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,26 @@ package dev.datlag.aniflow.ui.navigation.screen.favorites
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember
import com.apollographql.apollo3.ApolloClient
import com.apollographql.apollo3.api.Optional
import com.arkivanov.decompose.ComponentContext
import dev.chrisbanes.haze.HazeState
import dev.datlag.aniflow.LocalHaze
import dev.datlag.aniflow.anilist.EditMutation
import dev.datlag.aniflow.anilist.ListRepository
import dev.datlag.aniflow.anilist.model.Medium
import dev.datlag.aniflow.anilist.type.MediaListStatus
import dev.datlag.aniflow.anilist.type.MediaType
import dev.datlag.aniflow.common.onRender
import dev.datlag.aniflow.model.coroutines.Executor
import dev.datlag.aniflow.other.Constants
import dev.datlag.aniflow.other.UserHelper
import dev.datlag.aniflow.settings.Settings
import dev.datlag.aniflow.settings.model.TitleLanguage
import dev.datlag.tooling.compose.ioDispatcher
import dev.datlag.tooling.compose.withMainContext
import dev.datlag.tooling.decompose.ioScope
import io.github.aakira.napier.Napier
import kotlinx.coroutines.flow.*
import org.kodein.di.DI
import org.kodein.di.instance
Expand All @@ -30,6 +38,7 @@ class FavoritesScreenComponent(
private val appSettings by instance<Settings.PlatformAppSettings>()
override val titleLanguage: Flow<TitleLanguage?> = appSettings.titleLanguage

private val apolloClient by instance<ApolloClient>(Constants.AniList.APOLLO_CLIENT)
private val listRepository by instance<ListRepository>()
override val listState: StateFlow<ListRepository.State> = listRepository.list.flowOn(
context = ioDispatcher()
Expand All @@ -39,6 +48,10 @@ class FavoritesScreenComponent(
initialValue = ListRepository.State.None
)

private val increaseExecutor = Executor()
override val type: Flow<MediaType> = listRepository.type
override val status: Flow<MediaListStatus> = listRepository.status

@Composable
override fun render() {
val haze = remember { HazeState() }
Expand All @@ -63,4 +76,40 @@ class FavoritesScreenComponent(
override fun details(medium: Medium) {
onMedium(medium)
}

override fun increase(medium: Medium, progress: Int) {
val newStatus = if (progress >= medium.episodesOrChapters) {
when (val current = medium.entry?.status) {
MediaListStatus.REPEATING -> current
else -> MediaListStatus.COMPLETED
}
} else {
medium.entry?.status ?: MediaListStatus.UNKNOWN__
}

val mutation = EditMutation(
mediaId = Optional.present(medium.id),
progress = Optional.present(progress),
status = if (newStatus == MediaListStatus.UNKNOWN__) {
Optional.absent()
} else {
Optional.present(newStatus)
},
repeat = Optional.absent()
)

launchIO {
increaseExecutor.enqueue {
apolloClient.mutation(mutation).execute()
}
}
}

override fun toggleView() {
listRepository.toggleType()
}

override fun setStatus(status: MediaListStatus) {
listRepository.setStatus(status)
}
}
Loading

0 comments on commit f8f111d

Please sign in to comment.