diff --git a/anilist/src/commonMain/kotlin/dev/datlag/aniflow/anilist/model/AiringInfo.kt b/anilist/src/commonMain/kotlin/dev/datlag/aniflow/anilist/model/AiringInfo.kt new file mode 100644 index 0000000..3d1e148 --- /dev/null +++ b/anilist/src/commonMain/kotlin/dev/datlag/aniflow/anilist/model/AiringInfo.kt @@ -0,0 +1,23 @@ +package dev.datlag.aniflow.anilist.model + +import dev.datlag.aniflow.anilist.AiringQuery +import kotlinx.serialization.Serializable + +@Serializable +data class AiringInfo( + val airingAt: Int, + val episode: Int, + val medium: Medium +) { + companion object { + operator fun invoke(schedule: AiringQuery.AiringSchedule): AiringInfo? { + val medium = schedule.media?.let(::Medium) ?: return null + + return AiringInfo( + airingAt = schedule.airingAt, + episode = schedule.episode, + medium = medium + ) + } + } +} \ No newline at end of file diff --git a/anilist/src/commonMain/kotlin/dev/datlag/aniflow/anilist/model/PageMediaQuery.kt b/anilist/src/commonMain/kotlin/dev/datlag/aniflow/anilist/model/PageMediaQuery.kt index bb05aea..918b304 100644 --- a/anilist/src/commonMain/kotlin/dev/datlag/aniflow/anilist/model/PageMediaQuery.kt +++ b/anilist/src/commonMain/kotlin/dev/datlag/aniflow/anilist/model/PageMediaQuery.kt @@ -7,6 +7,7 @@ import dev.datlag.aniflow.anilist.common.presentAsList import dev.datlag.aniflow.anilist.common.presentIfNot import dev.datlag.aniflow.anilist.common.presentMediaSeason import dev.datlag.aniflow.anilist.common.presentMediaType +import dev.datlag.aniflow.anilist.common.year import dev.datlag.aniflow.anilist.type.MediaSeason import dev.datlag.aniflow.anilist.type.MediaSort import dev.datlag.aniflow.anilist.type.MediaType @@ -15,6 +16,10 @@ import dev.datlag.tooling.safeSubSet import kotlinx.collections.immutable.ImmutableCollection import kotlinx.collections.immutable.toImmutableSet import kotlinx.datetime.Clock +import kotlinx.datetime.DateTimeUnit +import kotlinx.datetime.TimeZone +import kotlinx.datetime.minus +import kotlin.time.Duration.Companion.seconds import dev.datlag.aniflow.anilist.PageMediaQuery as PageMediaGraphQL sealed interface PageMediaQuery { @@ -163,10 +168,12 @@ sealed interface PageMediaQuery { data class Season( val season: MediaSeason, + val year: Int = Clock.System.now().minus(1, DateTimeUnit.YEAR, TimeZone.currentSystemDefault()).year, val nsfw: Boolean ) : PageMediaQuery { override fun toGraphQL() = PageMediaGraphQL( season = Optional.presentMediaSeason(season), + year = Optional.present(year), adultContent = Optional.presentIfNot(nsfw), type = Optional.presentMediaType(MediaType.ANIME), preventGenres = Optional.presentIfNot(nsfw, AdultContent.Genre.allTags), diff --git a/anilist/src/commonMain/kotlin/dev/datlag/aniflow/anilist/state/HomeAiringState.kt b/anilist/src/commonMain/kotlin/dev/datlag/aniflow/anilist/state/HomeAiringState.kt index d2b9290..e63ea68 100644 --- a/anilist/src/commonMain/kotlin/dev/datlag/aniflow/anilist/state/HomeAiringState.kt +++ b/anilist/src/commonMain/kotlin/dev/datlag/aniflow/anilist/state/HomeAiringState.kt @@ -5,6 +5,7 @@ import com.apollographql.apollo3.exception.CacheMissException 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.AiringInfo import dev.datlag.aniflow.anilist.model.PageAiringQuery import kotlinx.collections.immutable.ImmutableCollection import kotlinx.collections.immutable.toImmutableList @@ -24,7 +25,7 @@ sealed interface HomeAiringState { private sealed interface PostLoading : HomeAiringState data class Success( - val collection: ImmutableCollection + val collection: ImmutableCollection ) : PostLoading data class Failure( @@ -53,11 +54,12 @@ sealed interface HomeAiringState { } } } + val airingInfo = airingList?.mapNotNull { AiringInfo(it) } - if (airingList.isNullOrEmpty()) { + if (airingInfo.isNullOrEmpty()) { Failure(throwable = response.exception) } else { - Success(airingList.toImmutableList()) + Success(airingInfo.toImmutableList()) } } } diff --git a/anilist/src/commonMain/kotlin/dev/datlag/aniflow/anilist/state/ListState.kt b/anilist/src/commonMain/kotlin/dev/datlag/aniflow/anilist/state/ListState.kt index a0abc56..61d3aab 100644 --- a/anilist/src/commonMain/kotlin/dev/datlag/aniflow/anilist/state/ListState.kt +++ b/anilist/src/commonMain/kotlin/dev/datlag/aniflow/anilist/state/ListState.kt @@ -5,6 +5,7 @@ import dev.datlag.aniflow.anilist.ListQuery import dev.datlag.aniflow.anilist.common.hasNonCacheError import dev.datlag.aniflow.anilist.model.Medium import kotlinx.collections.immutable.ImmutableCollection +import kotlinx.collections.immutable.persistentSetOf import kotlinx.collections.immutable.toImmutableList sealed interface ListState { @@ -20,6 +21,12 @@ sealed interface ListState { private sealed interface PostLoading : ListState + data object Empty : PostLoading { + override val hasNextPage: Boolean = false + + override val collection: ImmutableCollection = persistentSetOf() + } + data class Success( override val hasNextPage: Boolean, override val collection: ImmutableCollection @@ -61,10 +68,7 @@ sealed interface ListState { val mediaList = data.Page?.mediaListFilterNotNull() if (mediaList.isNullOrEmpty()) { - Failure( - throwable = response.exception, - collection = previousCollection - ) + Empty } else { Success( hasNextPage = data.Page.pageInfo?.hasNextPage ?: false, diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/favorites/FavoritesScreen.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/favorites/FavoritesScreen.kt index 778eea8..ae4c89d 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/favorites/FavoritesScreen.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/favorites/FavoritesScreen.kt @@ -21,6 +21,7 @@ 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.text.style.TextAlign import androidx.compose.ui.unit.dp import com.maxkeppeker.sheets.core.models.base.Header import com.maxkeppeker.sheets.core.models.base.IconSource @@ -219,6 +220,17 @@ private fun ListData( ) } else { when (state) { + is ListState.Empty -> { + Box( + modifier = Modifier.fillMaxSize().padding(padding).padding(horizontal = 16.dp), + contentAlignment = Alignment.Center + ) { + Text( + text = stringResource(SharedRes.strings.nothing_on_list), + textAlign = TextAlign.Center + ) + } + } is ListState.Loading -> { Box( modifier = Modifier.fillMaxSize().padding(padding), diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/home/component/airing/AiringCard.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/home/component/airing/AiringCard.kt index fab58e0..fc249a5 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/home/component/airing/AiringCard.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/home/component/airing/AiringCard.kt @@ -14,6 +14,7 @@ import androidx.compose.ui.unit.dp import coil3.compose.AsyncImage import coil3.compose.rememberAsyncImagePainter import dev.datlag.aniflow.anilist.AiringQuery +import dev.datlag.aniflow.anilist.model.AiringInfo import dev.datlag.aniflow.anilist.model.Medium import dev.datlag.aniflow.common.preferred import dev.datlag.aniflow.ui.theme.SchemeTheme @@ -21,39 +22,34 @@ import dev.datlag.aniflow.settings.model.TitleLanguage as SettingsTitle @Composable fun AiringCard( - airing: AiringQuery.AiringSchedule, + airing: AiringInfo, titleLanguage: SettingsTitle?, modifier: Modifier = Modifier, onClick: (Medium) -> Unit ) { - airing.media?.let(::Medium)?.let { media -> - val updater = SchemeTheme.create(media.id) + val updater = SchemeTheme.create(airing.medium.id) - Card( - modifier = modifier, - onClick = { - onClick(media) - } + Card( + modifier = modifier, + onClick = { + onClick(airing.medium) + } + ) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(8.dp) ) { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(8.dp) - ) { - AsyncImage( - modifier = Modifier.widthIn(min = 100.dp, max = 120.dp).fillMaxHeight().clip(MaterialTheme.shapes.medium), - model = media.coverImage.extraLarge, - contentDescription = media.preferred(titleLanguage), + AsyncImage( + modifier = Modifier.widthIn(min = 100.dp, max = 120.dp).fillMaxHeight().clip(MaterialTheme.shapes.medium), + model = airing.medium.coverImage.extraLarge, + contentDescription = airing.medium.preferred(titleLanguage), + contentScale = ContentScale.Crop, + error = rememberAsyncImagePainter( + model = airing.medium.coverImage.large, contentScale = ContentScale.Crop, error = rememberAsyncImagePainter( - model = media.coverImage.large, + model = airing.medium.coverImage.medium, contentScale = ContentScale.Crop, - error = rememberAsyncImagePainter( - model = media.coverImage.medium, - contentScale = ContentScale.Crop, - onSuccess = { state -> - updater?.update(state.painter) - } - ), onSuccess = { state -> updater?.update(state.painter) } @@ -61,22 +57,25 @@ fun AiringCard( onSuccess = { state -> updater?.update(state.painter) } - ) - Column( - modifier = Modifier.weight(1F).padding(8.dp), - verticalArrangement = Arrangement.spacedBy(8.dp) - ) { - Text( - text = media.preferred(titleLanguage), - style = MaterialTheme.typography.titleLarge, - overflow = TextOverflow.Ellipsis, - softWrap = true, - modifier = Modifier.fillMaxWidth().weight(1F), - fontWeight = FontWeight.SemiBold - ) - Episode(airing.episode) - Airing(airing.airingAt) + ), + onSuccess = { state -> + updater?.update(state.painter) } + ) + Column( + modifier = Modifier.weight(1F).padding(8.dp), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + Text( + text = airing.medium.preferred(titleLanguage), + style = MaterialTheme.typography.titleLarge, + overflow = TextOverflow.Ellipsis, + softWrap = true, + modifier = Modifier.fillMaxWidth().weight(1F), + fontWeight = FontWeight.SemiBold + ) + Episode(airing.episode) + Airing(airing.airingAt) } } } diff --git a/composeApp/src/commonMain/moko-resources/base/strings.xml b/composeApp/src/commonMain/moko-resources/base/strings.xml index 81a136a..b30c6d2 100644 --- a/composeApp/src/commonMain/moko-resources/base/strings.xml +++ b/composeApp/src/commonMain/moko-resources/base/strings.xml @@ -118,4 +118,5 @@ Winter Recommendation This feature will be supported with Burning-Series version 6.0.0 and upwards. + You have nothing on your list, either change the filter or add any Anime/Manga. diff --git a/composeApp/src/commonMain/moko-resources/de-DE/strings.xml b/composeApp/src/commonMain/moko-resources/de-DE/strings.xml index e0585fb..074eb0b 100644 --- a/composeApp/src/commonMain/moko-resources/de-DE/strings.xml +++ b/composeApp/src/commonMain/moko-resources/de-DE/strings.xml @@ -118,4 +118,5 @@ Winter Empfehlung Diese Option wird mit Burning-Series Version 6.0.0 und höher unterstützt. + Du hast nichts auf deiner Liste, ändere den Filter oder füge einen Anime/Manga hinzu.