Last commit merged: 8ff0c9d61a
This commit is contained in:
LuftVerbot 2023-11-17 19:47:18 +01:00
parent 370212c677
commit a439045319
112 changed files with 758 additions and 867 deletions

View file

@ -72,9 +72,9 @@ class SetReadStatus(
suspend fun await(manga: Manga, read: Boolean) = suspend fun await(manga: Manga, read: Boolean) =
await(manga.id, read) await(manga.id, read)
sealed class Result { sealed interface Result {
data object Success : Result() data object Success : Result
data object NoChapters : Result() data object NoChapters : Result
data class InternalError(val error: Throwable) : Result() data class InternalError(val error: Throwable) : Result
} }
} }

View file

@ -72,9 +72,9 @@ class SetSeenStatus(
suspend fun await(anime: Anime, seen: Boolean) = suspend fun await(anime: Anime, seen: Boolean) =
await(anime.id, seen) await(anime.id, seen)
sealed class Result { sealed interface Result {
data object Success : Result() data object Success : Result
data object NoEpisodes : Result() data object NoEpisodes : Result
data class InternalError(val error: Throwable) : Result() data class InternalError(val error: Throwable) : Result
} }
} }

View file

@ -22,12 +22,14 @@ import androidx.compose.material.icons.outlined.HelpOutline
import androidx.compose.material.icons.outlined.History import androidx.compose.material.icons.outlined.History
import androidx.compose.material.icons.outlined.Settings import androidx.compose.material.icons.outlined.Settings
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Switch import androidx.compose.material3.Switch
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.VerticalDivider
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@ -52,11 +54,9 @@ import eu.kanade.presentation.more.settings.widget.TrailingWidgetBuffer
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension
import eu.kanade.tachiyomi.ui.browse.anime.extension.details.AnimeExtensionDetailsState import eu.kanade.tachiyomi.ui.browse.anime.extension.details.AnimeExtensionDetailsScreenModel
import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.LocaleHelper
import tachiyomi.presentation.core.components.ScrollbarLazyColumn import tachiyomi.presentation.core.components.ScrollbarLazyColumn
import tachiyomi.presentation.core.components.material.DIVIDER_ALPHA
import tachiyomi.presentation.core.components.material.Divider
import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.screens.EmptyScreen import tachiyomi.presentation.core.screens.EmptyScreen
@ -64,7 +64,7 @@ import tachiyomi.presentation.core.screens.EmptyScreen
@Composable @Composable
fun AnimeExtensionDetailsScreen( fun AnimeExtensionDetailsScreen(
navigateUp: () -> Unit, navigateUp: () -> Unit,
state: AnimeExtensionDetailsState, state: AnimeExtensionDetailsScreenModel.State,
onClickSourcePreferences: (sourceId: Long) -> Unit, onClickSourcePreferences: (sourceId: Long) -> Unit,
onClickWhatsNew: () -> Unit, onClickWhatsNew: () -> Unit,
onClickReadme: () -> Unit, onClickReadme: () -> Unit,
@ -313,7 +313,7 @@ private fun DetailsHeader(
} }
} }
Divider() HorizontalDivider()
} }
} }
@ -355,11 +355,8 @@ private fun InfoText(
@Composable @Composable
private fun InfoDivider() { private fun InfoDivider() {
Divider( VerticalDivider(
modifier = Modifier modifier = Modifier.height(20.dp),
.height(20.dp)
.width(1.dp),
color = MaterialTheme.colorScheme.onSurface.copy(alpha = DIVIDER_ALPHA),
) )
} }

View file

@ -43,7 +43,7 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.extension.InstallStep import eu.kanade.tachiyomi.extension.InstallStep
import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension
import eu.kanade.tachiyomi.ui.browse.anime.extension.AnimeExtensionUiModel import eu.kanade.tachiyomi.ui.browse.anime.extension.AnimeExtensionUiModel
import eu.kanade.tachiyomi.ui.browse.anime.extension.AnimeExtensionsState import eu.kanade.tachiyomi.ui.browse.anime.extension.AnimeExtensionsScreenModel
import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.LocaleHelper
import tachiyomi.presentation.core.components.FastScrollLazyColumn import tachiyomi.presentation.core.components.FastScrollLazyColumn
import tachiyomi.presentation.core.components.material.PullRefresh import tachiyomi.presentation.core.components.material.PullRefresh
@ -56,7 +56,7 @@ import tachiyomi.presentation.core.util.secondaryItemAlpha
@Composable @Composable
fun AnimeExtensionScreen( fun AnimeExtensionScreen(
state: AnimeExtensionsState, state: AnimeExtensionsScreenModel.State,
contentPadding: PaddingValues, contentPadding: PaddingValues,
searchQuery: String?, searchQuery: String?,
onLongClickItem: (AnimeExtension) -> Unit, onLongClickItem: (AnimeExtension) -> Unit,
@ -107,7 +107,7 @@ fun AnimeExtensionScreen(
@Composable @Composable
private fun AnimeExtensionContent( private fun AnimeExtensionContent(
state: AnimeExtensionsState, state: AnimeExtensionsScreenModel.State,
contentPadding: PaddingValues, contentPadding: PaddingValues,
onLongClickItem: (AnimeExtension) -> Unit, onLongClickItem: (AnimeExtension) -> Unit,
onClickItemCancel: (AnimeExtension) -> Unit, onClickItemCancel: (AnimeExtension) -> Unit,

View file

@ -12,7 +12,7 @@ import eu.kanade.presentation.browse.anime.components.BaseAnimeSourceItem
import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.more.settings.widget.SwitchPreferenceWidget import eu.kanade.presentation.more.settings.widget.SwitchPreferenceWidget
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.browse.anime.source.AnimeSourcesFilterState import eu.kanade.tachiyomi.ui.browse.anime.source.AnimeSourcesFilterScreenModel
import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.LocaleHelper
import tachiyomi.domain.source.anime.model.AnimeSource import tachiyomi.domain.source.anime.model.AnimeSource
import tachiyomi.presentation.core.components.FastScrollLazyColumn import tachiyomi.presentation.core.components.FastScrollLazyColumn
@ -22,7 +22,7 @@ import tachiyomi.presentation.core.screens.EmptyScreen
@Composable @Composable
fun AnimeSourcesFilterScreen( fun AnimeSourcesFilterScreen(
navigateUp: () -> Unit, navigateUp: () -> Unit,
state: AnimeSourcesFilterState.Success, state: AnimeSourcesFilterScreenModel.State.Success,
onClickLanguage: (String) -> Unit, onClickLanguage: (String) -> Unit,
onClickSource: (AnimeSource) -> Unit, onClickSource: (AnimeSource) -> Unit,
) { ) {
@ -54,7 +54,7 @@ fun AnimeSourcesFilterScreen(
@Composable @Composable
private fun AnimeSourcesFilterContent( private fun AnimeSourcesFilterContent(
contentPadding: PaddingValues, contentPadding: PaddingValues,
state: AnimeSourcesFilterState.Success, state: AnimeSourcesFilterScreenModel.State.Success,
onClickLanguage: (String) -> Unit, onClickLanguage: (String) -> Unit,
onClickSource: (AnimeSource) -> Unit, onClickSource: (AnimeSource) -> Unit,
) { ) {

View file

@ -192,7 +192,7 @@ fun AnimeSourceOptionsDialog(
) )
} }
sealed class AnimeSourceUiModel { sealed interface AnimeSourceUiModel {
data class Item(val source: AnimeSource) : AnimeSourceUiModel() data class Item(val source: AnimeSource) : AnimeSourceUiModel
data class Header(val language: String) : AnimeSourceUiModel() data class Header(val language: String) : AnimeSourceUiModel
} }

View file

@ -8,7 +8,7 @@ import androidx.compose.ui.Modifier
import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.entries.anime.components.BaseAnimeListItem import eu.kanade.presentation.entries.anime.components.BaseAnimeListItem
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.browse.anime.migration.anime.MigrateAnimeState import eu.kanade.tachiyomi.ui.browse.anime.migration.anime.MigrateAnimeScreenModel
import tachiyomi.domain.entries.anime.model.Anime import tachiyomi.domain.entries.anime.model.Anime
import tachiyomi.presentation.core.components.FastScrollLazyColumn import tachiyomi.presentation.core.components.FastScrollLazyColumn
import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
@ -18,7 +18,7 @@ import tachiyomi.presentation.core.screens.EmptyScreen
fun MigrateAnimeScreen( fun MigrateAnimeScreen(
navigateUp: () -> Unit, navigateUp: () -> Unit,
title: String?, title: String?,
state: MigrateAnimeState, state: MigrateAnimeScreenModel.State,
onClickItem: (Anime) -> Unit, onClickItem: (Anime) -> Unit,
onClickCover: (Anime) -> Unit, onClickCover: (Anime) -> Unit,
) { ) {
@ -51,7 +51,7 @@ fun MigrateAnimeScreen(
@Composable @Composable
private fun MigrateAnimeContent( private fun MigrateAnimeContent(
contentPadding: PaddingValues, contentPadding: PaddingValues,
state: MigrateAnimeState, state: MigrateAnimeScreenModel.State,
onClickItem: (Anime) -> Unit, onClickItem: (Anime) -> Unit,
onClickCover: (Anime) -> Unit, onClickCover: (Anime) -> Unit,
) { ) {

View file

@ -26,7 +26,7 @@ import eu.kanade.domain.source.service.SetMigrateSorting
import eu.kanade.presentation.browse.anime.components.AnimeSourceIcon import eu.kanade.presentation.browse.anime.components.AnimeSourceIcon
import eu.kanade.presentation.browse.anime.components.BaseAnimeSourceItem import eu.kanade.presentation.browse.anime.components.BaseAnimeSourceItem
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.browse.anime.migration.sources.MigrateAnimeSourceState import eu.kanade.tachiyomi.ui.browse.anime.migration.sources.MigrateAnimeSourceScreenModel
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import tachiyomi.domain.source.anime.model.AnimeSource import tachiyomi.domain.source.anime.model.AnimeSource
import tachiyomi.presentation.core.components.Badge import tachiyomi.presentation.core.components.Badge
@ -43,7 +43,7 @@ import tachiyomi.presentation.core.util.secondaryItemAlpha
@Composable @Composable
fun MigrateAnimeSourceScreen( fun MigrateAnimeSourceScreen(
state: MigrateAnimeSourceState, state: MigrateAnimeSourceScreenModel.State,
contentPadding: PaddingValues, contentPadding: PaddingValues,
onClickItem: (AnimeSource) -> Unit, onClickItem: (AnimeSource) -> Unit,
onToggleSortingDirection: () -> Unit, onToggleSortingDirection: () -> Unit,

View file

@ -14,14 +14,15 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.DoneAll import androidx.compose.material.icons.outlined.DoneAll
import androidx.compose.material.icons.outlined.FilterList import androidx.compose.material.icons.outlined.FilterList
import androidx.compose.material.icons.outlined.PushPin import androidx.compose.material.icons.outlined.PushPin
import androidx.compose.material3.Divider
import androidx.compose.material3.FilterChip import androidx.compose.material3.FilterChip
import androidx.compose.material3.FilterChipDefaults import androidx.compose.material3.FilterChipDefaults
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.material3.VerticalDivider
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -29,7 +30,6 @@ import androidx.compose.ui.res.stringResource
import eu.kanade.presentation.components.SearchToolbar import eu.kanade.presentation.components.SearchToolbar
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.browse.anime.source.globalsearch.AnimeSourceFilter import eu.kanade.tachiyomi.ui.browse.anime.source.globalsearch.AnimeSourceFilter
import tachiyomi.presentation.core.components.material.VerticalDivider
import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.components.material.padding
@Composable @Composable
@ -123,6 +123,6 @@ fun GlobalAnimeSearchToolbar(
) )
} }
Divider() HorizontalDivider()
} }
} }

View file

@ -23,6 +23,7 @@ import androidx.compose.material.icons.outlined.History
import androidx.compose.material.icons.outlined.Settings import androidx.compose.material.icons.outlined.Settings
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
@ -30,6 +31,7 @@ import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Switch import androidx.compose.material3.Switch
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.material3.VerticalDivider
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@ -53,11 +55,9 @@ import eu.kanade.presentation.more.settings.widget.TrailingWidgetBuffer
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.extension.manga.model.MangaExtension import eu.kanade.tachiyomi.extension.manga.model.MangaExtension
import eu.kanade.tachiyomi.source.ConfigurableSource import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.ui.browse.manga.extension.details.MangaExtensionDetailsState import eu.kanade.tachiyomi.ui.browse.manga.extension.details.MangaExtensionDetailsScreenModel
import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.LocaleHelper
import tachiyomi.presentation.core.components.ScrollbarLazyColumn import tachiyomi.presentation.core.components.ScrollbarLazyColumn
import tachiyomi.presentation.core.components.material.DIVIDER_ALPHA
import tachiyomi.presentation.core.components.material.Divider
import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.screens.EmptyScreen import tachiyomi.presentation.core.screens.EmptyScreen
@ -65,7 +65,7 @@ import tachiyomi.presentation.core.screens.EmptyScreen
@Composable @Composable
fun ExtensionDetailsScreen( fun ExtensionDetailsScreen(
navigateUp: () -> Unit, navigateUp: () -> Unit,
state: MangaExtensionDetailsState, state: MangaExtensionDetailsScreenModel.State,
onClickSourcePreferences: (sourceId: Long) -> Unit, onClickSourcePreferences: (sourceId: Long) -> Unit,
onClickWhatsNew: () -> Unit, onClickWhatsNew: () -> Unit,
onClickReadme: () -> Unit, onClickReadme: () -> Unit,
@ -314,7 +314,7 @@ private fun DetailsHeader(
} }
} }
Divider() HorizontalDivider()
} }
} }
@ -356,11 +356,8 @@ private fun InfoText(
@Composable @Composable
private fun InfoDivider() { private fun InfoDivider() {
Divider( VerticalDivider(
modifier = Modifier modifier = Modifier.height(20.dp),
.height(20.dp)
.width(1.dp),
color = MaterialTheme.colorScheme.onSurface.copy(alpha = DIVIDER_ALPHA),
) )
} }

View file

@ -43,7 +43,7 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.extension.InstallStep import eu.kanade.tachiyomi.extension.InstallStep
import eu.kanade.tachiyomi.extension.manga.model.MangaExtension import eu.kanade.tachiyomi.extension.manga.model.MangaExtension
import eu.kanade.tachiyomi.ui.browse.manga.extension.MangaExtensionUiModel import eu.kanade.tachiyomi.ui.browse.manga.extension.MangaExtensionUiModel
import eu.kanade.tachiyomi.ui.browse.manga.extension.MangaExtensionsState import eu.kanade.tachiyomi.ui.browse.manga.extension.MangaExtensionsScreenModel
import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.LocaleHelper
import tachiyomi.presentation.core.components.FastScrollLazyColumn import tachiyomi.presentation.core.components.FastScrollLazyColumn
import tachiyomi.presentation.core.components.material.PullRefresh import tachiyomi.presentation.core.components.material.PullRefresh
@ -57,7 +57,7 @@ import tachiyomi.presentation.core.util.secondaryItemAlpha
@Composable @Composable
fun MangaExtensionScreen( fun MangaExtensionScreen(
state: MangaExtensionsState, state: MangaExtensionsScreenModel.State,
contentPadding: PaddingValues, contentPadding: PaddingValues,
searchQuery: String?, searchQuery: String?,
onLongClickItem: (MangaExtension) -> Unit, onLongClickItem: (MangaExtension) -> Unit,
@ -108,7 +108,7 @@ fun MangaExtensionScreen(
@Composable @Composable
private fun ExtensionContent( private fun ExtensionContent(
state: MangaExtensionsState, state: MangaExtensionsScreenModel.State,
contentPadding: PaddingValues, contentPadding: PaddingValues,
onLongClickItem: (MangaExtension) -> Unit, onLongClickItem: (MangaExtension) -> Unit,
onClickItemCancel: (MangaExtension) -> Unit, onClickItemCancel: (MangaExtension) -> Unit,

View file

@ -12,7 +12,7 @@ import eu.kanade.presentation.browse.manga.components.BaseMangaSourceItem
import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.more.settings.widget.SwitchPreferenceWidget import eu.kanade.presentation.more.settings.widget.SwitchPreferenceWidget
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.browse.manga.source.MangaSourcesFilterState import eu.kanade.tachiyomi.ui.browse.manga.source.SourcesFilterScreenModel
import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.LocaleHelper
import tachiyomi.domain.source.manga.model.Source import tachiyomi.domain.source.manga.model.Source
import tachiyomi.presentation.core.components.FastScrollLazyColumn import tachiyomi.presentation.core.components.FastScrollLazyColumn
@ -22,7 +22,7 @@ import tachiyomi.presentation.core.screens.EmptyScreen
@Composable @Composable
fun MangaSourcesFilterScreen( fun MangaSourcesFilterScreen(
navigateUp: () -> Unit, navigateUp: () -> Unit,
state: MangaSourcesFilterState.Success, state: SourcesFilterScreenModel.State.Success,
onClickLanguage: (String) -> Unit, onClickLanguage: (String) -> Unit,
onClickSource: (Source) -> Unit, onClickSource: (Source) -> Unit,
) { ) {
@ -54,7 +54,7 @@ fun MangaSourcesFilterScreen(
@Composable @Composable
private fun SourcesFilterContent( private fun SourcesFilterContent(
contentPadding: PaddingValues, contentPadding: PaddingValues,
state: MangaSourcesFilterState.Success, state: SourcesFilterScreenModel.State.Success,
onClickLanguage: (String) -> Unit, onClickLanguage: (String) -> Unit,
onClickSource: (Source) -> Unit, onClickSource: (Source) -> Unit,
) { ) {

View file

@ -210,7 +210,7 @@ fun MangaSourceOptionsDialog(
) )
} }
sealed class MangaSourceUiModel { sealed interface MangaSourceUiModel {
data class Item(val source: Source) : MangaSourceUiModel() data class Item(val source: Source) : MangaSourceUiModel
data class Header(val language: String) : MangaSourceUiModel() data class Header(val language: String) : MangaSourceUiModel
} }

View file

@ -8,7 +8,7 @@ import androidx.compose.ui.Modifier
import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.entries.manga.components.BaseMangaListItem import eu.kanade.presentation.entries.manga.components.BaseMangaListItem
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.browse.manga.migration.manga.MigrateMangaState import eu.kanade.tachiyomi.ui.browse.manga.migration.manga.MigrateMangaScreenModel
import tachiyomi.domain.entries.manga.model.Manga import tachiyomi.domain.entries.manga.model.Manga
import tachiyomi.presentation.core.components.FastScrollLazyColumn import tachiyomi.presentation.core.components.FastScrollLazyColumn
import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
@ -18,7 +18,7 @@ import tachiyomi.presentation.core.screens.EmptyScreen
fun MigrateMangaScreen( fun MigrateMangaScreen(
navigateUp: () -> Unit, navigateUp: () -> Unit,
title: String?, title: String?,
state: MigrateMangaState, state: MigrateMangaScreenModel.State,
onClickItem: (Manga) -> Unit, onClickItem: (Manga) -> Unit,
onClickCover: (Manga) -> Unit, onClickCover: (Manga) -> Unit,
) { ) {
@ -51,7 +51,7 @@ fun MigrateMangaScreen(
@Composable @Composable
private fun MigrateMangaContent( private fun MigrateMangaContent(
contentPadding: PaddingValues, contentPadding: PaddingValues,
state: MigrateMangaState, state: MigrateMangaScreenModel.State,
onClickItem: (Manga) -> Unit, onClickItem: (Manga) -> Unit,
onClickCover: (Manga) -> Unit, onClickCover: (Manga) -> Unit,
) { ) {

View file

@ -26,7 +26,7 @@ import eu.kanade.domain.source.service.SetMigrateSorting
import eu.kanade.presentation.browse.manga.components.BaseMangaSourceItem import eu.kanade.presentation.browse.manga.components.BaseMangaSourceItem
import eu.kanade.presentation.browse.manga.components.MangaSourceIcon import eu.kanade.presentation.browse.manga.components.MangaSourceIcon
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.browse.manga.migration.sources.MigrateMangaSourceState import eu.kanade.tachiyomi.ui.browse.manga.migration.sources.MigrateSourceScreenModel
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import tachiyomi.domain.source.manga.model.Source import tachiyomi.domain.source.manga.model.Source
import tachiyomi.presentation.core.components.Badge import tachiyomi.presentation.core.components.Badge
@ -43,7 +43,7 @@ import tachiyomi.presentation.core.util.secondaryItemAlpha
@Composable @Composable
fun MigrateMangaSourceScreen( fun MigrateMangaSourceScreen(
state: MigrateMangaSourceState, state: MigrateSourceScreenModel.State,
contentPadding: PaddingValues, contentPadding: PaddingValues,
onClickItem: (Source) -> Unit, onClickItem: (Source) -> Unit,
onToggleSortingDirection: () -> Unit, onToggleSortingDirection: () -> Unit,

View file

@ -14,14 +14,15 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.DoneAll import androidx.compose.material.icons.outlined.DoneAll
import androidx.compose.material.icons.outlined.FilterList import androidx.compose.material.icons.outlined.FilterList
import androidx.compose.material.icons.outlined.PushPin import androidx.compose.material.icons.outlined.PushPin
import androidx.compose.material3.Divider
import androidx.compose.material3.FilterChip import androidx.compose.material3.FilterChip
import androidx.compose.material3.FilterChipDefaults import androidx.compose.material3.FilterChipDefaults
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.material3.VerticalDivider
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -29,7 +30,6 @@ import androidx.compose.ui.res.stringResource
import eu.kanade.presentation.components.SearchToolbar import eu.kanade.presentation.components.SearchToolbar
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.browse.manga.source.globalsearch.MangaSourceFilter import eu.kanade.tachiyomi.ui.browse.manga.source.globalsearch.MangaSourceFilter
import tachiyomi.presentation.core.components.material.VerticalDivider
import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.components.material.padding
@Composable @Composable
@ -123,6 +123,6 @@ fun GlobalMangaSearchToolbar(
) )
} }
Divider() HorizontalDivider()
} }
} }

View file

@ -10,6 +10,7 @@ import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
@ -31,7 +32,6 @@ import androidx.compose.ui.util.fastForEachIndexed
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import tachiyomi.presentation.core.components.HorizontalPager import tachiyomi.presentation.core.components.HorizontalPager
import tachiyomi.presentation.core.components.material.Divider
import tachiyomi.presentation.core.components.material.TabIndicator import tachiyomi.presentation.core.components.material.TabIndicator
object TabbedDialogPaddings { object TabbedDialogPaddings {
@ -87,7 +87,7 @@ fun TabbedDialog(
MoreMenu(onOverflowMenuClicked, tabOverflowMenuContent, overflowIcon) MoreMenu(onOverflowMenuClicked, tabOverflowMenuContent, overflowIcon)
} }
Divider() HorizontalDivider()
HorizontalPager( HorizontalPager(
modifier = Modifier.animateContentSize(), modifier = Modifier.animateContentSize(),

View file

@ -72,7 +72,7 @@ import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.data.download.anime.model.AnimeDownload import eu.kanade.tachiyomi.data.download.anime.model.AnimeDownload
import eu.kanade.tachiyomi.source.anime.getNameForAnimeInfo import eu.kanade.tachiyomi.source.anime.getNameForAnimeInfo
import eu.kanade.tachiyomi.ui.browse.anime.extension.details.SourcePreferencesScreen import eu.kanade.tachiyomi.ui.browse.anime.extension.details.SourcePreferencesScreen
import eu.kanade.tachiyomi.ui.entries.anime.AnimeScreenState import eu.kanade.tachiyomi.ui.entries.anime.AnimeScreenModel
import eu.kanade.tachiyomi.ui.entries.anime.EpisodeItem import eu.kanade.tachiyomi.ui.entries.anime.EpisodeItem
import eu.kanade.tachiyomi.util.lang.toRelativeString import eu.kanade.tachiyomi.util.lang.toRelativeString
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
@ -95,7 +95,7 @@ import java.util.concurrent.TimeUnit
@Composable @Composable
fun AnimeScreen( fun AnimeScreen(
state: AnimeScreenState.Success, state: AnimeScreenModel.State.Success,
snackbarHostState: SnackbarHostState, snackbarHostState: SnackbarHostState,
dateRelativeTime: Int, dateRelativeTime: Int,
dateFormat: DateFormat, dateFormat: DateFormat,
@ -240,7 +240,7 @@ fun AnimeScreen(
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
private fun AnimeScreenSmallImpl( private fun AnimeScreenSmallImpl(
state: AnimeScreenState.Success, state: AnimeScreenModel.State.Success,
snackbarHostState: SnackbarHostState, snackbarHostState: SnackbarHostState,
dateRelativeTime: Int, dateRelativeTime: Int,
dateFormat: DateFormat, dateFormat: DateFormat,
@ -506,7 +506,7 @@ private fun AnimeScreenSmallImpl(
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun AnimeScreenLargeImpl( fun AnimeScreenLargeImpl(
state: AnimeScreenState.Success, state: AnimeScreenModel.State.Success,
snackbarHostState: SnackbarHostState, snackbarHostState: SnackbarHostState,
dateRelativeTime: Int, dateRelativeTime: Int,
dateFormat: DateFormat, dateFormat: DateFormat,

View file

@ -68,7 +68,7 @@ import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.source.manga.getNameForMangaInfo import eu.kanade.tachiyomi.source.manga.getNameForMangaInfo
import eu.kanade.tachiyomi.ui.browse.manga.extension.details.MangaSourcePreferencesScreen import eu.kanade.tachiyomi.ui.browse.manga.extension.details.MangaSourcePreferencesScreen
import eu.kanade.tachiyomi.ui.entries.manga.ChapterItem import eu.kanade.tachiyomi.ui.entries.manga.ChapterItem
import eu.kanade.tachiyomi.ui.entries.manga.MangaScreenState import eu.kanade.tachiyomi.ui.entries.manga.MangaScreenModel
import eu.kanade.tachiyomi.util.lang.toRelativeString import eu.kanade.tachiyomi.util.lang.toRelativeString
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import tachiyomi.domain.entries.manga.model.Manga import tachiyomi.domain.entries.manga.model.Manga
@ -88,7 +88,7 @@ import java.util.Date
@Composable @Composable
fun MangaScreen( fun MangaScreen(
state: MangaScreenState.Success, state: MangaScreenModel.State.Success,
snackbarHostState: SnackbarHostState, snackbarHostState: SnackbarHostState,
dateRelativeTime: Int, dateRelativeTime: Int,
dateFormat: DateFormat, dateFormat: DateFormat,
@ -223,7 +223,7 @@ fun MangaScreen(
@Composable @Composable
private fun MangaScreenSmallImpl( private fun MangaScreenSmallImpl(
state: MangaScreenState.Success, state: MangaScreenModel.State.Success,
snackbarHostState: SnackbarHostState, snackbarHostState: SnackbarHostState,
dateRelativeTime: Int, dateRelativeTime: Int,
dateFormat: DateFormat, dateFormat: DateFormat,
@ -454,7 +454,7 @@ private fun MangaScreenSmallImpl(
@Composable @Composable
fun MangaScreenLargeImpl( fun MangaScreenLargeImpl(
state: MangaScreenState.Success, state: MangaScreenModel.State.Success,
snackbarHostState: SnackbarHostState, snackbarHostState: SnackbarHostState,
dateRelativeTime: Int, dateRelativeTime: Int,
dateFormat: DateFormat, dateFormat: DateFormat,

View file

@ -9,7 +9,6 @@ import androidx.compose.ui.Modifier
import eu.kanade.presentation.animehistory.components.AnimeHistoryContent import eu.kanade.presentation.animehistory.components.AnimeHistoryContent
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.history.anime.AnimeHistoryScreenModel import eu.kanade.tachiyomi.ui.history.anime.AnimeHistoryScreenModel
import eu.kanade.tachiyomi.ui.history.anime.AnimeHistoryState
import tachiyomi.domain.history.anime.model.AnimeHistoryWithRelations import tachiyomi.domain.history.anime.model.AnimeHistoryWithRelations
import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.screens.EmptyScreen import tachiyomi.presentation.core.screens.EmptyScreen
@ -18,7 +17,7 @@ import java.util.Date
@Composable @Composable
fun AnimeHistoryScreen( fun AnimeHistoryScreen(
state: AnimeHistoryState, state: AnimeHistoryScreenModel.State,
contentPadding: PaddingValues, contentPadding: PaddingValues,
searchQuery: String? = null, searchQuery: String? = null,
snackbarHostState: SnackbarHostState, snackbarHostState: SnackbarHostState,
@ -55,7 +54,7 @@ fun AnimeHistoryScreen(
} }
} }
sealed class AnimeHistoryUiModel { sealed interface AnimeHistoryUiModel {
data class Header(val date: Date) : AnimeHistoryUiModel() data class Header(val date: Date) : AnimeHistoryUiModel
data class Item(val item: AnimeHistoryWithRelations) : AnimeHistoryUiModel() data class Item(val item: AnimeHistoryWithRelations) : AnimeHistoryUiModel
} }

View file

@ -7,7 +7,6 @@ import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.history.manga.HistoryState
import eu.kanade.tachiyomi.ui.history.manga.MangaHistoryScreenModel import eu.kanade.tachiyomi.ui.history.manga.MangaHistoryScreenModel
import tachiyomi.domain.history.manga.model.MangaHistoryWithRelations import tachiyomi.domain.history.manga.model.MangaHistoryWithRelations
import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
@ -17,7 +16,7 @@ import java.util.Date
@Composable @Composable
fun MangaHistoryScreen( fun MangaHistoryScreen(
state: HistoryState, state: MangaHistoryScreenModel.State,
contentPadding: PaddingValues, contentPadding: PaddingValues,
searchQuery: String? = null, searchQuery: String? = null,
snackbarHostState: SnackbarHostState, snackbarHostState: SnackbarHostState,
@ -54,7 +53,7 @@ fun MangaHistoryScreen(
} }
} }
sealed class MangaHistoryUiModel { sealed interface MangaHistoryUiModel {
data class Header(val date: Date) : MangaHistoryUiModel() data class Header(val date: Date) : MangaHistoryUiModel
data class Item(val item: MangaHistoryWithRelations) : MangaHistoryUiModel() data class Item(val item: MangaHistoryWithRelations) : MangaHistoryUiModel
} }

View file

@ -2,6 +2,7 @@ package eu.kanade.presentation.library
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.pager.PagerState import androidx.compose.foundation.pager.PagerState
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ScrollableTabRow import androidx.compose.material3.ScrollableTabRow
import androidx.compose.material3.Tab import androidx.compose.material3.Tab
@ -9,7 +10,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import eu.kanade.presentation.category.visualName import eu.kanade.presentation.category.visualName
import tachiyomi.domain.category.model.Category import tachiyomi.domain.category.model.Category
import tachiyomi.presentation.core.components.material.Divider
import tachiyomi.presentation.core.components.material.TabIndicator import tachiyomi.presentation.core.components.material.TabIndicator
import tachiyomi.presentation.core.components.material.TabText import tachiyomi.presentation.core.components.material.TabText
@ -44,6 +44,6 @@ fun LibraryTabs(
} }
} }
Divider() HorizontalDivider()
} }
} }

View file

@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -12,7 +13,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import tachiyomi.presentation.core.components.material.Divider
@Composable @Composable
fun LogoHeader() { fun LogoHeader() {
@ -29,6 +29,6 @@ fun LogoHeader() {
.size(64.dp), .size(64.dp),
) )
Divider() HorizontalDivider()
} }
} }

View file

@ -20,6 +20,7 @@ import androidx.compose.material.icons.outlined.QueryStats
import androidx.compose.material.icons.outlined.Settings import androidx.compose.material.icons.outlined.Settings
import androidx.compose.material.icons.outlined.SettingsBackupRestore import androidx.compose.material.icons.outlined.SettingsBackupRestore
import androidx.compose.material.icons.outlined.Storage import androidx.compose.material.icons.outlined.Storage
import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
@ -35,7 +36,6 @@ import eu.kanade.tachiyomi.core.Constants
import eu.kanade.tachiyomi.ui.more.DownloadQueueState import eu.kanade.tachiyomi.ui.more.DownloadQueueState
import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.presentation.core.components.ScrollbarLazyColumn import tachiyomi.presentation.core.components.ScrollbarLazyColumn
import tachiyomi.presentation.core.components.material.Divider
import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
@ -101,7 +101,7 @@ fun MoreScreen(
) )
} }
item { Divider() } item { HorizontalDivider() }
val libraryPreferences: LibraryPreferences by injectLazy() val libraryPreferences: LibraryPreferences by injectLazy()
@ -182,7 +182,7 @@ fun MoreScreen(
) )
} }
item { Divider() } item { HorizontalDivider() }
item { item {
TextPreferenceWidget( TextPreferenceWidget(

View file

@ -13,8 +13,6 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.structuralEqualityPolicy import androidx.compose.runtime.structuralEqualityPolicy
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import eu.kanade.domain.track.service.TrackPreferences import eu.kanade.domain.track.service.TrackPreferences
import eu.kanade.domain.ui.UiPreferences
import eu.kanade.presentation.more.settings.widget.AppThemePreferenceWidget
import eu.kanade.presentation.more.settings.widget.EditTextPreferenceWidget import eu.kanade.presentation.more.settings.widget.EditTextPreferenceWidget
import eu.kanade.presentation.more.settings.widget.InfoWidget import eu.kanade.presentation.more.settings.widget.InfoWidget
import eu.kanade.presentation.more.settings.widget.ListPreferenceWidget import eu.kanade.presentation.more.settings.widget.ListPreferenceWidget
@ -173,16 +171,6 @@ internal fun PreferenceItem(
singleLine = false, singleLine = false,
) )
} }
is Preference.PreferenceItem.AppThemePreference -> {
val value by item.pref.collectAsState()
val amoled by Injekt.get<UiPreferences>().themeDarkAmoled().collectAsState()
AppThemePreferenceWidget(
title = item.title,
value = value,
amoled = amoled,
onItemClick = { scope.launch { item.pref.set(it) } },
)
}
is Preference.PreferenceItem.TrackingPreference -> { is Preference.PreferenceItem.TrackingPreference -> {
val uName by Injekt.get<PreferenceStore>() val uName by Injekt.get<PreferenceStore>()
.getString(TrackPreferences.trackUsername(item.service.id)) .getString(TrackPreferences.trackUsername(item.service.id))
@ -198,6 +186,9 @@ internal fun PreferenceItem(
is Preference.PreferenceItem.InfoPreference -> { is Preference.PreferenceItem.InfoPreference -> {
InfoWidget(text = item.title) InfoWidget(text = item.title)
} }
is Preference.PreferenceItem.CustomPreference -> {
item.content(item)
}
} }
} }
} }

View file

@ -4,7 +4,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import eu.kanade.domain.ui.model.AppTheme
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.track.TrackService import eu.kanade.tachiyomi.data.track.TrackService
import tachiyomi.core.preference.Preference as PreferenceData import tachiyomi.core.preference.Preference as PreferenceData
@ -144,19 +143,6 @@ sealed class Preference {
override val onValueChanged: suspend (newValue: String) -> Boolean = { true }, override val onValueChanged: suspend (newValue: String) -> Boolean = { true },
) : PreferenceItem<String>() ) : PreferenceItem<String>()
/**
* A [PreferenceItem] that shows previews of [AppTheme] selection.
*/
data class AppThemePreference(
val pref: PreferenceData<AppTheme>,
override val title: String,
) : PreferenceItem<AppTheme>() {
override val enabled: Boolean = true
override val subtitle: String? = null
override val icon: ImageVector? = null
override val onValueChanged: suspend (newValue: AppTheme) -> Boolean = { true }
}
/** /**
* A [PreferenceItem] for individual tracking service. * A [PreferenceItem] for individual tracking service.
*/ */
@ -180,6 +166,16 @@ sealed class Preference {
override val icon: ImageVector? = null override val icon: ImageVector? = null
override val onValueChanged: suspend (newValue: String) -> Boolean = { true } override val onValueChanged: suspend (newValue: String) -> Boolean = { true }
} }
data class CustomPreference(
override val title: String,
val content: @Composable (PreferenceItem<String>) -> Unit,
) : PreferenceItem<String>() {
override val enabled: Boolean = true
override val subtitle: String? = null
override val icon: ImageVector? = null
override val onValueChanged: suspend (newValue: String) -> Boolean = { true }
}
} }
data class PreferenceGroup( data class PreferenceGroup(

View file

@ -21,6 +21,7 @@ import eu.kanade.domain.ui.model.TabletUiMode
import eu.kanade.domain.ui.model.ThemeMode import eu.kanade.domain.ui.model.ThemeMode
import eu.kanade.domain.ui.model.setAppCompatDelegateThemeMode import eu.kanade.domain.ui.model.setAppCompatDelegateThemeMode
import eu.kanade.presentation.more.settings.Preference import eu.kanade.presentation.more.settings.Preference
import eu.kanade.presentation.more.settings.widget.AppThemePreferenceWidget
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.home.HomeScreen import eu.kanade.tachiyomi.ui.home.HomeScreen
import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.LocaleHelper
@ -61,8 +62,11 @@ object SettingsAppearanceScreen : SearchableSettings {
): Preference.PreferenceGroup { ): Preference.PreferenceGroup {
val themeModePref = uiPreferences.themeMode() val themeModePref = uiPreferences.themeMode()
val themeMode by themeModePref.collectAsState() val themeMode by themeModePref.collectAsState()
val appThemePref = uiPreferences.appTheme() val appThemePref = uiPreferences.appTheme()
val amoledPref = uiPreferences.themeDarkAmoled() val amoledPref = uiPreferences.themeDarkAmoled()
val amoled by amoledPref.collectAsState()
LaunchedEffect(themeMode) { LaunchedEffect(themeMode) {
setAppCompatDelegateThemeMode(themeMode) setAppCompatDelegateThemeMode(themeMode)
@ -93,10 +97,17 @@ object SettingsAppearanceScreen : SearchableSettings {
) )
}, },
), ),
Preference.PreferenceItem.AppThemePreference( Preference.PreferenceItem.CustomPreference(
title = stringResource(R.string.pref_app_theme), title = stringResource(R.string.pref_app_theme),
pref = appThemePref, ) { item ->
), val value by appThemePref.collectAsState()
AppThemePreferenceWidget(
title = item.title,
value = value,
amoled = amoled,
onItemClick = { appThemePref.set(it) },
)
},
Preference.PreferenceItem.SwitchPreference( Preference.PreferenceItem.SwitchPreference(
pref = amoledPref, pref = amoledPref,
title = stringResource(R.string.pref_dark_theme_pure_black), title = stringResource(R.string.pref_dark_theme_pure_black),

View file

@ -20,6 +20,7 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Checkbox import androidx.compose.material3.Checkbox
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
@ -62,7 +63,6 @@ import tachiyomi.domain.backup.service.FLAG_HISTORY
import tachiyomi.domain.backup.service.FLAG_SETTINGS import tachiyomi.domain.backup.service.FLAG_SETTINGS
import tachiyomi.domain.backup.service.FLAG_TRACK import tachiyomi.domain.backup.service.FLAG_TRACK
import tachiyomi.presentation.core.components.ScrollbarLazyColumn import tachiyomi.presentation.core.components.ScrollbarLazyColumn
import tachiyomi.presentation.core.components.material.Divider
import tachiyomi.presentation.core.util.collectAsState import tachiyomi.presentation.core.util.collectAsState
import tachiyomi.presentation.core.util.isScrolledToEnd import tachiyomi.presentation.core.util.isScrolledToEnd
import tachiyomi.presentation.core.util.isScrolledToStart import tachiyomi.presentation.core.util.isScrolledToStart
@ -197,8 +197,8 @@ object SettingsBackupScreen : SearchableSettings {
} }
} }
} }
if (!state.isScrolledToStart()) Divider(modifier = Modifier.align(Alignment.TopCenter)) if (!state.isScrolledToStart()) HorizontalDivider(modifier = Modifier.align(Alignment.TopCenter))
if (!state.isScrolledToEnd()) Divider(modifier = Modifier.align(Alignment.BottomCenter)) if (!state.isScrolledToEnd()) HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter))
} }
}, },
dismissButton = { dismissButton = {

View file

@ -17,6 +17,7 @@ import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Close import androidx.compose.material.icons.outlined.Close
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
@ -53,7 +54,6 @@ import eu.kanade.presentation.components.UpIcon
import eu.kanade.presentation.more.settings.Preference import eu.kanade.presentation.more.settings.Preference
import eu.kanade.presentation.util.Screen import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import tachiyomi.presentation.core.components.material.Divider
import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.screens.EmptyScreen import tachiyomi.presentation.core.screens.EmptyScreen
import tachiyomi.presentation.core.util.runOnEnterKeyPressed import tachiyomi.presentation.core.util.runOnEnterKeyPressed
@ -138,7 +138,7 @@ class SettingsSearchScreen : Screen() {
} }
}, },
) )
Divider() HorizontalDivider()
} }
}, },
) { contentPadding -> ) { contentPadding ->

View file

@ -15,10 +15,12 @@ import androidx.compose.material.icons.outlined.SelectAll
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.Checkbox import androidx.compose.material3.Checkbox
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
@ -48,7 +50,6 @@ import tachiyomi.domain.source.anime.interactor.GetAnimeSourcesWithNonLibraryAni
import tachiyomi.domain.source.anime.model.AnimeSource import tachiyomi.domain.source.anime.model.AnimeSource
import tachiyomi.domain.source.anime.model.AnimeSourceWithCount import tachiyomi.domain.source.anime.model.AnimeSourceWithCount
import tachiyomi.mi.data.AnimeDatabase import tachiyomi.mi.data.AnimeDatabase
import tachiyomi.presentation.core.components.material.Divider
import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.screens.EmptyScreen import tachiyomi.presentation.core.screens.EmptyScreen
import tachiyomi.presentation.core.screens.LoadingScreen import tachiyomi.presentation.core.screens.LoadingScreen
@ -148,7 +149,7 @@ class ClearAnimeDatabaseScreen : Screen() {
} }
} }
Divider() HorizontalDivider()
Button( Button(
modifier = Modifier modifier = Modifier
@ -269,12 +270,15 @@ private class ClearAnimeDatabaseScreenModel : StateScreenModel<ClearAnimeDatabas
state.copy(showConfirmation = false) state.copy(showConfirmation = false)
} }
sealed class State { sealed interface State {
data object Loading : State() @Immutable
data object Loading : State
@Immutable
data class Ready( data class Ready(
val items: List<AnimeSourceWithCount>, val items: List<AnimeSourceWithCount>,
val selection: List<Long> = emptyList(), val selection: List<Long> = emptyList(),
val showConfirmation: Boolean = false, val showConfirmation: Boolean = false,
) : State() ) : State
} }
} }

View file

@ -15,10 +15,12 @@ import androidx.compose.material.icons.outlined.SelectAll
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.Checkbox import androidx.compose.material3.Checkbox
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
@ -48,7 +50,6 @@ import tachiyomi.data.Database
import tachiyomi.domain.source.manga.interactor.GetMangaSourcesWithNonLibraryManga import tachiyomi.domain.source.manga.interactor.GetMangaSourcesWithNonLibraryManga
import tachiyomi.domain.source.manga.model.MangaSourceWithCount import tachiyomi.domain.source.manga.model.MangaSourceWithCount
import tachiyomi.domain.source.manga.model.Source import tachiyomi.domain.source.manga.model.Source
import tachiyomi.presentation.core.components.material.Divider
import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.screens.EmptyScreen import tachiyomi.presentation.core.screens.EmptyScreen
import tachiyomi.presentation.core.screens.LoadingScreen import tachiyomi.presentation.core.screens.LoadingScreen
@ -148,7 +149,7 @@ class ClearDatabaseScreen : Screen() {
} }
} }
Divider() HorizontalDivider()
Button( Button(
modifier = Modifier modifier = Modifier
@ -269,12 +270,15 @@ private class ClearDatabaseScreenModel : StateScreenModel<ClearDatabaseScreenMod
state.copy(showConfirmation = false) state.copy(showConfirmation = false)
} }
sealed class State { sealed interface State {
data object Loading : State() @Immutable
data object Loading : State
@Immutable
data class Ready( data class Ready(
val items: List<MangaSourceWithCount>, val items: List<MangaSourceWithCount>,
val selection: List<Long> = emptyList(), val selection: List<Long> = emptyList(),
val showConfirmation: Boolean = false, val showConfirmation: Boolean = false,
) : State() ) : State
} }
} }

View file

@ -22,6 +22,7 @@ import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.CheckCircle import androidx.compose.material.icons.filled.CheckCircle
import androidx.compose.material3.DividerDefaults
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
@ -44,7 +45,6 @@ import eu.kanade.presentation.theme.TachiyomiTheme
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.util.system.DeviceUtil import eu.kanade.tachiyomi.util.system.DeviceUtil
import eu.kanade.tachiyomi.util.system.isDynamicColorAvailable import eu.kanade.tachiyomi.util.system.isDynamicColorAvailable
import tachiyomi.presentation.core.components.material.DIVIDER_ALPHA
import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.util.ThemePreviews import tachiyomi.presentation.core.util.ThemePreviews
import tachiyomi.presentation.core.util.secondaryItemAlpha import tachiyomi.presentation.core.util.secondaryItemAlpha
@ -123,7 +123,6 @@ fun AppThemePreviewItem(
selected: Boolean, selected: Boolean,
onClick: () -> Unit, onClick: () -> Unit,
) { ) {
val dividerColor = MaterialTheme.colorScheme.onSurface.copy(alpha = DIVIDER_ALPHA)
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@ -133,7 +132,7 @@ fun AppThemePreviewItem(
color = if (selected) { color = if (selected) {
MaterialTheme.colorScheme.primary MaterialTheme.colorScheme.primary
} else { } else {
dividerColor DividerDefaults.color
}, },
shape = RoundedCornerShape(17.dp), shape = RoundedCornerShape(17.dp),
) )
@ -180,7 +179,7 @@ fun AppThemePreviewItem(
modifier = Modifier modifier = Modifier
.padding(start = 8.dp, top = 2.dp) .padding(start = 8.dp, top = 2.dp)
.background( .background(
color = dividerColor, color = DividerDefaults.color,
shape = MaterialTheme.shapes.small, shape = MaterialTheme.shapes.small,
) )
.fillMaxWidth(0.5f) .fillMaxWidth(0.5f)

View file

@ -8,6 +8,7 @@ import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.selection.selectable import androidx.compose.foundation.selection.selectable
import androidx.compose.material.minimumInteractiveComponentSize import androidx.compose.material.minimumInteractiveComponentSize
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton import androidx.compose.material3.RadioButton
import androidx.compose.material3.Text import androidx.compose.material3.Text
@ -25,7 +26,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import tachiyomi.presentation.core.components.ScrollbarLazyColumn import tachiyomi.presentation.core.components.ScrollbarLazyColumn
import tachiyomi.presentation.core.components.material.Divider
import tachiyomi.presentation.core.util.isScrolledToEnd import tachiyomi.presentation.core.util.isScrolledToEnd
import tachiyomi.presentation.core.util.isScrolledToStart import tachiyomi.presentation.core.util.isScrolledToStart
@ -69,8 +69,8 @@ fun <T> ListPreferenceWidget(
} }
} }
} }
if (!state.isScrolledToStart()) Divider(modifier = Modifier.align(Alignment.TopCenter)) if (!state.isScrolledToStart()) HorizontalDivider(modifier = Modifier.align(Alignment.TopCenter))
if (!state.isScrolledToEnd()) Divider(modifier = Modifier.align(Alignment.BottomCenter)) if (!state.isScrolledToEnd()) HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter))
} }
}, },
confirmButton = { confirmButton = {

View file

@ -15,6 +15,7 @@ import androidx.compose.material.icons.rounded.CheckBox
import androidx.compose.material.icons.rounded.CheckBoxOutlineBlank import androidx.compose.material.icons.rounded.CheckBoxOutlineBlank
import androidx.compose.material.icons.rounded.DisabledByDefault import androidx.compose.material.icons.rounded.DisabledByDefault
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.LocalContentColor import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
@ -29,7 +30,6 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import tachiyomi.presentation.core.components.material.Divider
import tachiyomi.presentation.core.util.isScrolledToEnd import tachiyomi.presentation.core.util.isScrolledToEnd
import tachiyomi.presentation.core.util.isScrolledToStart import tachiyomi.presentation.core.util.isScrolledToStart
@ -115,8 +115,8 @@ fun <T> TriStateListDialog(
} }
} }
if (!listState.isScrolledToStart()) Divider(modifier = Modifier.align(Alignment.TopCenter)) if (!listState.isScrolledToStart()) HorizontalDivider(modifier = Modifier.align(Alignment.TopCenter))
if (!listState.isScrolledToEnd()) Divider(modifier = Modifier.align(Alignment.BottomCenter)) if (!listState.isScrolledToEnd()) HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter))
} }
} }
}, },

View file

@ -3,9 +3,9 @@ package eu.kanade.presentation.more.stats
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import eu.kanade.presentation.more.stats.data.StatsData import eu.kanade.presentation.more.stats.data.StatsData
sealed class StatsScreenState { sealed interface StatsScreenState {
@Immutable @Immutable
data object Loading : StatsScreenState() data object Loading : StatsScreenState
@Immutable @Immutable
data class SuccessManga( data class SuccessManga(
@ -13,7 +13,7 @@ sealed class StatsScreenState {
val titles: StatsData.MangaTitles, val titles: StatsData.MangaTitles,
val chapters: StatsData.Chapters, val chapters: StatsData.Chapters,
val trackers: StatsData.Trackers, val trackers: StatsData.Trackers,
) : StatsScreenState() ) : StatsScreenState
@Immutable @Immutable
data class SuccessAnime( data class SuccessAnime(
@ -21,5 +21,5 @@ sealed class StatsScreenState {
val titles: StatsData.AnimeTitles, val titles: StatsData.AnimeTitles,
val episodes: StatsData.Episodes, val episodes: StatsData.Episodes,
val trackers: StatsData.Trackers, val trackers: StatsData.Trackers,
) : StatsScreenState() ) : StatsScreenState
} }

View file

@ -1,46 +1,46 @@
package eu.kanade.presentation.more.stats.data package eu.kanade.presentation.more.stats.data
sealed class StatsData { sealed interface StatsData {
data class MangaOverview( data class MangaOverview(
val libraryMangaCount: Int, val libraryMangaCount: Int,
val completedMangaCount: Int, val completedMangaCount: Int,
val totalReadDuration: Long, val totalReadDuration: Long,
) : StatsData() ) : StatsData
data class AnimeOverview( data class AnimeOverview(
val libraryAnimeCount: Int, val libraryAnimeCount: Int,
val completedAnimeCount: Int, val completedAnimeCount: Int,
val totalSeenDuration: Long, val totalSeenDuration: Long,
) : StatsData() ) : StatsData
data class MangaTitles( data class MangaTitles(
val globalUpdateItemCount: Int, val globalUpdateItemCount: Int,
val startedMangaCount: Int, val startedMangaCount: Int,
val localMangaCount: Int, val localMangaCount: Int,
) : StatsData() ) : StatsData
data class AnimeTitles( data class AnimeTitles(
val globalUpdateItemCount: Int, val globalUpdateItemCount: Int,
val startedAnimeCount: Int, val startedAnimeCount: Int,
val localAnimeCount: Int, val localAnimeCount: Int,
) : StatsData() ) : StatsData
data class Chapters( data class Chapters(
val totalChapterCount: Int, val totalChapterCount: Int,
val readChapterCount: Int, val readChapterCount: Int,
val downloadCount: Int, val downloadCount: Int,
) : StatsData() ) : StatsData
data class Episodes( data class Episodes(
val totalEpisodeCount: Int, val totalEpisodeCount: Int,
val readEpisodeCount: Int, val readEpisodeCount: Int,
val downloadCount: Int, val downloadCount: Int,
) : StatsData() ) : StatsData
data class Trackers( data class Trackers(
val trackedTitleCount: Int, val trackedTitleCount: Int,
val meanScore: Double, val meanScore: Double,
val trackerCount: Int, val trackerCount: Int,
) : StatsData() ) : StatsData
} }

View file

@ -17,6 +17,7 @@ import androidx.compose.foundation.selection.selectable
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.minimumInteractiveComponentSize import androidx.compose.material.minimumInteractiveComponentSize
import androidx.compose.material3.DatePicker import androidx.compose.material3.DatePicker
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton import androidx.compose.material3.RadioButton
import androidx.compose.material3.SelectableDates import androidx.compose.material3.SelectableDates
@ -36,7 +37,6 @@ import tachiyomi.presentation.core.components.ScrollbarLazyColumn
import tachiyomi.presentation.core.components.WheelNumberPicker import tachiyomi.presentation.core.components.WheelNumberPicker
import tachiyomi.presentation.core.components.WheelTextPicker import tachiyomi.presentation.core.components.WheelTextPicker
import tachiyomi.presentation.core.components.material.AlertDialogContent import tachiyomi.presentation.core.components.material.AlertDialogContent
import tachiyomi.presentation.core.components.material.Divider
import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.util.isScrolledToEnd import tachiyomi.presentation.core.util.isScrolledToEnd
import tachiyomi.presentation.core.util.isScrolledToStart import tachiyomi.presentation.core.util.isScrolledToStart
@ -81,8 +81,8 @@ fun TrackStatusSelector(
} }
} }
} }
if (!state.isScrolledToStart()) Divider(modifier = Modifier.align(Alignment.TopCenter)) if (!state.isScrolledToStart()) HorizontalDivider(modifier = Modifier.align(Alignment.TopCenter))
if (!state.isScrolledToEnd()) Divider(modifier = Modifier.align(Alignment.BottomCenter)) if (!state.isScrolledToEnd()) HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter))
}, },
onConfirm = onConfirm, onConfirm = onConfirm,
onDismissRequest = onDismissRequest, onDismissRequest = onDismissRequest,

View file

@ -18,9 +18,11 @@ import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.material3.VerticalDivider
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
@ -39,8 +41,6 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.track.TrackService import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.ui.entries.anime.track.AnimeTrackItem import eu.kanade.tachiyomi.ui.entries.anime.track.AnimeTrackItem
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import tachiyomi.presentation.core.components.material.Divider
import tachiyomi.presentation.core.components.material.VerticalDivider
import java.text.DateFormat import java.text.DateFormat
private const val UnsetStatusTextAlpha = 0.5F private const val UnsetStatusTextAlpha = 0.5F
@ -200,7 +200,7 @@ private fun TrackInfoItem(
} }
if (onStartDateClick != null && onEndDateClick != null) { if (onStartDateClick != null && onEndDateClick != null) {
Divider() HorizontalDivider()
Row(modifier = Modifier.height(IntrinsicSize.Min)) { Row(modifier = Modifier.height(IntrinsicSize.Min)) {
TrackDetailsItem( TrackDetailsItem(
modifier = Modifier.weight(1F), modifier = Modifier.weight(1F),

View file

@ -22,6 +22,7 @@ import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
@ -45,7 +46,6 @@ import eu.kanade.presentation.track.manga.SearchResultItem
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.track.model.AnimeTrackSearch import eu.kanade.tachiyomi.data.track.model.AnimeTrackSearch
import tachiyomi.presentation.core.components.ScrollbarLazyColumn import tachiyomi.presentation.core.components.ScrollbarLazyColumn
import tachiyomi.presentation.core.components.material.Divider
import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.screens.EmptyScreen import tachiyomi.presentation.core.screens.EmptyScreen
import tachiyomi.presentation.core.screens.LoadingScreen import tachiyomi.presentation.core.screens.LoadingScreen
@ -126,7 +126,7 @@ fun AnimeTrackServiceSearch(
} }
}, },
) )
Divider() HorizontalDivider()
} }
}, },
bottomBar = { bottomBar = {

View file

@ -24,11 +24,13 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.material3.VerticalDivider
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@ -50,8 +52,6 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.track.TrackService import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.ui.entries.manga.track.MangaTrackItem import eu.kanade.tachiyomi.ui.entries.manga.track.MangaTrackItem
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import tachiyomi.presentation.core.components.material.Divider
import tachiyomi.presentation.core.components.material.VerticalDivider
import java.text.DateFormat import java.text.DateFormat
private const val UnsetStatusTextAlpha = 0.5F private const val UnsetStatusTextAlpha = 0.5F
@ -211,7 +211,7 @@ private fun TrackInfoItem(
} }
if (onStartDateClick != null && onEndDateClick != null) { if (onStartDateClick != null && onEndDateClick != null) {
Divider() HorizontalDivider()
Row(modifier = Modifier.height(IntrinsicSize.Min)) { Row(modifier = Modifier.height(IntrinsicSize.Min)) {
TrackDetailsItem( TrackDetailsItem(
modifier = Modifier.weight(1F), modifier = Modifier.weight(1F),

View file

@ -33,6 +33,7 @@ import androidx.compose.material.icons.filled.CheckCircle
import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
@ -60,7 +61,6 @@ import eu.kanade.presentation.entries.ItemCover
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.track.model.MangaTrackSearch import eu.kanade.tachiyomi.data.track.model.MangaTrackSearch
import tachiyomi.presentation.core.components.ScrollbarLazyColumn import tachiyomi.presentation.core.components.ScrollbarLazyColumn
import tachiyomi.presentation.core.components.material.Divider
import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.screens.EmptyScreen import tachiyomi.presentation.core.screens.EmptyScreen
@ -143,7 +143,7 @@ fun MangaTrackServiceSearch(
} }
}, },
) )
Divider() HorizontalDivider()
} }
}, },
bottomBar = { bottomBar = {

View file

@ -22,7 +22,7 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.download.anime.model.AnimeDownload import eu.kanade.tachiyomi.data.download.anime.model.AnimeDownload
import eu.kanade.tachiyomi.ui.player.settings.PlayerPreferences import eu.kanade.tachiyomi.ui.player.settings.PlayerPreferences
import eu.kanade.tachiyomi.ui.updates.anime.AnimeUpdatesItem import eu.kanade.tachiyomi.ui.updates.anime.AnimeUpdatesItem
import eu.kanade.tachiyomi.ui.updates.anime.AnimeUpdatesState import eu.kanade.tachiyomi.ui.updates.anime.AnimeUpdatesScreenModel
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import tachiyomi.presentation.core.components.FastScrollLazyColumn import tachiyomi.presentation.core.components.FastScrollLazyColumn
@ -36,7 +36,7 @@ import kotlin.time.Duration.Companion.seconds
@Composable @Composable
fun AnimeUpdateScreen( fun AnimeUpdateScreen(
state: AnimeUpdatesState, state: AnimeUpdatesScreenModel.State,
snackbarHostState: SnackbarHostState, snackbarHostState: SnackbarHostState,
contentPadding: PaddingValues, contentPadding: PaddingValues,
lastUpdated: Long, lastUpdated: Long,
@ -158,7 +158,7 @@ private fun AnimeUpdatesBottomBar(
) )
} }
sealed class AnimeUpdatesUiModel { sealed interface AnimeUpdatesUiModel {
data class Header(val date: String) : AnimeUpdatesUiModel() data class Header(val date: String) : AnimeUpdatesUiModel
data class Item(val item: AnimeUpdatesItem) : AnimeUpdatesUiModel() data class Item(val item: AnimeUpdatesItem) : AnimeUpdatesUiModel
} }

View file

@ -21,7 +21,7 @@ import eu.kanade.presentation.entries.manga.components.ChapterDownloadAction
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.download.manga.model.MangaDownload import eu.kanade.tachiyomi.data.download.manga.model.MangaDownload
import eu.kanade.tachiyomi.ui.updates.manga.MangaUpdatesItem import eu.kanade.tachiyomi.ui.updates.manga.MangaUpdatesItem
import eu.kanade.tachiyomi.ui.updates.manga.UpdatesState import eu.kanade.tachiyomi.ui.updates.manga.MangaUpdatesScreenModel
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import tachiyomi.presentation.core.components.FastScrollLazyColumn import tachiyomi.presentation.core.components.FastScrollLazyColumn
@ -33,7 +33,7 @@ import kotlin.time.Duration.Companion.seconds
@Composable @Composable
fun MangaUpdateScreen( fun MangaUpdateScreen(
state: UpdatesState, state: MangaUpdatesScreenModel.State,
snackbarHostState: SnackbarHostState, snackbarHostState: SnackbarHostState,
contentPadding: PaddingValues, contentPadding: PaddingValues,
lastUpdated: Long, lastUpdated: Long,
@ -147,7 +147,7 @@ private fun MangaUpdatesBottomBar(
) )
} }
sealed class MangaUpdatesUiModel { sealed interface MangaUpdatesUiModel {
data class Header(val date: String) : MangaUpdatesUiModel() data class Header(val date: String) : MangaUpdatesUiModel
data class Item(val item: MangaUpdatesItem) : MangaUpdatesUiModel() data class Item(val item: MangaUpdatesItem) : MangaUpdatesUiModel
} }

View file

@ -152,8 +152,8 @@ sealed class Image(
} }
} }
sealed class Location { sealed interface Location {
data class Pictures private constructor(val relativePath: String) : Location() { data class Pictures private constructor(val relativePath: String) : Location {
companion object { companion object {
fun create(relativePath: String = ""): Pictures { fun create(relativePath: String = ""): Pictures {
return Pictures(relativePath) return Pictures(relativePath)
@ -161,7 +161,7 @@ sealed class Location {
} }
} }
data object Cache : Location() data object Cache : Location
fun directory(context: Context): File { fun directory(context: Context): File {
return when (this) { return when (this) {

View file

@ -1,7 +1,7 @@
package eu.kanade.tachiyomi.extension.anime.model package eu.kanade.tachiyomi.extension.anime.model
sealed class AnimeLoadResult { sealed interface AnimeLoadResult {
data class Success(val extension: AnimeExtension.Installed) : AnimeLoadResult() data class Success(val extension: AnimeExtension.Installed) : AnimeLoadResult
data class Untrusted(val extension: AnimeExtension.Untrusted) : AnimeLoadResult() data class Untrusted(val extension: AnimeExtension.Untrusted) : AnimeLoadResult
data object Error : AnimeLoadResult() data object Error : AnimeLoadResult
} }

View file

@ -1,7 +1,7 @@
package eu.kanade.tachiyomi.extension.manga.model package eu.kanade.tachiyomi.extension.manga.model
sealed class MangaLoadResult { sealed interface MangaLoadResult {
data class Success(val extension: MangaExtension.Installed) : MangaLoadResult() data class Success(val extension: MangaExtension.Installed) : MangaLoadResult
data class Untrusted(val extension: MangaExtension.Untrusted) : MangaLoadResult() data class Untrusted(val extension: MangaExtension.Untrusted) : MangaLoadResult
data object Error : MangaLoadResult() data object Error : MangaLoadResult
} }

View file

@ -54,20 +54,20 @@ class AnimeExtensionFilterScreenModel(
} }
} }
sealed class AnimeExtensionFilterEvent { sealed interface AnimeExtensionFilterEvent {
data object FailedFetchingLanguages : AnimeExtensionFilterEvent() data object FailedFetchingLanguages : AnimeExtensionFilterEvent
} }
sealed class AnimeExtensionFilterState { sealed interface AnimeExtensionFilterState {
@Immutable @Immutable
data object Loading : AnimeExtensionFilterState() data object Loading : AnimeExtensionFilterState
@Immutable @Immutable
data class Success( data class Success(
val languages: List<String>, val languages: List<String>,
val enabledLanguages: Set<String> = emptySet(), val enabledLanguages: Set<String> = emptySet(),
) : AnimeExtensionFilterState() { ) : AnimeExtensionFilterState {
val isEmpty: Boolean val isEmpty: Boolean
get() = languages.isEmpty() get() = languages.isEmpty()

View file

@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.browse.anime.extension
import android.app.Application import android.app.Application
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.runtime.Immutable
import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.coroutineScope import cafe.adriel.voyager.core.model.coroutineScope
import eu.kanade.domain.extension.anime.interactor.GetAnimeExtensionsByType import eu.kanade.domain.extension.anime.interactor.GetAnimeExtensionsByType
@ -35,7 +36,7 @@ class AnimeExtensionsScreenModel(
preferences: SourcePreferences = Injekt.get(), preferences: SourcePreferences = Injekt.get(),
private val extensionManager: AnimeExtensionManager = Injekt.get(), private val extensionManager: AnimeExtensionManager = Injekt.get(),
private val getExtensions: GetAnimeExtensionsByType = Injekt.get(), private val getExtensions: GetAnimeExtensionsByType = Injekt.get(),
) : StateScreenModel<AnimeExtensionsState>(AnimeExtensionsState()) { ) : StateScreenModel<AnimeExtensionsScreenModel.State>(State()) {
private var _currentDownloads = MutableStateFlow<Map<String, InstallStep>>(hashMapOf()) private var _currentDownloads = MutableStateFlow<Map<String, InstallStep>>(hashMapOf())
@ -189,16 +190,17 @@ class AnimeExtensionsScreenModel(
fun trustSignature(signatureHash: String) { fun trustSignature(signatureHash: String) {
extensionManager.trustSignature(signatureHash) extensionManager.trustSignature(signatureHash)
} }
}
data class AnimeExtensionsState( @Immutable
val isLoading: Boolean = true, data class State(
val isRefreshing: Boolean = false, val isLoading: Boolean = true,
val items: ItemGroups = mutableMapOf(), val isRefreshing: Boolean = false,
val updates: Int = 0, val items: ItemGroups = mutableMapOf(),
val searchQuery: String? = null, val updates: Int = 0,
) { val searchQuery: String? = null,
val isEmpty = items.isEmpty() ) {
val isEmpty = items.isEmpty()
}
} }
typealias ItemGroups = MutableMap<AnimeExtensionUiModel.Header, List<AnimeExtensionUiModel.Item>> typealias ItemGroups = MutableMap<AnimeExtensionUiModel.Header, List<AnimeExtensionUiModel.Item>>

View file

@ -38,7 +38,7 @@ class AnimeExtensionDetailsScreenModel(
private val extensionManager: AnimeExtensionManager = Injekt.get(), private val extensionManager: AnimeExtensionManager = Injekt.get(),
private val getExtensionSources: GetAnimeExtensionSources = Injekt.get(), private val getExtensionSources: GetAnimeExtensionSources = Injekt.get(),
private val toggleSource: ToggleAnimeSource = Injekt.get(), private val toggleSource: ToggleAnimeSource = Injekt.get(),
) : StateScreenModel<AnimeExtensionDetailsState>(AnimeExtensionDetailsState()) { ) : StateScreenModel<AnimeExtensionDetailsScreenModel.State>(State()) {
private val _events: Channel<AnimeExtensionDetailsEvent> = Channel() private val _events: Channel<AnimeExtensionDetailsEvent> = Channel()
val events: Flow<AnimeExtensionDetailsEvent> = _events.receiveAsFlow() val events: Flow<AnimeExtensionDetailsEvent> = _events.receiveAsFlow()
@ -160,21 +160,21 @@ class AnimeExtensionDetailsScreenModel(
url + "/src/" + pkgName.replace(".", "/") + path url + "/src/" + pkgName.replace(".", "/") + path
} }
} }
@Immutable
data class State(
val extension: AnimeExtension.Installed? = null,
private val _sources: List<AnimeExtensionSourceItem>? = null,
) {
val sources: List<AnimeExtensionSourceItem>
get() = _sources.orEmpty()
val isLoading: Boolean
get() = extension == null || _sources == null
}
} }
sealed class AnimeExtensionDetailsEvent { sealed interface AnimeExtensionDetailsEvent {
data object Uninstalled : AnimeExtensionDetailsEvent() data object Uninstalled : AnimeExtensionDetailsEvent
}
@Immutable
data class AnimeExtensionDetailsState(
val extension: AnimeExtension.Installed? = null,
private val _sources: List<AnimeExtensionSourceItem>? = null,
) {
val sources: List<AnimeExtensionSourceItem>
get() = _sources.orEmpty()
val isLoading: Boolean
get() = extension == null || _sources == null
} }

View file

@ -17,7 +17,7 @@ import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import tachiyomi.presentation.core.screens.LoadingScreen import tachiyomi.presentation.core.screens.LoadingScreen
data class MigrationAnimeScreen( data class MigrateAnimeScreen(
private val sourceId: Long, private val sourceId: Long,
) : Screen() { ) : Screen() {
@ -25,7 +25,7 @@ data class MigrationAnimeScreen(
override fun Content() { override fun Content() {
val context = LocalContext.current val context = LocalContext.current
val navigator = LocalNavigator.currentOrThrow val navigator = LocalNavigator.currentOrThrow
val screenModel = rememberScreenModel { MigrationAnimeScreenModel(sourceId) } val screenModel = rememberScreenModel { MigrateAnimeScreenModel(sourceId) }
val state by screenModel.state.collectAsState() val state by screenModel.state.collectAsState()

View file

@ -20,11 +20,11 @@ import tachiyomi.domain.source.anime.service.AnimeSourceManager
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
class MigrationAnimeScreenModel( class MigrateAnimeScreenModel(
private val sourceId: Long, private val sourceId: Long,
private val sourceManager: AnimeSourceManager = Injekt.get(), private val sourceManager: AnimeSourceManager = Injekt.get(),
private val getFavorites: GetAnimeFavorites = Injekt.get(), private val getFavorites: GetAnimeFavorites = Injekt.get(),
) : StateScreenModel<MigrateAnimeState>(MigrateAnimeState()) { ) : StateScreenModel<MigrateAnimeScreenModel.State>(State()) {
private val _events: Channel<MigrationAnimeEvent> = Channel() private val _events: Channel<MigrationAnimeEvent> = Channel()
val events: Flow<MigrationAnimeEvent> = _events.receiveAsFlow() val events: Flow<MigrationAnimeEvent> = _events.receiveAsFlow()
@ -51,24 +51,24 @@ class MigrationAnimeScreenModel(
} }
} }
} }
@Immutable
data class State(
val source: AnimeSource? = null,
private val titleList: List<Anime>? = null,
) {
val titles: List<Anime>
get() = titleList.orEmpty()
val isLoading: Boolean
get() = source == null || titleList == null
val isEmpty: Boolean
get() = titles.isEmpty()
}
} }
sealed class MigrationAnimeEvent { sealed interface MigrationAnimeEvent {
data object FailedFetchingFavorites : MigrationAnimeEvent() data object FailedFetchingFavorites : MigrationAnimeEvent
}
@Immutable
data class MigrateAnimeState(
val source: AnimeSource? = null,
private val titleList: List<Anime>? = null,
) {
val titles: List<Anime>
get() = titleList.orEmpty()
val isLoading: Boolean
get() = source == null || titleList == null
val isEmpty: Boolean
get() = titles.isEmpty()
} }

View file

@ -37,7 +37,7 @@ class AnimeMigrateSearchScreenDialogScreenModel(
val dialog: Dialog? = null, val dialog: Dialog? = null,
) )
sealed class Dialog { sealed interface Dialog {
data class Migrate(val anime: Anime) : Dialog() data class Migrate(val anime: Anime) : Dialog
} }
} }

View file

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.ui.browse.anime.migration.sources package eu.kanade.tachiyomi.ui.browse.anime.migration.sources
import androidx.compose.runtime.Immutable
import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.coroutineScope import cafe.adriel.voyager.core.model.coroutineScope
import eu.kanade.domain.source.anime.interactor.GetAnimeSourcesWithFavoriteCount import eu.kanade.domain.source.anime.interactor.GetAnimeSourcesWithFavoriteCount
@ -23,7 +24,7 @@ class MigrateAnimeSourceScreenModel(
preferences: SourcePreferences = Injekt.get(), preferences: SourcePreferences = Injekt.get(),
private val getSourcesWithFavoriteCount: GetAnimeSourcesWithFavoriteCount = Injekt.get(), private val getSourcesWithFavoriteCount: GetAnimeSourcesWithFavoriteCount = Injekt.get(),
private val setMigrateSorting: SetMigrateSorting = Injekt.get(), private val setMigrateSorting: SetMigrateSorting = Injekt.get(),
) : StateScreenModel<MigrateAnimeSourceState>(MigrateAnimeSourceState()) { ) : StateScreenModel<MigrateAnimeSourceScreenModel.State>(State()) {
private val _channel = Channel<Event>(Int.MAX_VALUE) private val _channel = Channel<Event>(Int.MAX_VALUE)
val channel = _channel.receiveAsFlow() val channel = _channel.receiveAsFlow()
@ -76,16 +77,17 @@ class MigrateAnimeSourceScreenModel(
} }
} }
sealed class Event { @Immutable
data object FailedFetchingSourcesWithCount : Event() data class State(
val isLoading: Boolean = true,
val items: List<Pair<AnimeSource, Long>> = emptyList(),
val sortingMode: SetMigrateSorting.Mode = SetMigrateSorting.Mode.ALPHABETICAL,
val sortingDirection: SetMigrateSorting.Direction = SetMigrateSorting.Direction.ASCENDING,
) {
val isEmpty = items.isEmpty()
}
sealed interface Event {
data object FailedFetchingSourcesWithCount : Event
} }
} }
data class MigrateAnimeSourceState(
val isLoading: Boolean = true,
val items: List<Pair<AnimeSource, Long>> = emptyList(),
val sortingMode: SetMigrateSorting.Mode = SetMigrateSorting.Mode.ALPHABETICAL,
val sortingDirection: SetMigrateSorting.Direction = SetMigrateSorting.Direction.ASCENDING,
) {
val isEmpty = items.isEmpty()
}

View file

@ -15,7 +15,7 @@ import eu.kanade.presentation.browse.anime.MigrateAnimeSourceScreen
import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.TabContent import eu.kanade.presentation.components.TabContent
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.browse.anime.migration.anime.MigrationAnimeScreen import eu.kanade.tachiyomi.ui.browse.anime.migration.anime.MigrateAnimeScreen
@Composable @Composable
fun Screen.migrateAnimeSourceTab(): TabContent { fun Screen.migrateAnimeSourceTab(): TabContent {
@ -39,7 +39,9 @@ fun Screen.migrateAnimeSourceTab(): TabContent {
MigrateAnimeSourceScreen( MigrateAnimeSourceScreen(
state = state, state = state,
contentPadding = contentPadding, contentPadding = contentPadding,
onClickItem = { source -> navigator.push(MigrationAnimeScreen(source.id)) }, onClickItem = { source ->
navigator.push(MigrateAnimeScreen(source.id))
},
onToggleSortingDirection = screenModel::toggleSortingDirection, onToggleSortingDirection = screenModel::toggleSortingDirection,
onToggleSortingMode = screenModel::toggleSortingMode, onToggleSortingMode = screenModel::toggleSortingMode,
) )

View file

@ -22,12 +22,12 @@ class AnimeSourcesFilterScreen : Screen() {
val screenModel = rememberScreenModel { AnimeSourcesFilterScreenModel() } val screenModel = rememberScreenModel { AnimeSourcesFilterScreenModel() }
val state by screenModel.state.collectAsState() val state by screenModel.state.collectAsState()
if (state is AnimeSourcesFilterState.Loading) { if (state is AnimeSourcesFilterScreenModel.State.Loading) {
LoadingScreen() LoadingScreen()
return return
} }
if (state is AnimeSourcesFilterState.Error) { if (state is AnimeSourcesFilterScreenModel.State.Error) {
val context = LocalContext.current val context = LocalContext.current
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
context.toast(R.string.internal_error) context.toast(R.string.internal_error)
@ -36,7 +36,7 @@ class AnimeSourcesFilterScreen : Screen() {
return return
} }
val successState = state as AnimeSourcesFilterState.Success val successState = state as AnimeSourcesFilterScreenModel.State.Success
AnimeSourcesFilterScreen( AnimeSourcesFilterScreen(
navigateUp = navigator::pop, navigateUp = navigator::pop,

View file

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.ui.browse.anime.source package eu.kanade.tachiyomi.ui.browse.anime.source
import androidx.compose.runtime.Immutable
import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.coroutineScope import cafe.adriel.voyager.core.model.coroutineScope
import eu.kanade.domain.source.anime.interactor.GetLanguagesWithAnimeSources import eu.kanade.domain.source.anime.interactor.GetLanguagesWithAnimeSources
@ -21,7 +22,7 @@ class AnimeSourcesFilterScreenModel(
private val getLanguagesWithSources: GetLanguagesWithAnimeSources = Injekt.get(), private val getLanguagesWithSources: GetLanguagesWithAnimeSources = Injekt.get(),
private val toggleSource: ToggleAnimeSource = Injekt.get(), private val toggleSource: ToggleAnimeSource = Injekt.get(),
private val toggleLanguage: ToggleLanguage = Injekt.get(), private val toggleLanguage: ToggleLanguage = Injekt.get(),
) : StateScreenModel<AnimeSourcesFilterState>(AnimeSourcesFilterState.Loading) { ) : StateScreenModel<AnimeSourcesFilterScreenModel.State>(State.Loading) {
init { init {
coroutineScope.launch { coroutineScope.launch {
@ -32,14 +33,14 @@ class AnimeSourcesFilterScreenModel(
) { a, b, c -> Triple(a, b, c) } ) { a, b, c -> Triple(a, b, c) }
.catch { throwable -> .catch { throwable ->
mutableState.update { mutableState.update {
AnimeSourcesFilterState.Error( State.Error(
throwable = throwable, throwable = throwable,
) )
} }
} }
.collectLatest { (languagesWithSources, enabledLanguages, disabledSources) -> .collectLatest { (languagesWithSources, enabledLanguages, disabledSources) ->
mutableState.update { mutableState.update {
AnimeSourcesFilterState.Success( State.Success(
items = languagesWithSources, items = languagesWithSources,
enabledLanguages = enabledLanguages, enabledLanguages = enabledLanguages,
disabledSources = disabledSources, disabledSources = disabledSources,
@ -56,23 +57,26 @@ class AnimeSourcesFilterScreenModel(
fun toggleLanguage(language: String) { fun toggleLanguage(language: String) {
toggleLanguage.await(language) toggleLanguage.await(language)
} }
}
sealed class AnimeSourcesFilterState { sealed interface State {
object Loading : AnimeSourcesFilterState() @Immutable
data object Loading : State
data class Error( @Immutable
val throwable: Throwable, data class Error(
) : AnimeSourcesFilterState() val throwable: Throwable,
) : State
data class Success( @Immutable
val items: SortedMap<String, List<AnimeSource>>, data class Success(
val enabledLanguages: Set<String>, val items: SortedMap<String, List<AnimeSource>>,
val disabledSources: Set<String>, val enabledLanguages: Set<String>,
) : AnimeSourcesFilterState() { val disabledSources: Set<String>,
) : State {
val isEmpty: Boolean val isEmpty: Boolean
get() = items.isEmpty() get() = items.isEmpty()
}
} }
} }

View file

@ -99,8 +99,8 @@ class AnimeSourcesScreenModel(
mutableState.update { it.copy(dialog = null) } mutableState.update { it.copy(dialog = null) }
} }
sealed class Event { sealed interface Event {
data object FailedFetchingSources : Event() data object FailedFetchingSources : Event
} }
data class Dialog(val source: AnimeSource) data class Dialog(val source: AnimeSource)

View file

@ -14,6 +14,7 @@ import androidx.compose.material.icons.outlined.FilterList
import androidx.compose.material.icons.outlined.NewReleases import androidx.compose.material.icons.outlined.NewReleases
import androidx.compose.material3.FilterChip import androidx.compose.material3.FilterChip
import androidx.compose.material3.FilterChipDefaults import androidx.compose.material3.FilterChipDefaults
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHost
@ -57,7 +58,6 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.receiveAsFlow
import tachiyomi.core.util.lang.launchIO import tachiyomi.core.util.lang.launchIO
import tachiyomi.domain.source.anime.model.StubAnimeSource import tachiyomi.domain.source.anime.model.StubAnimeSource
import tachiyomi.presentation.core.components.material.Divider
import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.components.material.padding
import tachiyomi.source.local.entries.anime.LocalAnimeSource import tachiyomi.source.local.entries.anime.LocalAnimeSource
@ -192,7 +192,7 @@ data class BrowseAnimeSourceScreen(
} }
} }
Divider() HorizontalDivider()
} }
}, },
snackbarHost = { SnackbarHost(hostState = snackbarHostState) }, snackbarHost = { SnackbarHost(hostState = snackbarHostState) },

View file

@ -373,15 +373,15 @@ class BrowseAnimeSourceScreenModel(
} }
} }
sealed class Dialog { sealed interface Dialog {
data object Filter : Dialog() data object Filter : Dialog
data class RemoveAnime(val anime: Anime) : Dialog() data class RemoveAnime(val anime: Anime) : Dialog
data class AddDuplicateAnime(val anime: Anime, val duplicate: Anime) : Dialog() data class AddDuplicateAnime(val anime: Anime, val duplicate: Anime) : Dialog
data class ChangeAnimeCategory( data class ChangeAnimeCategory(
val anime: Anime, val anime: Anime,
val initialSelection: List<CheckboxState.State<Category>>, val initialSelection: List<CheckboxState.State<Category>>,
) : Dialog() ) : Dialog
data class Migrate(val newAnime: Anime) : Dialog() data class Migrate(val newAnime: Anime) : Dialog
} }
@Immutable @Immutable

View file

@ -8,7 +8,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.Divider import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
@ -68,7 +68,7 @@ fun SourceFilterAnimeDialog(
Text(stringResource(R.string.action_filter)) Text(stringResource(R.string.action_filter))
} }
} }
Divider() HorizontalDivider()
} }
items(filters) { items(filters) {
@ -85,7 +85,7 @@ private fun FilterItem(filter: AnimeFilter<*>, onUpdate: () -> Unit) {
HeadingItem(filter.name) HeadingItem(filter.name)
} }
is AnimeFilter.Separator -> { is AnimeFilter.Separator -> {
Divider() HorizontalDivider()
} }
is AnimeFilter.CheckBox -> { is AnimeFilter.CheckBox -> {
CheckboxItem( CheckboxItem(

View file

@ -186,16 +186,16 @@ enum class AnimeSourceFilter {
PinnedOnly, PinnedOnly,
} }
sealed class AnimeSearchItemResult { sealed interface AnimeSearchItemResult {
data object Loading : AnimeSearchItemResult() data object Loading : AnimeSearchItemResult
data class Error( data class Error(
val throwable: Throwable, val throwable: Throwable,
) : AnimeSearchItemResult() ) : AnimeSearchItemResult
data class Success( data class Success(
val result: List<Anime>, val result: List<Anime>,
) : AnimeSearchItemResult() { ) : AnimeSearchItemResult {
val isEmpty: Boolean val isEmpty: Boolean
get() = result.isEmpty() get() = result.isEmpty()
} }

View file

@ -54,20 +54,20 @@ class MangaExtensionFilterScreenModel(
} }
} }
sealed class MangaExtensionFilterEvent { sealed interface MangaExtensionFilterEvent {
data object FailedFetchingLanguages : MangaExtensionFilterEvent() data object FailedFetchingLanguages : MangaExtensionFilterEvent
} }
sealed class MangaExtensionFilterState { sealed interface MangaExtensionFilterState {
@Immutable @Immutable
data object Loading : MangaExtensionFilterState() data object Loading : MangaExtensionFilterState
@Immutable @Immutable
data class Success( data class Success(
val languages: List<String>, val languages: List<String>,
val enabledLanguages: Set<String> = emptySet(), val enabledLanguages: Set<String> = emptySet(),
) : MangaExtensionFilterState() { ) : MangaExtensionFilterState {
val isEmpty: Boolean val isEmpty: Boolean
get() = languages.isEmpty() get() = languages.isEmpty()

View file

@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.browse.manga.extension
import android.app.Application import android.app.Application
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.runtime.Immutable
import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.coroutineScope import cafe.adriel.voyager.core.model.coroutineScope
import eu.kanade.domain.extension.manga.interactor.GetMangaExtensionsByType import eu.kanade.domain.extension.manga.interactor.GetMangaExtensionsByType
@ -35,7 +36,7 @@ class MangaExtensionsScreenModel(
preferences: SourcePreferences = Injekt.get(), preferences: SourcePreferences = Injekt.get(),
private val extensionManager: MangaExtensionManager = Injekt.get(), private val extensionManager: MangaExtensionManager = Injekt.get(),
private val getExtensions: GetMangaExtensionsByType = Injekt.get(), private val getExtensions: GetMangaExtensionsByType = Injekt.get(),
) : StateScreenModel<MangaExtensionsState>(MangaExtensionsState()) { ) : StateScreenModel<MangaExtensionsScreenModel.State>(State()) {
private var _currentDownloads = MutableStateFlow<Map<String, InstallStep>>(hashMapOf()) private var _currentDownloads = MutableStateFlow<Map<String, InstallStep>>(hashMapOf())
@ -191,16 +192,17 @@ class MangaExtensionsScreenModel(
fun trustSignature(signatureHash: String) { fun trustSignature(signatureHash: String) {
extensionManager.trustSignature(signatureHash) extensionManager.trustSignature(signatureHash)
} }
}
data class MangaExtensionsState( @Immutable
val isLoading: Boolean = true, data class State(
val isRefreshing: Boolean = false, val isLoading: Boolean = true,
val items: ItemGroups = mutableMapOf(), val isRefreshing: Boolean = false,
val updates: Int = 0, val items: ItemGroups = mutableMapOf(),
val searchQuery: String? = null, val updates: Int = 0,
) { val searchQuery: String? = null,
val isEmpty = items.isEmpty() ) {
val isEmpty = items.isEmpty()
}
} }
typealias ItemGroups = MutableMap<MangaExtensionUiModel.Header, List<MangaExtensionUiModel.Item>> typealias ItemGroups = MutableMap<MangaExtensionUiModel.Header, List<MangaExtensionUiModel.Item>>

View file

@ -38,7 +38,7 @@ class MangaExtensionDetailsScreenModel(
private val extensionManager: MangaExtensionManager = Injekt.get(), private val extensionManager: MangaExtensionManager = Injekt.get(),
private val getExtensionSources: GetExtensionSources = Injekt.get(), private val getExtensionSources: GetExtensionSources = Injekt.get(),
private val toggleSource: ToggleMangaSource = Injekt.get(), private val toggleSource: ToggleMangaSource = Injekt.get(),
) : StateScreenModel<MangaExtensionDetailsState>(MangaExtensionDetailsState()) { ) : StateScreenModel<MangaExtensionDetailsScreenModel.State>(State()) {
private val _events: Channel<MangaExtensionDetailsEvent> = Channel() private val _events: Channel<MangaExtensionDetailsEvent> = Channel()
val events: Flow<MangaExtensionDetailsEvent> = _events.receiveAsFlow() val events: Flow<MangaExtensionDetailsEvent> = _events.receiveAsFlow()
@ -160,21 +160,21 @@ class MangaExtensionDetailsScreenModel(
url + "/src/" + pkgName.replace(".", "/") + path url + "/src/" + pkgName.replace(".", "/") + path
} }
} }
@Immutable
data class State(
val extension: MangaExtension.Installed? = null,
private val _sources: List<MangaExtensionSourceItem>? = null,
) {
val sources: List<MangaExtensionSourceItem>
get() = _sources.orEmpty()
val isLoading: Boolean
get() = extension == null || _sources == null
}
} }
sealed class MangaExtensionDetailsEvent { sealed interface MangaExtensionDetailsEvent {
data object Uninstalled : MangaExtensionDetailsEvent() data object Uninstalled : MangaExtensionDetailsEvent
}
@Immutable
data class MangaExtensionDetailsState(
val extension: MangaExtension.Installed? = null,
private val _sources: List<MangaExtensionSourceItem>? = null,
) {
val sources: List<MangaExtensionSourceItem>
get() = _sources.orEmpty()
val isLoading: Boolean
get() = extension == null || _sources == null
} }

View file

@ -17,7 +17,7 @@ import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import tachiyomi.presentation.core.screens.LoadingScreen import tachiyomi.presentation.core.screens.LoadingScreen
data class MigrationMangaScreen( data class MigrateMangaScreen(
private val sourceId: Long, private val sourceId: Long,
) : Screen() { ) : Screen() {
@ -25,7 +25,7 @@ data class MigrationMangaScreen(
override fun Content() { override fun Content() {
val context = LocalContext.current val context = LocalContext.current
val navigator = LocalNavigator.currentOrThrow val navigator = LocalNavigator.currentOrThrow
val screenModel = rememberScreenModel { MigrationMangaScreenModel(sourceId) } val screenModel = rememberScreenModel { MigrateMangaScreenModel(sourceId) }
val state by screenModel.state.collectAsState() val state by screenModel.state.collectAsState()

View file

@ -20,11 +20,11 @@ import tachiyomi.domain.source.manga.service.MangaSourceManager
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
class MigrationMangaScreenModel( class MigrateMangaScreenModel(
private val sourceId: Long, private val sourceId: Long,
private val sourceManager: MangaSourceManager = Injekt.get(), private val sourceManager: MangaSourceManager = Injekt.get(),
private val getFavorites: GetMangaFavorites = Injekt.get(), private val getFavorites: GetMangaFavorites = Injekt.get(),
) : StateScreenModel<MigrateMangaState>(MigrateMangaState()) { ) : StateScreenModel<MigrateMangaScreenModel.State>(State()) {
private val _events: Channel<MigrationMangaEvent> = Channel() private val _events: Channel<MigrationMangaEvent> = Channel()
val events: Flow<MigrationMangaEvent> = _events.receiveAsFlow() val events: Flow<MigrationMangaEvent> = _events.receiveAsFlow()
@ -51,24 +51,24 @@ class MigrationMangaScreenModel(
} }
} }
} }
@Immutable
data class State(
val source: MangaSource? = null,
private val titleList: List<Manga>? = null,
) {
val titles: List<Manga>
get() = titleList.orEmpty()
val isLoading: Boolean
get() = source == null || titleList == null
val isEmpty: Boolean
get() = titles.isEmpty()
}
} }
sealed class MigrationMangaEvent { sealed interface MigrationMangaEvent {
data object FailedFetchingFavorites : MigrationMangaEvent() data object FailedFetchingFavorites : MigrationMangaEvent
}
@Immutable
data class MigrateMangaState(
val source: MangaSource? = null,
private val titleList: List<Manga>? = null,
) {
val titles: List<Manga>
get() = titleList.orEmpty()
val isLoading: Boolean
get() = source == null || titleList == null
val isEmpty: Boolean
get() = titles.isEmpty()
} }

View file

@ -37,7 +37,7 @@ class MangaMigrateSearchScreenDialogScreenModel(
val dialog: Dialog? = null, val dialog: Dialog? = null,
) )
sealed class Dialog { sealed interface Dialog {
data class Migrate(val manga: Manga) : Dialog() data class Migrate(val manga: Manga) : Dialog
} }
} }

View file

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.ui.browse.manga.migration.sources package eu.kanade.tachiyomi.ui.browse.manga.migration.sources
import androidx.compose.runtime.Immutable
import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.coroutineScope import cafe.adriel.voyager.core.model.coroutineScope
import eu.kanade.domain.source.manga.interactor.GetMangaSourcesWithFavoriteCount import eu.kanade.domain.source.manga.interactor.GetMangaSourcesWithFavoriteCount
@ -23,7 +24,7 @@ class MigrateSourceScreenModel(
preferences: SourcePreferences = Injekt.get(), preferences: SourcePreferences = Injekt.get(),
private val getSourcesWithFavoriteCount: GetMangaSourcesWithFavoriteCount = Injekt.get(), private val getSourcesWithFavoriteCount: GetMangaSourcesWithFavoriteCount = Injekt.get(),
private val setMigrateSorting: SetMigrateSorting = Injekt.get(), private val setMigrateSorting: SetMigrateSorting = Injekt.get(),
) : StateScreenModel<MigrateMangaSourceState>(MigrateMangaSourceState()) { ) : StateScreenModel<MigrateSourceScreenModel.State>(State()) {
private val _channel = Channel<Event>(Int.MAX_VALUE) private val _channel = Channel<Event>(Int.MAX_VALUE)
val channel = _channel.receiveAsFlow() val channel = _channel.receiveAsFlow()
@ -76,16 +77,17 @@ class MigrateSourceScreenModel(
} }
} }
sealed class Event { @Immutable
data object FailedFetchingSourcesWithCount : Event() data class State(
val isLoading: Boolean = true,
val items: List<Pair<Source, Long>> = emptyList(),
val sortingMode: SetMigrateSorting.Mode = SetMigrateSorting.Mode.ALPHABETICAL,
val sortingDirection: SetMigrateSorting.Direction = SetMigrateSorting.Direction.ASCENDING,
) {
val isEmpty = items.isEmpty()
}
sealed interface Event {
data object FailedFetchingSourcesWithCount : Event
} }
} }
data class MigrateMangaSourceState(
val isLoading: Boolean = true,
val items: List<Pair<Source, Long>> = emptyList(),
val sortingMode: SetMigrateSorting.Mode = SetMigrateSorting.Mode.ALPHABETICAL,
val sortingDirection: SetMigrateSorting.Direction = SetMigrateSorting.Direction.ASCENDING,
) {
val isEmpty = items.isEmpty()
}

View file

@ -15,7 +15,7 @@ import eu.kanade.presentation.browse.manga.MigrateMangaSourceScreen
import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.TabContent import eu.kanade.presentation.components.TabContent
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.browse.manga.migration.manga.MigrationMangaScreen import eu.kanade.tachiyomi.ui.browse.manga.migration.manga.MigrateMangaScreen
@Composable @Composable
fun Screen.migrateMangaSourceTab(): TabContent { fun Screen.migrateMangaSourceTab(): TabContent {
@ -40,7 +40,7 @@ fun Screen.migrateMangaSourceTab(): TabContent {
state = state, state = state,
contentPadding = contentPadding, contentPadding = contentPadding,
onClickItem = { source -> onClickItem = { source ->
navigator.push(MigrationMangaScreen(source.id)) navigator.push(MigrateMangaScreen(source.id))
}, },
onToggleSortingDirection = screenModel::toggleSortingDirection, onToggleSortingDirection = screenModel::toggleSortingDirection,
onToggleSortingMode = screenModel::toggleSortingMode, onToggleSortingMode = screenModel::toggleSortingMode,

View file

@ -22,12 +22,12 @@ class MangaSourcesFilterScreen : Screen() {
val screenModel = rememberScreenModel { SourcesFilterScreenModel() } val screenModel = rememberScreenModel { SourcesFilterScreenModel() }
val state by screenModel.state.collectAsState() val state by screenModel.state.collectAsState()
if (state is MangaSourcesFilterState.Loading) { if (state is SourcesFilterScreenModel.State.Loading) {
LoadingScreen() LoadingScreen()
return return
} }
if (state is MangaSourcesFilterState.Error) { if (state is SourcesFilterScreenModel.State.Error) {
val context = LocalContext.current val context = LocalContext.current
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
context.toast(R.string.internal_error) context.toast(R.string.internal_error)
@ -36,7 +36,7 @@ class MangaSourcesFilterScreen : Screen() {
return return
} }
val successState = state as MangaSourcesFilterState.Success val successState = state as SourcesFilterScreenModel.State.Success
MangaSourcesFilterScreen( MangaSourcesFilterScreen(
navigateUp = navigator::pop, navigateUp = navigator::pop,

View file

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.ui.browse.manga.source package eu.kanade.tachiyomi.ui.browse.manga.source
import androidx.compose.runtime.Immutable
import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.coroutineScope import cafe.adriel.voyager.core.model.coroutineScope
import eu.kanade.domain.source.manga.interactor.GetLanguagesWithMangaSources import eu.kanade.domain.source.manga.interactor.GetLanguagesWithMangaSources
@ -21,7 +22,7 @@ class SourcesFilterScreenModel(
private val getLanguagesWithSources: GetLanguagesWithMangaSources = Injekt.get(), private val getLanguagesWithSources: GetLanguagesWithMangaSources = Injekt.get(),
private val toggleSource: ToggleMangaSource = Injekt.get(), private val toggleSource: ToggleMangaSource = Injekt.get(),
private val toggleLanguage: ToggleLanguage = Injekt.get(), private val toggleLanguage: ToggleLanguage = Injekt.get(),
) : StateScreenModel<MangaSourcesFilterState>(MangaSourcesFilterState.Loading) { ) : StateScreenModel<SourcesFilterScreenModel.State>(State.Loading) {
init { init {
coroutineScope.launch { coroutineScope.launch {
@ -32,14 +33,14 @@ class SourcesFilterScreenModel(
) { a, b, c -> Triple(a, b, c) } ) { a, b, c -> Triple(a, b, c) }
.catch { throwable -> .catch { throwable ->
mutableState.update { mutableState.update {
MangaSourcesFilterState.Error( State.Error(
throwable = throwable, throwable = throwable,
) )
} }
} }
.collectLatest { (languagesWithSources, enabledLanguages, disabledSources) -> .collectLatest { (languagesWithSources, enabledLanguages, disabledSources) ->
mutableState.update { mutableState.update {
MangaSourcesFilterState.Success( State.Success(
items = languagesWithSources, items = languagesWithSources,
enabledLanguages = enabledLanguages, enabledLanguages = enabledLanguages,
disabledSources = disabledSources, disabledSources = disabledSources,
@ -56,23 +57,26 @@ class SourcesFilterScreenModel(
fun toggleLanguage(language: String) { fun toggleLanguage(language: String) {
toggleLanguage.await(language) toggleLanguage.await(language)
} }
}
sealed class MangaSourcesFilterState { sealed interface State {
object Loading : MangaSourcesFilterState() @Immutable
data object Loading : State
data class Error( @Immutable
val throwable: Throwable, data class Error(
) : MangaSourcesFilterState() val throwable: Throwable,
) : State
data class Success( @Immutable
val items: SortedMap<String, List<Source>>, data class Success(
val enabledLanguages: Set<String>, val items: SortedMap<String, List<Source>>,
val disabledSources: Set<String>, val enabledLanguages: Set<String>,
) : MangaSourcesFilterState() { val disabledSources: Set<String>,
) : State {
val isEmpty: Boolean val isEmpty: Boolean
get() = items.isEmpty() get() = items.isEmpty()
}
} }
} }

View file

@ -123,8 +123,8 @@ class MangaSourcesScreenModel(
mutableState.update { it.copy(dialog = null) } mutableState.update { it.copy(dialog = null) }
} }
sealed class Event { sealed interface Event {
data object FailedFetchingSources : Event() data object FailedFetchingSources : Event
} }
data class Dialog(val source: Source) data class Dialog(val source: Source)

View file

@ -14,6 +14,7 @@ import androidx.compose.material.icons.outlined.FilterList
import androidx.compose.material.icons.outlined.NewReleases import androidx.compose.material.icons.outlined.NewReleases
import androidx.compose.material3.FilterChip import androidx.compose.material3.FilterChip
import androidx.compose.material3.FilterChipDefaults import androidx.compose.material3.FilterChipDefaults
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHost
@ -57,7 +58,6 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.receiveAsFlow
import tachiyomi.core.util.lang.launchIO import tachiyomi.core.util.lang.launchIO
import tachiyomi.domain.source.manga.model.StubMangaSource import tachiyomi.domain.source.manga.model.StubMangaSource
import tachiyomi.presentation.core.components.material.Divider
import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.components.material.padding
import tachiyomi.source.local.entries.manga.LocalMangaSource import tachiyomi.source.local.entries.manga.LocalMangaSource
@ -192,7 +192,7 @@ data class BrowseMangaSourceScreen(
} }
} }
Divider() HorizontalDivider()
} }
}, },
snackbarHost = { SnackbarHost(hostState = snackbarHostState) }, snackbarHost = { SnackbarHost(hostState = snackbarHostState) },

View file

@ -367,15 +367,15 @@ class BrowseMangaSourceScreenModel(
} }
} }
sealed class Dialog { sealed interface Dialog {
data object Filter : Dialog() data object Filter : Dialog
data class RemoveManga(val manga: Manga) : Dialog() data class RemoveManga(val manga: Manga) : Dialog
data class AddDuplicateManga(val manga: Manga, val duplicate: Manga) : Dialog() data class AddDuplicateManga(val manga: Manga, val duplicate: Manga) : Dialog
data class ChangeMangaCategory( data class ChangeMangaCategory(
val manga: Manga, val manga: Manga,
val initialSelection: List<CheckboxState.State<Category>>, val initialSelection: List<CheckboxState.State<Category>>,
) : Dialog() ) : Dialog
data class Migrate(val newManga: Manga) : Dialog() data class Migrate(val newManga: Manga) : Dialog
} }
@Immutable @Immutable

View file

@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
@ -28,7 +29,6 @@ import tachiyomi.presentation.core.components.SortItem
import tachiyomi.presentation.core.components.TextItem import tachiyomi.presentation.core.components.TextItem
import tachiyomi.presentation.core.components.TriStateItem import tachiyomi.presentation.core.components.TriStateItem
import tachiyomi.presentation.core.components.material.Button import tachiyomi.presentation.core.components.material.Button
import tachiyomi.presentation.core.components.material.Divider
@Composable @Composable
fun SourceFilterMangaDialog( fun SourceFilterMangaDialog(
@ -68,7 +68,7 @@ fun SourceFilterMangaDialog(
Text(stringResource(R.string.action_filter)) Text(stringResource(R.string.action_filter))
} }
} }
Divider() HorizontalDivider()
} }
items(filters) { items(filters) {
@ -85,7 +85,7 @@ private fun FilterItem(filter: Filter<*>, onUpdate: () -> Unit) {
HeadingItem(filter.name) HeadingItem(filter.name)
} }
is Filter.Separator -> { is Filter.Separator -> {
Divider() HorizontalDivider()
} }
is Filter.CheckBox -> { is Filter.CheckBox -> {
CheckboxItem( CheckboxItem(

View file

@ -186,16 +186,16 @@ enum class MangaSourceFilter {
PinnedOnly, PinnedOnly,
} }
sealed class MangaSearchItemResult { sealed interface MangaSearchItemResult {
data object Loading : MangaSearchItemResult() data object Loading : MangaSearchItemResult
data class Error( data class Error(
val throwable: Throwable, val throwable: Throwable,
) : MangaSearchItemResult() ) : MangaSearchItemResult
data class Success( data class Success(
val result: List<Manga>, val result: List<Manga>,
) : MangaSearchItemResult() { ) : MangaSearchItemResult {
val isEmpty: Boolean val isEmpty: Boolean
get() = result.isEmpty() get() = result.isEmpty()
} }

View file

@ -130,27 +130,27 @@ class AnimeCategoryScreenModel(
} }
} }
sealed class AnimeCategoryDialog { sealed interface AnimeCategoryDialog {
data object Create : AnimeCategoryDialog() data object Create : AnimeCategoryDialog
data class Rename(val category: Category) : AnimeCategoryDialog() data class Rename(val category: Category) : AnimeCategoryDialog
data class Delete(val category: Category) : AnimeCategoryDialog() data class Delete(val category: Category) : AnimeCategoryDialog
} }
sealed class AnimeCategoryEvent { sealed interface AnimeCategoryEvent {
sealed class LocalizedMessage(@StringRes val stringRes: Int) : AnimeCategoryEvent() sealed class LocalizedMessage(@StringRes val stringRes: Int) : AnimeCategoryEvent
data object InternalError : LocalizedMessage(R.string.internal_error) data object InternalError : LocalizedMessage(R.string.internal_error)
} }
sealed class AnimeCategoryScreenState { sealed interface AnimeCategoryScreenState {
@Immutable @Immutable
data object Loading : AnimeCategoryScreenState() data object Loading : AnimeCategoryScreenState
@Immutable @Immutable
data class Success( data class Success(
val categories: List<Category>, val categories: List<Category>,
val dialog: AnimeCategoryDialog? = null, val dialog: AnimeCategoryDialog? = null,
) : AnimeCategoryScreenState() { ) : AnimeCategoryScreenState {
val isEmpty: Boolean val isEmpty: Boolean
get() = categories.isEmpty() get() = categories.isEmpty()

View file

@ -130,27 +130,27 @@ class MangaCategoryScreenModel(
} }
} }
sealed class MangaCategoryDialog { sealed interface MangaCategoryDialog {
data object Create : MangaCategoryDialog() data object Create : MangaCategoryDialog
data class Rename(val category: Category) : MangaCategoryDialog() data class Rename(val category: Category) : MangaCategoryDialog
data class Delete(val category: Category) : MangaCategoryDialog() data class Delete(val category: Category) : MangaCategoryDialog
} }
sealed class MangaCategoryEvent { sealed interface MangaCategoryEvent {
sealed class LocalizedMessage(@StringRes val stringRes: Int) : MangaCategoryEvent() sealed class LocalizedMessage(@StringRes val stringRes: Int) : MangaCategoryEvent
data object InternalError : LocalizedMessage(R.string.internal_error) data object InternalError : LocalizedMessage(R.string.internal_error)
} }
sealed class MangaCategoryScreenState { sealed interface MangaCategoryScreenState {
@Immutable @Immutable
data object Loading : MangaCategoryScreenState() data object Loading : MangaCategoryScreenState
@Immutable @Immutable
data class Success( data class Success(
val categories: List<Category>, val categories: List<Category>,
val dialog: MangaCategoryDialog? = null, val dialog: MangaCategoryDialog? = null,
) : MangaCategoryScreenState() { ) : MangaCategoryScreenState {
val isEmpty: Boolean val isEmpty: Boolean
get() = categories.isEmpty() get() = categories.isEmpty()

View file

@ -81,12 +81,12 @@ class AnimeScreen(
val state by screenModel.state.collectAsState() val state by screenModel.state.collectAsState()
if (state is AnimeScreenState.Loading) { if (state is AnimeScreenModel.State.Loading) {
LoadingScreen() LoadingScreen()
return return
} }
val successState = state as AnimeScreenState.Success val successState = state as AnimeScreenModel.State.Success
val isAnimeHttpSource = remember { successState.source is AnimeHttpSource } val isAnimeHttpSource = remember { successState.source is AnimeHttpSource }
LaunchedEffect(successState.anime, screenModel.source) { LaunchedEffect(successState.anime, screenModel.source) {

View file

@ -105,10 +105,10 @@ class AnimeScreenModel(
private val setAnimeCategories: SetAnimeCategories = Injekt.get(), private val setAnimeCategories: SetAnimeCategories = Injekt.get(),
internal val setAnimeViewerFlags: SetAnimeViewerFlags = Injekt.get(), internal val setAnimeViewerFlags: SetAnimeViewerFlags = Injekt.get(),
val snackbarHostState: SnackbarHostState = SnackbarHostState(), val snackbarHostState: SnackbarHostState = SnackbarHostState(),
) : StateScreenModel<AnimeScreenState>(AnimeScreenState.Loading) { ) : StateScreenModel<AnimeScreenModel.State>(State.Loading) {
private val successState: AnimeScreenState.Success? private val successState: State.Success?
get() = state.value as? AnimeScreenState.Success get() = state.value as? State.Success
private val loggedServices by lazy { trackManager.services.filter { it.isLogged && it is AnimeTrackService } } private val loggedServices by lazy { trackManager.services.filter { it.isLogged && it is AnimeTrackService } }
@ -145,11 +145,11 @@ class AnimeScreenModel(
/** /**
* Helper function to update the UI state only if it's currently in success state * Helper function to update the UI state only if it's currently in success state
*/ */
private inline fun updateSuccessState(func: (AnimeScreenState.Success) -> AnimeScreenState.Success) { private inline fun updateSuccessState(func: (State.Success) -> State.Success) {
mutableState.update { mutableState.update {
when (it) { when (it) {
AnimeScreenState.Loading -> it State.Loading -> it
is AnimeScreenState.Success -> func(it) is State.Success -> func(it)
} }
} }
} }
@ -187,7 +187,7 @@ class AnimeScreenModel(
// Show what we have earlier // Show what we have earlier
mutableState.update { mutableState.update {
AnimeScreenState.Success( State.Success(
anime = anime, anime = anime,
source = Injekt.get<AnimeSourceManager>().getOrStub(anime.source), source = Injekt.get<AnimeSourceManager>().getOrStub(anime.source),
isFromSource = isFromSource, isFromSource = isFromSource,
@ -351,8 +351,7 @@ class AnimeScreenModel(
} }
fun promptChangeCategories() { fun promptChangeCategories() {
val state = successState ?: return val anime = successState?.anime ?: return
val anime = state.anime
coroutineScope.launch { coroutineScope.launch {
val categories = getCategories() val categories = getCategories()
val selection = getAnimeCategoryIds(anime) val selection = getAnimeCategoryIds(anime)
@ -685,9 +684,9 @@ class AnimeScreenModel(
} }
fun markPreviousEpisodeSeen(pointer: Episode) { fun markPreviousEpisodeSeen(pointer: Episode) {
val successState = successState ?: return val anime = successState?.anime ?: return
val episodes = processedEpisodes.orEmpty().map { it.episode }.toList() val episodes = processedEpisodes.orEmpty().map { it.episode }.toList()
val prevEpisodes = if (successState.anime.sortDescending()) episodes.asReversed() else episodes val prevEpisodes = if (anime.sortDescending()) episodes.asReversed() else episodes
val pointerPos = prevEpisodes.indexOf(pointer) val pointerPos = prevEpisodes.indexOf(pointer)
if (pointerPos != -1) markEpisodesSeen(prevEpisodes.take(pointerPos), true) if (pointerPos != -1) markEpisodesSeen(prevEpisodes.take(pointerPos), true)
} }
@ -969,15 +968,15 @@ class AnimeScreenModel(
// Track sheet - end // Track sheet - end
sealed class Dialog { sealed interface Dialog {
data class ChangeCategory(val anime: Anime, val initialSelection: List<CheckboxState<Category>>) : Dialog() data class ChangeCategory(val anime: Anime, val initialSelection: List<CheckboxState<Category>>) : Dialog
data class DeleteEpisodes(val episodes: List<Episode>) : Dialog() data class DeleteEpisodes(val episodes: List<Episode>) : Dialog
data class DuplicateAnime(val anime: Anime, val duplicate: Anime) : Dialog() data class DuplicateAnime(val anime: Anime, val duplicate: Anime) : Dialog
data class ShowQualities(val episode: Episode, val anime: Anime, val source: AnimeSource) : Dialog() data class ShowQualities(val episode: Episode, val anime: Anime, val source: AnimeSource) : Dialog
data object ChangeAnimeSkipIntro : Dialog() data object ChangeAnimeSkipIntro : Dialog
data object SettingsSheet : Dialog() data object SettingsSheet : Dialog
data object TrackSheet : Dialog() data object TrackSheet : Dialog
data object FullCover : Dialog() data object FullCover : Dialog
} }
fun dismissDialog() { fun dismissDialog() {
@ -1007,55 +1006,60 @@ class AnimeScreenModel(
private fun showQualitiesDialog(episode: Episode) { private fun showQualitiesDialog(episode: Episode) {
updateSuccessState { it.copy(dialog = Dialog.ShowQualities(episode, it.anime, it.source)) } updateSuccessState { it.copy(dialog = Dialog.ShowQualities(episode, it.anime, it.source)) }
} }
}
sealed class AnimeScreenState { sealed interface State {
@Immutable @Immutable
object Loading : AnimeScreenState() object Loading : State
@Immutable @Immutable
data class Success( data class Success(
val anime: Anime, val anime: Anime,
val source: AnimeSource, val source: AnimeSource,
val isFromSource: Boolean, val isFromSource: Boolean,
val episodes: List<EpisodeItem>, val episodes: List<EpisodeItem>,
val trackItems: List<AnimeTrackItem> = emptyList(), val trackItems: List<AnimeTrackItem> = emptyList(),
val isRefreshingData: Boolean = false, val isRefreshingData: Boolean = false,
val dialog: AnimeScreenModel.Dialog? = null, val dialog: Dialog? = null,
val hasPromptedToAddBefore: Boolean = false, val hasPromptedToAddBefore: Boolean = false,
val nextAiringEpisode: Pair<Int, Long> = Pair(anime.nextEpisodeToAir, anime.nextEpisodeAiringAt), val nextAiringEpisode: Pair<Int, Long> = Pair(anime.nextEpisodeToAir, anime.nextEpisodeAiringAt),
) : AnimeScreenState() { ) : State {
val processedEpisodes by lazy { val processedEpisodes by lazy {
episodes.applyFilters(anime).toList() episodes.applyFilters(anime).toList()
} }
val trackingAvailable: Boolean val trackingAvailable: Boolean
get() = trackItems.isNotEmpty() get() = trackItems.isNotEmpty()
val trackingCount: Int val trackingCount: Int
get() = trackItems.count { it.track != null } get() = trackItems.count { it.track != null }
val airingEpisodeNumber: Double val airingEpisodeNumber: Double
get() = nextAiringEpisode.first.toDouble() get() = nextAiringEpisode.first.toDouble()
val airingTime: Long val airingTime: Long
get() = nextAiringEpisode.second.times(1000L).minus(Calendar.getInstance().timeInMillis) get() = nextAiringEpisode.second.times(1000L).minus(Calendar.getInstance().timeInMillis)
/** /**
* Applies the view filters to the list of episodes obtained from the database. * Applies the view filters to the list of episodes obtained from the database.
* @return an observable of the list of episodes filtered and sorted. * @return an observable of the list of episodes filtered and sorted.
*/ */
private fun List<EpisodeItem>.applyFilters(anime: Anime): Sequence<EpisodeItem> { private fun List<EpisodeItem>.applyFilters(anime: Anime): Sequence<EpisodeItem> {
val isLocalAnime = anime.isLocal() val isLocalAnime = anime.isLocal()
val unseenFilter = anime.unseenFilter val unseenFilter = anime.unseenFilter
val downloadedFilter = anime.downloadedFilter val downloadedFilter = anime.downloadedFilter
val bookmarkedFilter = anime.bookmarkedFilter val bookmarkedFilter = anime.bookmarkedFilter
return asSequence() return asSequence()
.filter { (episode) -> applyFilter(unseenFilter) { !episode.seen } } .filter { (episode) -> applyFilter(unseenFilter) { !episode.seen } }
.filter { (episode) -> applyFilter(bookmarkedFilter) { episode.bookmark } } .filter { (episode) -> applyFilter(bookmarkedFilter) { episode.bookmark } }
.filter { applyFilter(downloadedFilter) { it.isDownloaded || isLocalAnime } } .filter { applyFilter(downloadedFilter) { it.isDownloaded || isLocalAnime } }
.sortedWith { (episode1), (episode2) -> getEpisodeSort(anime).invoke(episode1, episode2) } .sortedWith { (episode1), (episode2) ->
getEpisodeSort(anime).invoke(
episode1,
episode2,
)
}
}
} }
} }
} }

View file

@ -76,12 +76,12 @@ class MangaScreen(
val state by screenModel.state.collectAsState() val state by screenModel.state.collectAsState()
if (state is MangaScreenState.Loading) { if (state is MangaScreenModel.State.Loading) {
LoadingScreen() LoadingScreen()
return return
} }
val successState = state as MangaScreenState.Success val successState = state as MangaScreenModel.State.Success
val isHttpSource = remember { successState.source is HttpSource } val isHttpSource = remember { successState.source is HttpSource }
LaunchedEffect(successState.manga, screenModel.source) { LaunchedEffect(successState.manga, screenModel.source) {

View file

@ -100,10 +100,10 @@ class MangaScreenModel(
private val getTracks: GetMangaTracks = Injekt.get(), private val getTracks: GetMangaTracks = Injekt.get(),
private val setMangaCategories: SetMangaCategories = Injekt.get(), private val setMangaCategories: SetMangaCategories = Injekt.get(),
val snackbarHostState: SnackbarHostState = SnackbarHostState(), val snackbarHostState: SnackbarHostState = SnackbarHostState(),
) : StateScreenModel<MangaScreenState>(MangaScreenState.Loading) { ) : StateScreenModel<MangaScreenModel.State>(State.Loading) {
private val successState: MangaScreenState.Success? private val successState: State.Success?
get() = state.value as? MangaScreenState.Success get() = state.value as? State.Success
private val loggedServices by lazy { trackManager.services.filter { it.isLogged && it is MangaTrackService } } private val loggedServices by lazy { trackManager.services.filter { it.isLogged && it is MangaTrackService } }
@ -140,11 +140,11 @@ class MangaScreenModel(
/** /**
* Helper function to update the UI state only if it's currently in success state * Helper function to update the UI state only if it's currently in success state
*/ */
private inline fun updateSuccessState(func: (MangaScreenState.Success) -> MangaScreenState.Success) { private inline fun updateSuccessState(func: (State.Success) -> State.Success) {
mutableState.update { mutableState.update {
when (it) { when (it) {
MangaScreenState.Loading -> it State.Loading -> it
is MangaScreenState.Success -> func(it) is State.Success -> func(it)
} }
} }
} }
@ -182,7 +182,7 @@ class MangaScreenModel(
// Show what we have earlier // Show what we have earlier
mutableState.update { mutableState.update {
MangaScreenState.Success( State.Success(
manga = manga, manga = manga,
source = Injekt.get<MangaSourceManager>().getOrStub(manga.source), source = Injekt.get<MangaSourceManager>().getOrStub(manga.source),
isFromSource = isFromSource, isFromSource = isFromSource,
@ -347,8 +347,7 @@ class MangaScreenModel(
} }
fun promptChangeCategories() { fun promptChangeCategories() {
val state = successState ?: return val manga = successState?.manga ?: return
val manga = state.manga
coroutineScope.launch { coroutineScope.launch {
val categories = getCategories() val categories = getCategories()
val selection = getMangaCategoryIds(manga) val selection = getMangaCategoryIds(manga)
@ -675,9 +674,9 @@ class MangaScreenModel(
} }
fun markPreviousChapterRead(pointer: Chapter) { fun markPreviousChapterRead(pointer: Chapter) {
val successState = successState ?: return val manga = successState?.manga ?: return
val chapters = filteredChapters.orEmpty().map { it.chapter } val chapters = filteredChapters.orEmpty().map { it.chapter }
val prevChapters = if (successState.manga.sortDescending()) chapters.asReversed() else chapters val prevChapters = if (manga.sortDescending()) chapters.asReversed() else chapters
val pointerPos = prevChapters.indexOf(pointer) val pointerPos = prevChapters.indexOf(pointer)
if (pointerPos != -1) markChaptersRead(prevChapters.take(pointerPos), true) if (pointerPos != -1) markChaptersRead(prevChapters.take(pointerPos), true)
} }
@ -953,13 +952,13 @@ class MangaScreenModel(
// Track sheet - end // Track sheet - end
sealed class Dialog { sealed interface Dialog {
data class ChangeCategory(val manga: Manga, val initialSelection: List<CheckboxState<Category>>) : Dialog() data class ChangeCategory(val manga: Manga, val initialSelection: List<CheckboxState<Category>>) : Dialog
data class DeleteChapters(val chapters: List<Chapter>) : Dialog() data class DeleteChapters(val chapters: List<Chapter>) : Dialog
data class DuplicateManga(val manga: Manga, val duplicate: Manga) : Dialog() data class DuplicateManga(val manga: Manga, val duplicate: Manga) : Dialog
data object SettingsSheet : Dialog() data object SettingsSheet : Dialog
data object TrackSheet : Dialog() data object TrackSheet : Dialog
data object FullCover : Dialog() data object FullCover : Dialog
} }
fun dismissDialog() { fun dismissDialog() {
@ -981,48 +980,53 @@ class MangaScreenModel(
fun showCoverDialog() { fun showCoverDialog() {
updateSuccessState { it.copy(dialog = Dialog.FullCover) } updateSuccessState { it.copy(dialog = Dialog.FullCover) }
} }
}
sealed class MangaScreenState { sealed interface State {
@Immutable @Immutable
object Loading : MangaScreenState() object Loading : State
@Immutable @Immutable
data class Success( data class Success(
val manga: Manga, val manga: Manga,
val source: MangaSource, val source: MangaSource,
val isFromSource: Boolean, val isFromSource: Boolean,
val chapters: List<ChapterItem>, val chapters: List<ChapterItem>,
val trackItems: List<MangaTrackItem> = emptyList(), val trackItems: List<MangaTrackItem> = emptyList(),
val isRefreshingData: Boolean = false, val isRefreshingData: Boolean = false,
val dialog: MangaScreenModel.Dialog? = null, val dialog: Dialog? = null,
val hasPromptedToAddBefore: Boolean = false, val hasPromptedToAddBefore: Boolean = false,
) : MangaScreenState() { ) : State {
val processedChapters by lazy { val processedChapters by lazy {
chapters.applyFilters(manga).toList() chapters.applyFilters(manga).toList()
} }
val trackingAvailable: Boolean val trackingAvailable: Boolean
get() = trackItems.isNotEmpty() get() = trackItems.isNotEmpty()
val trackingCount: Int val trackingCount: Int
get() = trackItems.count { it.track != null } get() = trackItems.count { it.track != null }
/** /**
* Applies the view filters to the list of chapters obtained from the database. * Applies the view filters to the list of chapters obtained from the database.
* @return an observable of the list of chapters filtered and sorted. * @return an observable of the list of chapters filtered and sorted.
*/ */
private fun List<ChapterItem>.applyFilters(manga: Manga): Sequence<ChapterItem> { private fun List<ChapterItem>.applyFilters(manga: Manga): Sequence<ChapterItem> {
val isLocalManga = manga.isLocal() val isLocalManga = manga.isLocal()
val unreadFilter = manga.unreadFilter val unreadFilter = manga.unreadFilter
val downloadedFilter = manga.downloadedFilter val downloadedFilter = manga.downloadedFilter
val bookmarkedFilter = manga.bookmarkedFilter val bookmarkedFilter = manga.bookmarkedFilter
return asSequence() return asSequence()
.filter { (chapter) -> applyFilter(unreadFilter) { !chapter.read } } .filter { (chapter) -> applyFilter(unreadFilter) { !chapter.read } }
.filter { (chapter) -> applyFilter(bookmarkedFilter) { chapter.bookmark } } .filter { (chapter) -> applyFilter(bookmarkedFilter) { chapter.bookmark } }
.filter { applyFilter(downloadedFilter) { it.isDownloaded || isLocalManga } } .filter { applyFilter(downloadedFilter) { it.isDownloaded || isLocalManga } }
.sortedWith { (chapter1), (chapter2) -> getChapterSort(manga).invoke(chapter1, chapter2) } .sortedWith { (chapter1), (chapter2) ->
getChapterSort(manga).invoke(
chapter1,
chapter2,
)
}
}
} }
} }
} }

View file

@ -37,7 +37,7 @@ class AnimeHistoryScreenModel(
private val getHistory: GetAnimeHistory = Injekt.get(), private val getHistory: GetAnimeHistory = Injekt.get(),
private val getNextEpisodes: GetNextEpisodes = Injekt.get(), private val getNextEpisodes: GetNextEpisodes = Injekt.get(),
private val removeHistory: RemoveAnimeHistory = Injekt.get(), private val removeHistory: RemoveAnimeHistory = Injekt.get(),
) : StateScreenModel<AnimeHistoryState>(AnimeHistoryState()) { ) : StateScreenModel<AnimeHistoryScreenModel.State>(State()) {
private val _events: Channel<Event> = Channel(Channel.UNLIMITED) private val _events: Channel<Event> = Channel(Channel.UNLIMITED)
val events: Flow<Event> = _events.receiveAsFlow() val events: Flow<Event> = _events.receiveAsFlow()
@ -119,20 +119,21 @@ class AnimeHistoryScreenModel(
mutableState.update { it.copy(dialog = dialog) } mutableState.update { it.copy(dialog = dialog) }
} }
sealed class Dialog { @Immutable
data object DeleteAll : Dialog() data class State(
data class Delete(val history: AnimeHistoryWithRelations) : Dialog() val searchQuery: String? = null,
val list: List<AnimeHistoryUiModel>? = null,
val dialog: Dialog? = null,
)
sealed interface Dialog {
data object DeleteAll : Dialog
data class Delete(val history: AnimeHistoryWithRelations) : Dialog
} }
sealed class Event { sealed interface Event {
data class OpenEpisode(val episode: Episode?) : Event() data class OpenEpisode(val episode: Episode?) : Event
data object InternalError : Event() data object InternalError : Event
data object HistoryCleared : Event() data object HistoryCleared : Event
} }
} }
@Immutable
data class AnimeHistoryState(
val list: List<AnimeHistoryUiModel>? = null,
val dialog: AnimeHistoryScreenModel.Dialog? = null,
)

View file

@ -37,7 +37,7 @@ class MangaHistoryScreenModel(
private val getHistory: GetMangaHistory = Injekt.get(), private val getHistory: GetMangaHistory = Injekt.get(),
private val getNextChapters: GetNextChapters = Injekt.get(), private val getNextChapters: GetNextChapters = Injekt.get(),
private val removeHistory: RemoveMangaHistory = Injekt.get(), private val removeHistory: RemoveMangaHistory = Injekt.get(),
) : StateScreenModel<HistoryState>(HistoryState()) { ) : StateScreenModel<MangaHistoryScreenModel.State>(State()) {
private val _events: Channel<Event> = Channel(Channel.UNLIMITED) private val _events: Channel<Event> = Channel(Channel.UNLIMITED)
val events: Flow<Event> = _events.receiveAsFlow() val events: Flow<Event> = _events.receiveAsFlow()
@ -119,20 +119,21 @@ class MangaHistoryScreenModel(
mutableState.update { it.copy(dialog = dialog) } mutableState.update { it.copy(dialog = dialog) }
} }
sealed class Dialog { @Immutable
data object DeleteAll : Dialog() data class State(
data class Delete(val history: MangaHistoryWithRelations) : Dialog() val searchQuery: String? = null,
val list: List<MangaHistoryUiModel>? = null,
val dialog: Dialog? = null,
)
sealed interface Dialog {
data object DeleteAll : Dialog
data class Delete(val history: MangaHistoryWithRelations) : Dialog
} }
sealed class Event { sealed interface Event {
data class OpenChapter(val chapter: Chapter?) : Event() data class OpenChapter(val chapter: Chapter?) : Event
data object InternalError : Event() data object InternalError : Event
data object HistoryCleared : Event() data object HistoryCleared : Event
} }
} }
@Immutable
data class HistoryState(
val list: List<MangaHistoryUiModel>? = null,
val dialog: MangaHistoryScreenModel.Dialog? = null,
)

View file

@ -336,12 +336,12 @@ object HomeScreen : Screen() {
showBottomNavEvent.send(show) showBottomNavEvent.send(show)
} }
sealed class Tab { sealed interface Tab {
data class Animelib(val animeIdToOpen: Long? = null) : Tab() data class Animelib(val animeIdToOpen: Long? = null) : Tab
data class Library(val mangaIdToOpen: Long? = null) : Tab() data class Library(val mangaIdToOpen: Long? = null) : Tab
data object Updates : Tab() data object Updates : Tab
data object History : Tab() data object History : Tab
data class Browse(val toExtensions: Boolean = false) : Tab() data class Browse(val toExtensions: Boolean = false) : Tab
data class More(val toDownloads: Boolean) : Tab() data class More(val toDownloads: Boolean) : Tab
} }
} }

View file

@ -664,10 +664,10 @@ class AnimeLibraryScreenModel(
mutableState.update { it.copy(dialog = null) } mutableState.update { it.copy(dialog = null) }
} }
sealed class Dialog { sealed interface Dialog {
data object SettingsSheet : Dialog() data object SettingsSheet : Dialog
data class ChangeCategory(val anime: List<Anime>, val initialSelection: List<CheckboxState<Category>>) : Dialog() data class ChangeCategory(val anime: List<Anime>, val initialSelection: List<CheckboxState<Category>>) : Dialog
data class DeleteAnime(val anime: List<Anime>) : Dialog() data class DeleteAnime(val anime: List<Anime>) : Dialog
} }
@Immutable @Immutable

View file

@ -658,10 +658,10 @@ class MangaLibraryScreenModel(
mutableState.update { it.copy(dialog = null) } mutableState.update { it.copy(dialog = null) }
} }
sealed class Dialog { sealed interface Dialog {
data object SettingsSheet : Dialog() data object SettingsSheet : Dialog
data class ChangeCategory(val manga: List<Manga>, val initialSelection: List<CheckboxState<Category>>) : Dialog() data class ChangeCategory(val manga: List<Manga>, val initialSelection: List<CheckboxState<Category>>) : Dialog
data class DeleteManga(val manga: List<Manga>) : Dialog() data class DeleteManga(val manga: List<Manga>) : Dialog
} }
@Immutable @Immutable

View file

@ -134,8 +134,8 @@ private class MoreScreenModel(
} }
} }
sealed class DownloadQueueState { sealed interface DownloadQueueState {
data object Stopped : DownloadQueueState() data object Stopped : DownloadQueueState
data class Paused(val pending: Int) : DownloadQueueState() data class Paused(val pending: Int) : DownloadQueueState
data class Downloading(val pending: Int) : DownloadQueueState() data class Downloading(val pending: Int) : DownloadQueueState
} }

View file

@ -815,9 +815,9 @@ class ReaderViewModel @JvmOverloads constructor(
Error, Error,
} }
sealed class SaveImageResult { sealed interface SaveImageResult {
class Success(val uri: Uri) : SaveImageResult() class Success(val uri: Uri) : SaveImageResult
class Error(val error: Throwable) : SaveImageResult() class Error(val error: Throwable) : SaveImageResult
} }
/** /**
@ -907,18 +907,18 @@ class ReaderViewModel @JvmOverloads constructor(
get() = viewerChapters?.currChapter?.pages?.size ?: -1 get() = viewerChapters?.currChapter?.pages?.size ?: -1
} }
sealed class Dialog { sealed interface Dialog {
data object Loading : Dialog() data object Loading : Dialog
data object Settings : Dialog() data object Settings : Dialog
data class PageActions(val page: ReaderPage) : Dialog() data class PageActions(val page: ReaderPage) : Dialog
} }
sealed class Event { sealed interface Event {
data object ReloadViewerChapters : Event() data object ReloadViewerChapters : Event
data class SetOrientation(val orientation: Int) : Event() data class SetOrientation(val orientation: Int) : Event
data class SetCoverResult(val result: SetAsCoverResult) : Event() data class SetCoverResult(val result: SetAsCoverResult) : Event
data class SavedImage(val result: SaveImageResult) : Event() data class SavedImage(val result: SaveImageResult) : Event
data class ShareImage(val uri: Uri, val page: ReaderPage) : Event() data class ShareImage(val uri: Uri, val page: ReaderPage) : Event
} }
} }

View file

@ -39,10 +39,10 @@ data class ReaderChapter(val chapter: Chapter) {
} }
} }
sealed class State { sealed interface State {
data object Wait : State() data object Wait : State
data object Loading : State() data object Loading : State
data class Error(val error: Throwable) : State() data class Error(val error: Throwable) : State
data class Loaded(val pages: List<ReaderPage>) : State() data class Loaded(val pages: List<ReaderPage>) : State
} }
} }

View file

@ -62,7 +62,7 @@ class AnimeUpdatesScreenModel(
val snackbarHostState: SnackbarHostState = SnackbarHostState(), val snackbarHostState: SnackbarHostState = SnackbarHostState(),
downloadPreferences: DownloadPreferences = Injekt.get(), downloadPreferences: DownloadPreferences = Injekt.get(),
uiPreferences: UiPreferences = Injekt.get(), uiPreferences: UiPreferences = Injekt.get(),
) : StateScreenModel<AnimeUpdatesState>(AnimeUpdatesState()) { ) : StateScreenModel<AnimeUpdatesScreenModel.State>(State()) {
private val _events: Channel<Event> = Channel(Int.MAX_VALUE) private val _events: Channel<Event> = Channel(Int.MAX_VALUE)
val events: Flow<Event> = _events.receiveAsFlow() val events: Flow<Event> = _events.receiveAsFlow()
@ -378,47 +378,47 @@ class AnimeUpdatesScreenModel(
libraryPreferences.newAnimeUpdatesCount().set(0) libraryPreferences.newAnimeUpdatesCount().set(0)
} }
sealed class Dialog { @Immutable
data class DeleteConfirmation(val toDelete: List<AnimeUpdatesItem>) : Dialog() data class State(
data class ShowQualities(val episodeTitle: String, val episodeId: Long, val animeId: Long, val sourceId: Long) : Dialog() val isLoading: Boolean = true,
} val items: List<AnimeUpdatesItem> = emptyList(),
val dialog: AnimeUpdatesScreenModel.Dialog? = null,
) {
val selected = items.filter { it.selected }
val selectionMode = selected.isNotEmpty()
sealed class Event { fun getUiModel(context: Context, relativeTime: Int): List<AnimeUpdatesUiModel> {
data object InternalError : Event() val dateFormat by mutableStateOf(UiPreferences.dateFormat(Injekt.get<UiPreferences>().dateFormat().get()))
data class LibraryUpdateTriggered(val started: Boolean) : Event()
}
}
@Immutable return items
data class AnimeUpdatesState( .map { AnimeUpdatesUiModel.Item(it) }
val isLoading: Boolean = true, .insertSeparators { before, after ->
val items: List<AnimeUpdatesItem> = emptyList(), val beforeDate = before?.item?.update?.dateFetch?.toDateKey() ?: Date(0)
val dialog: AnimeUpdatesScreenModel.Dialog? = null, val afterDate = after?.item?.update?.dateFetch?.toDateKey() ?: Date(0)
) { when {
val selected = items.filter { it.selected } beforeDate.time != afterDate.time && afterDate.time != 0L -> {
val selectionMode = selected.isNotEmpty() val text = afterDate.toRelativeString(
context = context,
fun getUiModel(context: Context, relativeTime: Int): List<AnimeUpdatesUiModel> { range = relativeTime,
val dateFormat by mutableStateOf(UiPreferences.dateFormat(Injekt.get<UiPreferences>().dateFormat().get())) dateFormat = dateFormat,
)
return items AnimeUpdatesUiModel.Header(text)
.map { AnimeUpdatesUiModel.Item(it) } }
.insertSeparators { before, after -> // Return null to avoid adding a separator between two items.
val beforeDate = before?.item?.update?.dateFetch?.toDateKey() ?: Date(0) else -> null
val afterDate = after?.item?.update?.dateFetch?.toDateKey() ?: Date(0)
when {
beforeDate.time != afterDate.time && afterDate.time != 0L -> {
val text = afterDate.toRelativeString(
context = context,
range = relativeTime,
dateFormat = dateFormat,
)
AnimeUpdatesUiModel.Header(text)
} }
// Return null to avoid adding a separator between two items.
else -> null
} }
} }
}
sealed interface Dialog {
data class DeleteConfirmation(val toDelete: List<AnimeUpdatesItem>) : Dialog
data class ShowQualities(val episodeTitle: String, val episodeId: Long, val animeId: Long, val sourceId: Long) : Dialog
}
sealed interface Event {
data object InternalError : Event
data class LibraryUpdateTriggered(val started: Boolean) : Event
} }
} }

View file

@ -60,7 +60,7 @@ class MangaUpdatesScreenModel(
private val libraryPreferences: LibraryPreferences = Injekt.get(), private val libraryPreferences: LibraryPreferences = Injekt.get(),
val snackbarHostState: SnackbarHostState = SnackbarHostState(), val snackbarHostState: SnackbarHostState = SnackbarHostState(),
uiPreferences: UiPreferences = Injekt.get(), uiPreferences: UiPreferences = Injekt.get(),
) : StateScreenModel<UpdatesState>(UpdatesState()) { ) : StateScreenModel<MangaUpdatesScreenModel.State>(State()) {
private val _events: Channel<Event> = Channel(Int.MAX_VALUE) private val _events: Channel<Event> = Channel(Int.MAX_VALUE)
val events: Flow<Event> = _events.receiveAsFlow() val events: Flow<Event> = _events.receiveAsFlow()
@ -366,46 +366,46 @@ class MangaUpdatesScreenModel(
libraryPreferences.newMangaUpdatesCount().set(0) libraryPreferences.newMangaUpdatesCount().set(0)
} }
sealed class Dialog { @Immutable
data class DeleteConfirmation(val toDelete: List<MangaUpdatesItem>) : Dialog() data class State(
} val isLoading: Boolean = true,
val items: List<MangaUpdatesItem> = emptyList(),
val dialog: MangaUpdatesScreenModel.Dialog? = null,
) {
val selected = items.filter { it.selected }
val selectionMode = selected.isNotEmpty()
sealed class Event { fun getUiModel(context: Context, relativeTime: Int): List<MangaUpdatesUiModel> {
data object InternalError : Event() val dateFormat by mutableStateOf(UiPreferences.dateFormat(Injekt.get<UiPreferences>().dateFormat().get()))
data class LibraryUpdateTriggered(val started: Boolean) : Event()
}
}
@Immutable return items
data class UpdatesState( .map { MangaUpdatesUiModel.Item(it) }
val isLoading: Boolean = true, .insertSeparators { before, after ->
val items: List<MangaUpdatesItem> = emptyList(), val beforeDate = before?.item?.update?.dateFetch?.toDateKey() ?: Date(0)
val dialog: MangaUpdatesScreenModel.Dialog? = null, val afterDate = after?.item?.update?.dateFetch?.toDateKey() ?: Date(0)
) { when {
val selected = items.filter { it.selected } beforeDate.time != afterDate.time && afterDate.time != 0L -> {
val selectionMode = selected.isNotEmpty() val text = afterDate.toRelativeString(
context = context,
fun getUiModel(context: Context, relativeTime: Int): List<MangaUpdatesUiModel> { range = relativeTime,
val dateFormat by mutableStateOf(UiPreferences.dateFormat(Injekt.get<UiPreferences>().dateFormat().get())) dateFormat = dateFormat,
)
return items MangaUpdatesUiModel.Header(text)
.map { MangaUpdatesUiModel.Item(it) } }
.insertSeparators { before, after -> // Return null to avoid adding a separator between two items.
val beforeDate = before?.item?.update?.dateFetch?.toDateKey() ?: Date(0) else -> null
val afterDate = after?.item?.update?.dateFetch?.toDateKey() ?: Date(0)
when {
beforeDate.time != afterDate.time && afterDate.time != 0L -> {
val text = afterDate.toRelativeString(
context = context,
range = relativeTime,
dateFormat = dateFormat,
)
MangaUpdatesUiModel.Header(text)
} }
// Return null to avoid adding a separator between two items.
else -> null
} }
} }
}
sealed interface Dialog {
data class DeleteConfirmation(val toDelete: List<MangaUpdatesItem>) : Dialog
}
sealed interface Event {
data object InternalError : Event
data class LibraryUpdateTriggered(val started: Boolean) : Event
} }
} }

View file

@ -38,8 +38,8 @@ class CreateAnimeCategoryWithName(
} }
} }
sealed class Result { sealed interface Result {
data object Success : Result() data object Success : Result
data class InternalError(val error: Throwable) : Result() data class InternalError(val error: Throwable) : Result
} }
} }

View file

@ -35,8 +35,8 @@ class DeleteAnimeCategory(
} }
} }
sealed class Result { sealed interface Result {
data object Success : Result() data object Success : Result
data class InternalError(val error: Throwable) : Result() data class InternalError(val error: Throwable) : Result
} }
} }

View file

@ -27,7 +27,7 @@ class HideAnimeCategory(
} }
sealed class Result { sealed class Result {
object Success : Result() data object Success : Result()
data class InternalError(val error: Throwable) : Result() data class InternalError(val error: Throwable) : Result()
} }
} }

View file

@ -28,8 +28,8 @@ class RenameAnimeCategory(
suspend fun await(category: Category, name: String) = await(category.id, name) suspend fun await(category: Category, name: String) = await(category.id, name)
sealed class Result { sealed interface Result {
object Success : Result() data object Success : Result
data class InternalError(val error: Throwable) : Result() data class InternalError(val error: Throwable) : Result
} }
} }

View file

@ -57,10 +57,10 @@ class ReorderAnimeCategory(
} }
} }
sealed class Result { sealed interface Result {
data object Success : Result() data object Success : Result
data object Unchanged : Result() data object Unchanged : Result
data class InternalError(val error: Throwable) : Result() data class InternalError(val error: Throwable) : Result
} }
private enum class MoveTo { private enum class MoveTo {

View file

@ -17,8 +17,8 @@ class UpdateAnimeCategory(
} }
} }
sealed class Result { sealed interface Result {
data object Success : Result() data object Success : Result
data class Error(val error: Exception) : Result() data class Error(val error: Exception) : Result
} }
} }

View file

@ -38,8 +38,8 @@ class CreateMangaCategoryWithName(
} }
} }
sealed class Result { sealed interface Result {
data object Success : Result() data object Success : Result
data class InternalError(val error: Throwable) : Result() data class InternalError(val error: Throwable) : Result
} }
} }

View file

@ -35,8 +35,8 @@ class DeleteMangaCategory(
} }
} }
sealed class Result { sealed interface Result {
data object Success : Result() data object Success : Result
data class InternalError(val error: Throwable) : Result() data class InternalError(val error: Throwable) : Result
} }
} }

Some files were not shown because too many files have changed in this diff Show more