Last commit merged: 634ceeec50
This commit is contained in:
LuftVerbot 2023-11-25 21:09:34 +01:00
parent d7aee03688
commit 375a252a69
71 changed files with 961 additions and 327 deletions

View file

@ -20,7 +20,7 @@ android {
defaultConfig {
applicationId = "xyz.jmir.tachiyomi.mi"
versionCode = 108
versionCode = 109
versionName = "0.14.7"
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")

View file

@ -4,6 +4,8 @@ import eu.kanade.domain.download.anime.interactor.DeleteEpisodeDownload
import eu.kanade.domain.download.manga.interactor.DeleteChapterDownload
import eu.kanade.domain.entries.anime.interactor.SetAnimeViewerFlags
import eu.kanade.domain.entries.anime.interactor.UpdateAnime
import eu.kanade.domain.entries.manga.interactor.GetExcludedScanlators
import eu.kanade.domain.entries.manga.interactor.SetExcludedScanlators
import eu.kanade.domain.entries.manga.interactor.SetMangaViewerFlags
import eu.kanade.domain.entries.manga.interactor.UpdateManga
import eu.kanade.domain.extension.anime.interactor.GetAnimeExtensionLanguages
@ -12,6 +14,7 @@ import eu.kanade.domain.extension.anime.interactor.GetAnimeExtensionsByType
import eu.kanade.domain.extension.manga.interactor.GetExtensionSources
import eu.kanade.domain.extension.manga.interactor.GetMangaExtensionLanguages
import eu.kanade.domain.extension.manga.interactor.GetMangaExtensionsByType
import eu.kanade.domain.items.chapter.interactor.GetAvailableScanlators
import eu.kanade.domain.items.chapter.interactor.SetReadStatus
import eu.kanade.domain.items.chapter.interactor.SyncChaptersWithSource
import eu.kanade.domain.items.episode.interactor.SetSeenStatus
@ -224,6 +227,8 @@ class DomainModule : InjektModule {
addFactory { NetworkToLocalManga(get()) }
addFactory { UpdateManga(get(), get()) }
addFactory { SetMangaCategories(get()) }
addFactory { GetExcludedScanlators(get()) }
addFactory { SetExcludedScanlators(get()) }
addSingletonFactory<ReleaseService> { ReleaseServiceImpl(get(), get()) }
addFactory { GetApplicationRelease(get(), get()) }
@ -264,7 +269,8 @@ class DomainModule : InjektModule {
addFactory { UpdateChapter(get()) }
addFactory { SetReadStatus(get(), get(), get(), get()) }
addFactory { ShouldUpdateDbChapter() }
addFactory { SyncChaptersWithSource(get(), get(), get(), get(), get(), get(), get()) }
addFactory { SyncChaptersWithSource(get(), get(), get(), get(), get(), get(), get(), get()) }
addFactory { GetAvailableScanlators(get()) }
addSingletonFactory<AnimeHistoryRepository> { AnimeHistoryRepositoryImpl(get()) }
addFactory { GetAnimeHistory(get()) }

View file

@ -0,0 +1,24 @@
package eu.kanade.domain.entries.manga.interactor
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import tachiyomi.data.handlers.manga.MangaDatabaseHandler
class GetExcludedScanlators(
private val handler: MangaDatabaseHandler,
) {
suspend fun await(mangaId: Long): Set<String> {
return handler.awaitList {
excluded_scanlatorsQueries.getExcludedScanlatorsByMangaId(mangaId)
}
.toSet()
}
fun subscribe(mangaId: Long): Flow<Set<String>> {
return handler.subscribeToList {
excluded_scanlatorsQueries.getExcludedScanlatorsByMangaId(mangaId)
}
.map { it.toSet() }
}
}

View file

@ -0,0 +1,22 @@
package eu.kanade.domain.entries.manga.interactor
import tachiyomi.data.handlers.manga.MangaDatabaseHandler
class SetExcludedScanlators(
private val handler: MangaDatabaseHandler,
) {
suspend fun await(mangaId: Long, excludedScanlators: Set<String>) {
handler.await(inTransaction = true) {
val currentExcluded = handler.awaitList {
excluded_scanlatorsQueries.getExcludedScanlatorsByMangaId(mangaId)
}.toSet()
val toAdd = excludedScanlators.minus(currentExcluded)
for (scanlator in toAdd) {
excluded_scanlatorsQueries.insert(mangaId, scanlator)
}
val toRemove = currentExcluded.minus(excludedScanlators)
excluded_scanlatorsQueries.remove(mangaId, toRemove)
}
}
}

View file

@ -1,7 +1,7 @@
package eu.kanade.domain.entries.manga.interactor
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation
import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode
import tachiyomi.domain.entries.manga.model.MangaUpdate
import tachiyomi.domain.entries.manga.repository.MangaRepository
@ -14,7 +14,7 @@ class SetMangaViewerFlags(
mangaRepository.updateManga(
MangaUpdate(
id = id,
viewerFlags = manga.viewerFlags.setFlag(flag, ReadingModeType.MASK.toLong()),
viewerFlags = manga.viewerFlags.setFlag(flag, ReadingMode.MASK.toLong()),
),
)
}
@ -24,7 +24,7 @@ class SetMangaViewerFlags(
mangaRepository.updateManga(
MangaUpdate(
id = id,
viewerFlags = manga.viewerFlags.setFlag(flag, OrientationType.MASK.toLong()),
viewerFlags = manga.viewerFlags.setFlag(flag, ReaderOrientation.MASK.toLong()),
),
)
}

View file

@ -3,8 +3,8 @@ package eu.kanade.domain.entries.manga.model
import eu.kanade.domain.base.BasePreferences
import eu.kanade.tachiyomi.data.cache.MangaCoverCache
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation
import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode
import tachiyomi.core.metadata.comicinfo.ComicInfo
import tachiyomi.core.metadata.comicinfo.ComicInfoPublishingStatus
import tachiyomi.core.preference.TriState
@ -14,11 +14,11 @@ import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
// TODO: move these into the domain model
val Manga.readingModeType: Long
get() = viewerFlags and ReadingModeType.MASK.toLong()
val Manga.readingMode: Long
get() = viewerFlags and ReadingMode.MASK.toLong()
val Manga.orientationType: Long
get() = viewerFlags and OrientationType.MASK.toLong()
val Manga.readerOrientation: Long
get() = viewerFlags and ReaderOrientation.MASK.toLong()
val Manga.downloadedFilter: TriState
get() {

View file

@ -0,0 +1,24 @@
package eu.kanade.domain.items.chapter.interactor
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import tachiyomi.domain.items.chapter.repository.ChapterRepository
class GetAvailableScanlators(
private val repository: ChapterRepository,
) {
private fun List<String>.cleanupAvailableScanlators(): Set<String> {
return mapNotNull { it.ifBlank { null } }.toSet()
}
suspend fun await(mangaId: Long): Set<String> {
return repository.getScanlatorsByMangaId(mangaId)
.cleanupAvailableScanlators()
}
fun subscribe(mangaId: Long): Flow<Set<String>> {
return repository.getScanlatorsByMangaIdAsFlow(mangaId)
.map { it.cleanupAvailableScanlators() }
}
}

View file

@ -1,5 +1,6 @@
package eu.kanade.domain.items.chapter.interactor
import eu.kanade.domain.entries.manga.interactor.GetExcludedScanlators
import eu.kanade.domain.entries.manga.interactor.UpdateManga
import eu.kanade.domain.entries.manga.model.toSManga
import eu.kanade.domain.items.chapter.model.copyFromSChapter
@ -33,6 +34,7 @@ class SyncChaptersWithSource(
private val updateManga: UpdateManga,
private val updateChapter: UpdateChapter,
private val getChaptersByMangaId: GetChaptersByMangaId,
private val getExcludedScanlators: GetExcludedScanlators,
) {
/**
@ -218,6 +220,10 @@ class SyncChaptersWithSource(
val reAddedUrls = reAdded.map { it.url }.toHashSet()
return updatedToAdd.filterNot { it.url in reAddedUrls }
val excludedScanlators = getExcludedScanlators.await(manga.id).toHashSet()
return updatedToAdd.filterNot {
it.url in reAddedUrls || it.scanlator in excludedScanlators
}
}
}

View file

@ -22,7 +22,7 @@ fun Chapter.copyFromSChapter(sChapter: SChapter): Chapter {
url = sChapter.url,
dateUpload = sChapter.date_upload,
chapterNumber = sChapter.chapter_number.toDouble(),
scanlator = sChapter.scanlator?.ifBlank { null },
scanlator = sChapter.scanlator?.ifBlank { null }?.trim(),
)
}

View file

@ -5,13 +5,13 @@ import androidx.compose.material.icons.outlined.HelpOutline
import androidx.compose.material.icons.outlined.Refresh
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.PreviewLightDark
import eu.kanade.presentation.theme.TachiyomiTheme
import eu.kanade.tachiyomi.R
import tachiyomi.presentation.core.screens.EmptyScreen
import tachiyomi.presentation.core.screens.EmptyScreenAction
import tachiyomi.presentation.core.util.ThemePreviews
@ThemePreviews
@PreviewLightDark
@Composable
private fun NoActionPreview() {
TachiyomiTheme {
@ -23,7 +23,7 @@ private fun NoActionPreview() {
}
}
@ThemePreviews
@PreviewLightDark
@Composable
private fun WithActionPreview() {
TachiyomiTheme {

View file

@ -14,13 +14,13 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewLightDark
import eu.kanade.presentation.theme.TachiyomiTheme
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.util.CrashLogUtil
import kotlinx.coroutines.launch
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.screens.InfoScreen
import tachiyomi.presentation.core.util.ThemePreviews
@Composable
fun CrashScreen(
@ -63,7 +63,7 @@ fun CrashScreen(
}
}
@ThemePreviews
@PreviewLightDark
@Composable
private fun CrashScreenPreview() {
TachiyomiTheme {

View file

@ -5,11 +5,9 @@ import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.asPaddingValues
@ -29,9 +27,7 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
@ -51,7 +47,6 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.util.fastAll
import androidx.compose.ui.util.fastAny
@ -69,6 +64,7 @@ import eu.kanade.presentation.entries.anime.components.AnimeEpisodeListItem
import eu.kanade.presentation.entries.anime.components.AnimeInfoBox
import eu.kanade.presentation.entries.anime.components.EpisodeDownloadAction
import eu.kanade.presentation.entries.anime.components.ExpandableAnimeDescription
import eu.kanade.presentation.entries.anime.components.MissingEpisodeCountListItem
import eu.kanade.presentation.entries.anime.components.NextEpisodeAiringListItem
import eu.kanade.presentation.util.formatEpisodeNumber
import eu.kanade.tachiyomi.R
@ -95,7 +91,6 @@ import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.util.isScrolledToEnd
import tachiyomi.presentation.core.util.isScrollingUp
import tachiyomi.presentation.core.util.secondaryItemAlpha
import java.text.DateFormat
import java.util.Date
import java.util.concurrent.TimeUnit
@ -915,25 +910,7 @@ private fun LazyListScope.sharedEpisodeItems(
when (item) {
is EpisodeList.MissingCount -> {
Row(
modifier = Modifier.padding(
horizontal = MaterialTheme.padding.medium,
vertical = MaterialTheme.padding.small,
),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.medium),
) {
HorizontalDivider(modifier = Modifier.weight(1f))
Text(
text = pluralStringResource(
id = R.plurals.missing_items,
count = item.count,
item.count,
),
modifier = Modifier.secondaryItemAlpha(),
)
HorizontalDivider(modifier = Modifier.weight(1f))
}
MissingEpisodeCountListItem(count = item.count)
}
is EpisodeList.Item -> {
AnimeEpisodeListItem(

View file

@ -0,0 +1,52 @@
package eu.kanade.presentation.entries.anime.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.tooling.preview.PreviewLightDark
import eu.kanade.presentation.theme.TachiyomiTheme
import eu.kanade.tachiyomi.R
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.util.secondaryItemAlpha
@Composable
fun MissingEpisodeCountListItem(
count: Int,
modifier: Modifier = Modifier,
) {
Row(
modifier = modifier
.padding(
horizontal = MaterialTheme.padding.medium,
vertical = MaterialTheme.padding.small,
)
.secondaryItemAlpha(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.medium),
) {
HorizontalDivider(modifier = Modifier.weight(1f))
Text(
text = pluralStringResource(id = R.plurals.missing_items, count = count, count),
style = MaterialTheme.typography.labelMedium,
)
HorizontalDivider(modifier = Modifier.weight(1f))
}
}
@PreviewLightDark
@Composable
private fun Preview() {
TachiyomiTheme {
Surface {
MissingEpisodeCountListItem(count = 42)
}
}
}

View file

@ -1,12 +1,20 @@
package eu.kanade.presentation.entries.manga
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.PeopleAlt
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
@ -14,6 +22,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
@ -28,6 +37,7 @@ import tachiyomi.presentation.core.components.LabeledCheckbox
import tachiyomi.presentation.core.components.RadioItem
import tachiyomi.presentation.core.components.SortItem
import tachiyomi.presentation.core.components.TriStateItem
import tachiyomi.presentation.core.theme.active
@Composable
fun ChapterSettingsDialog(
@ -36,6 +46,8 @@ fun ChapterSettingsDialog(
onDownloadFilterChanged: (TriState) -> Unit,
onUnreadFilterChanged: (TriState) -> Unit,
onBookmarkedFilterChanged: (TriState) -> Unit,
scanlatorFilterActive: Boolean,
onScanlatorFilterClicked: (() -> Unit),
onSortModeChanged: (Long) -> Unit,
onDisplayModeChanged: (Long) -> Unit,
onSetAsDefault: (applyToExistingManga: Boolean) -> Unit,
@ -88,6 +100,8 @@ fun ChapterSettingsDialog(
onUnreadFilterChanged = onUnreadFilterChanged,
bookmarkedFilter = manga?.bookmarkedFilter ?: TriState.DISABLED,
onBookmarkedFilterChanged = onBookmarkedFilterChanged,
scanlatorFilterActive = scanlatorFilterActive,
onScanlatorFilterClicked = onScanlatorFilterClicked,
)
}
1 -> {
@ -116,6 +130,8 @@ private fun FilterPage(
onUnreadFilterChanged: (TriState) -> Unit,
bookmarkedFilter: TriState,
onBookmarkedFilterChanged: (TriState) -> Unit,
scanlatorFilterActive: Boolean,
onScanlatorFilterClicked: (() -> Unit),
) {
TriStateItem(
label = stringResource(R.string.label_downloaded),
@ -132,6 +148,39 @@ private fun FilterPage(
state = bookmarkedFilter,
onClick = onBookmarkedFilterChanged,
)
ScanlatorFilterItem(
active = scanlatorFilterActive,
onClick = onScanlatorFilterClicked,
)
}
@Composable
fun ScanlatorFilterItem(
active: Boolean,
onClick: () -> Unit,
) {
Row(
modifier = Modifier
.clickable(onClick = onClick)
.fillMaxWidth()
.padding(horizontal = TabbedDialogPaddings.Horizontal, vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(24.dp),
) {
Icon(
imageVector = Icons.Outlined.PeopleAlt,
contentDescription = null,
tint = if (active) {
MaterialTheme.colorScheme.active
} else {
LocalContentColor.current
},
)
Text(
text = stringResource(R.string.scanlator),
style = MaterialTheme.typography.bodyMedium,
)
}
}
@Composable

View file

@ -5,11 +5,9 @@ import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.asPaddingValues
@ -28,9 +26,7 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
@ -48,14 +44,12 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.util.fastAll
import androidx.compose.ui.util.fastAny
import androidx.compose.ui.util.fastMap
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.domain.entries.manga.model.chaptersFiltered
import eu.kanade.presentation.entries.DownloadAction
import eu.kanade.presentation.entries.EntryBottomActionMenu
import eu.kanade.presentation.entries.EntryScreenItem
@ -66,6 +60,7 @@ import eu.kanade.presentation.entries.manga.components.ExpandableMangaDescriptio
import eu.kanade.presentation.entries.manga.components.MangaActionRow
import eu.kanade.presentation.entries.manga.components.MangaChapterListItem
import eu.kanade.presentation.entries.manga.components.MangaInfoBox
import eu.kanade.presentation.entries.manga.components.MissingChapterCountListItem
import eu.kanade.presentation.util.formatChapterNumber
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.download.manga.model.MangaDownload
@ -89,7 +84,6 @@ import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.util.isScrolledToEnd
import tachiyomi.presentation.core.util.isScrollingUp
import tachiyomi.presentation.core.util.secondaryItemAlpha
import java.text.DateFormat
import java.util.Date
@ -328,7 +322,7 @@ private fun MangaScreenSmallImpl(
title = state.manga.title,
titleAlphaProvider = { animatedTitleAlpha },
backgroundAlphaProvider = { animatedBgAlpha },
hasFilters = state.manga.chaptersFiltered(),
hasFilters = state.filterActive,
onBackClicked = internalOnBackPressed,
onClickFilter = onFilterClicked,
onClickShare = onShareClicked,
@ -586,7 +580,7 @@ fun MangaScreenLargeImpl(
title = state.manga.title,
titleAlphaProvider = { if (isAnySelected) 1f else 0f },
backgroundAlphaProvider = { 1f },
hasFilters = state.manga.chaptersFiltered(),
hasFilters = state.filterActive,
onBackClicked = internalOnBackPressed,
onClickFilter = onFilterButtonClicked,
onClickShare = onShareClicked,
@ -818,25 +812,7 @@ private fun LazyListScope.sharedChapterItems(
when (item) {
is ChapterList.MissingCount -> {
Row(
modifier = Modifier.padding(
horizontal = MaterialTheme.padding.medium,
vertical = MaterialTheme.padding.small,
),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.medium),
) {
HorizontalDivider(modifier = Modifier.weight(1f))
Text(
text = pluralStringResource(
id = R.plurals.missing_items,
count = item.count,
item.count,
),
modifier = Modifier.secondaryItemAlpha(),
)
HorizontalDivider(modifier = Modifier.weight(1f))
}
MissingChapterCountListItem(count = item.count)
}
is ChapterList.Item -> {
MangaChapterListItem(

View file

@ -0,0 +1,52 @@
package eu.kanade.presentation.entries.manga.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.tooling.preview.PreviewLightDark
import eu.kanade.presentation.theme.TachiyomiTheme
import eu.kanade.tachiyomi.R
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.util.secondaryItemAlpha
@Composable
fun MissingChapterCountListItem(
count: Int,
modifier: Modifier = Modifier,
) {
Row(
modifier = modifier
.padding(
horizontal = MaterialTheme.padding.medium,
vertical = MaterialTheme.padding.small,
)
.secondaryItemAlpha(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.medium),
) {
HorizontalDivider(modifier = Modifier.weight(1f))
Text(
text = pluralStringResource(id = R.plurals.missing_items, count = count, count),
style = MaterialTheme.typography.labelMedium,
)
HorizontalDivider(modifier = Modifier.weight(1f))
}
}
@PreviewLightDark
@Composable
private fun Preview() {
TachiyomiTheme {
Surface {
MissingChapterCountListItem(count = 42)
}
}
}

View file

@ -0,0 +1,132 @@
package eu.kanade.presentation.entries.manga.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.CheckBoxOutlineBlank
import androidx.compose.material.icons.rounded.DisabledByDefault
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.minimumInteractiveComponentSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.toMutableStateList
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.DialogProperties
import eu.kanade.tachiyomi.R
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.util.isScrolledToEnd
import tachiyomi.presentation.core.util.isScrolledToStart
@Composable
fun ScanlatorFilterDialog(
availableScanlators: Set<String>,
excludedScanlators: Set<String>,
onDismissRequest: () -> Unit,
onConfirm: (Set<String>) -> Unit,
) {
val sortedAvailableScanlators = remember(availableScanlators) {
availableScanlators.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it })
}
val mutableExcludedScanlators = remember(excludedScanlators) { excludedScanlators.toMutableStateList() }
AlertDialog(
onDismissRequest = onDismissRequest,
title = { Text(text = stringResource(R.string.exclude_scanlators)) },
text = textFunc@{
if (sortedAvailableScanlators.isEmpty()) {
Text(text = stringResource(R.string.no_scanlators_found))
return@textFunc
}
Box {
val state = rememberLazyListState()
LazyColumn(state = state) {
sortedAvailableScanlators.forEach { scanlator ->
item {
val isExcluded = mutableExcludedScanlators.contains(scanlator)
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clickable {
if (isExcluded) {
mutableExcludedScanlators.remove(scanlator)
} else {
mutableExcludedScanlators.add(scanlator)
}
}
.minimumInteractiveComponentSize()
.clip(MaterialTheme.shapes.small)
.fillMaxWidth()
.padding(horizontal = MaterialTheme.padding.small),
) {
Icon(
imageVector = if (isExcluded) {
Icons.Rounded.DisabledByDefault
} else {
Icons.Rounded.CheckBoxOutlineBlank
},
tint = if (isExcluded) {
MaterialTheme.colorScheme.primary
} else {
LocalContentColor.current
},
contentDescription = null,
)
Text(
text = scanlator,
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.padding(start = 24.dp),
)
}
}
}
}
if (!state.isScrolledToStart()) HorizontalDivider(modifier = Modifier.align(Alignment.TopCenter))
if (!state.isScrolledToEnd()) HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter))
}
},
properties = DialogProperties(
usePlatformDefaultWidth = true,
),
confirmButton = {
if (sortedAvailableScanlators.isEmpty()) {
TextButton(onClick = onDismissRequest) {
Text(text = stringResource(R.string.action_cancel))
}
} else {
FlowRow {
TextButton(onClick = mutableExcludedScanlators::clear) {
Text(text = stringResource(R.string.action_reset))
}
Spacer(modifier = Modifier.weight(1f))
TextButton(onClick = onDismissRequest) {
Text(text = stringResource(R.string.action_cancel))
}
TextButton(
onClick = {
onConfirm(mutableExcludedScanlators.toSet())
onDismissRequest()
},
) {
Text(text = stringResource(R.string.action_ok))
}
}
}
},
)
}

View file

@ -11,11 +11,11 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.unit.dp
import eu.kanade.presentation.theme.TachiyomiTheme
import eu.kanade.tachiyomi.R
import tachiyomi.presentation.core.components.LabeledCheckbox
import tachiyomi.presentation.core.util.ThemePreviews
import kotlin.random.Random
@Composable
@ -94,7 +94,7 @@ fun HistoryDeleteAllDialog(
)
}
@ThemePreviews
@PreviewLightDark
@Composable
private fun HistoryDeleteDialogPreview() {
TachiyomiTheme {

View file

@ -11,6 +11,7 @@ import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
@ -19,6 +20,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import eu.kanade.presentation.entries.ItemCover
@ -28,7 +30,6 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.util.lang.toTimestampString
import tachiyomi.domain.history.anime.model.AnimeHistoryWithRelations
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.util.ThemePreviews
private val HistoryItemHeight = 96.dp
@ -94,13 +95,14 @@ fun AnimeHistoryItem(
}
}
@ThemePreviews
@PreviewLightDark
@Composable
private fun HistoryItemPreviews(
@PreviewParameter(AnimeHistoryWithRelationsProvider::class)
historyWithRelations: AnimeHistoryWithRelations,
) {
TachiyomiTheme {
Surface {
AnimeHistoryItem(
history = historyWithRelations,
onClickCover = {},
@ -109,3 +111,4 @@ private fun HistoryItemPreviews(
)
}
}
}

View file

@ -6,6 +6,7 @@ import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.tooling.preview.PreviewParameter
import eu.kanade.domain.ui.UiPreferences
import eu.kanade.presentation.theme.TachiyomiTheme
@ -17,7 +18,6 @@ import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.components.material.topSmallPaddingValues
import tachiyomi.presentation.core.screens.EmptyScreen
import tachiyomi.presentation.core.screens.LoadingScreen
import tachiyomi.presentation.core.util.ThemePreviews
import java.util.Date
@Composable
@ -70,7 +70,7 @@ sealed interface AnimeHistoryUiModel {
data class Item(val item: AnimeHistoryWithRelations) : AnimeHistoryUiModel
}
@ThemePreviews
@PreviewLightDark
@Composable
internal fun HistoryScreenPreviews(
@PreviewParameter(AnimeHistoryScreenModelStateProvider::class)

View file

@ -11,6 +11,7 @@ import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
@ -19,6 +20,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import eu.kanade.presentation.entries.ItemCover
@ -28,7 +30,6 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.util.lang.toTimestampString
import tachiyomi.domain.history.manga.model.MangaHistoryWithRelations
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.util.ThemePreviews
private val HISTORY_ITEM_HEIGHT = 96.dp
@ -94,13 +95,14 @@ fun MangaHistoryItem(
}
}
@ThemePreviews
@PreviewLightDark
@Composable
internal fun HistoryItemPreviews(
@PreviewParameter(MangaHistoryWithRelationsProvider::class)
historyWithRelations: MangaHistoryWithRelations,
) {
TachiyomiTheme {
Surface {
MangaHistoryItem(
history = historyWithRelations,
onClickCover = {},
@ -109,3 +111,4 @@ internal fun HistoryItemPreviews(
)
}
}
}

View file

@ -6,6 +6,7 @@ import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.tooling.preview.PreviewParameter
import eu.kanade.domain.ui.UiPreferences
import eu.kanade.presentation.theme.TachiyomiTheme
@ -17,7 +18,6 @@ import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.components.material.topSmallPaddingValues
import tachiyomi.presentation.core.screens.EmptyScreen
import tachiyomi.presentation.core.screens.LoadingScreen
import tachiyomi.presentation.core.util.ThemePreviews
import uy.kohesive.injekt.api.get
import java.util.Date
@ -71,7 +71,7 @@ sealed interface MangaHistoryUiModel {
data class Item(val item: MangaHistoryWithRelations) : MangaHistoryUiModel
}
@ThemePreviews
@PreviewLightDark
@Composable
internal fun HistoryScreenPreviews(
@PreviewParameter(MangaHistoryScreenModelStateProvider::class)

View file

@ -5,9 +5,9 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Folder
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.PreviewLightDark
import eu.kanade.presentation.theme.TachiyomiTheme
import tachiyomi.presentation.core.components.Badge
import tachiyomi.presentation.core.util.ThemePreviews
@Composable
fun DownloadsBadge(count: Long) {
@ -47,7 +47,7 @@ fun LanguageBadge(
}
}
@ThemePreviews
@PreviewLightDark
@Composable
private fun BadgePreview() {
TachiyomiTheme {

View file

@ -16,6 +16,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.tooling.preview.PreviewLightDark
import com.halilibo.richtext.markdown.Markdown
import com.halilibo.richtext.ui.RichTextStyle
import com.halilibo.richtext.ui.material3.Material3RichText
@ -24,7 +25,6 @@ import eu.kanade.presentation.theme.TachiyomiTheme
import eu.kanade.tachiyomi.R
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.screens.InfoScreen
import tachiyomi.presentation.core.util.ThemePreviews
@Composable
fun NewUpdateScreen(
@ -67,7 +67,7 @@ fun NewUpdateScreen(
}
}
@ThemePreviews
@PreviewLightDark
@Composable
private fun NewUpdateScreenPreview() {
TachiyomiTheme {

View file

@ -10,9 +10,9 @@ import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.stringResource
import eu.kanade.presentation.more.settings.Preference
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode
import tachiyomi.presentation.core.util.collectAsState
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@ -32,7 +32,7 @@ object SettingsReaderScreen : SearchableSettings {
Preference.PreferenceItem.ListPreference(
pref = readerPref.defaultReadingMode(),
title = stringResource(R.string.pref_viewer_type),
entries = ReadingModeType.entries.drop(1)
entries = ReadingMode.entries.drop(1)
.associate { it.flagValue to stringResource(it.stringRes) },
),
Preference.PreferenceItem.ListPreference(
@ -88,7 +88,7 @@ object SettingsReaderScreen : SearchableSettings {
Preference.PreferenceItem.ListPreference(
pref = readerPreferences.defaultOrientationType(),
title = stringResource(R.string.pref_rotation_type),
entries = OrientationType.entries.drop(1)
entries = ReaderOrientation.entries.drop(1)
.associate { it.flagValue to stringResource(it.stringRes) },
),
Preference.PreferenceItem.ListPreference(

View file

@ -38,6 +38,7 @@ import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.unit.dp
import eu.kanade.domain.ui.model.AppTheme
import eu.kanade.presentation.entries.ItemCover
@ -46,7 +47,6 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.util.system.DeviceUtil
import eu.kanade.tachiyomi.util.system.isDynamicColorAvailable
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.util.ThemePreviews
import tachiyomi.presentation.core.util.secondaryItemAlpha
@Composable
@ -249,11 +249,12 @@ fun AppThemePreviewItem(
}
}
@ThemePreviews
@PreviewLightDark
@Composable
private fun AppThemesListPreview() {
var appTheme by remember { mutableStateOf(AppTheme.DEFAULT) }
TachiyomiTheme {
Surface {
AppThemesList(
currentTheme = appTheme,
amoled = false,
@ -261,3 +262,4 @@ private fun AppThemesListPreview() {
)
}
}
}

View file

@ -12,10 +12,10 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewLightDark
import eu.kanade.presentation.theme.TachiyomiTheme
import eu.kanade.tachiyomi.R
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.util.ThemePreviews
import tachiyomi.presentation.core.util.secondaryItemAlpha
@Composable
@ -40,7 +40,7 @@ internal fun InfoWidget(text: String) {
}
}
@ThemePreviews
@PreviewLightDark
@Composable
private fun InfoWidgetPreview() {
TachiyomiTheme {

View file

@ -9,8 +9,8 @@ import androidx.compose.material3.Switch
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.tooling.preview.PreviewLightDark
import eu.kanade.presentation.theme.TachiyomiTheme
import tachiyomi.presentation.core.util.ThemePreviews
@Composable
fun SwitchPreferenceWidget(
@ -37,7 +37,7 @@ fun SwitchPreferenceWidget(
)
}
@ThemePreviews
@PreviewLightDark
@Composable
private fun SwitchPreferenceWidgetPreview() {
TachiyomiTheme {

View file

@ -12,8 +12,8 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.tooling.preview.PreviewLightDark
import eu.kanade.presentation.theme.TachiyomiTheme
import tachiyomi.presentation.core.util.ThemePreviews
import tachiyomi.presentation.core.util.secondaryItemAlpha
@Composable
@ -59,7 +59,7 @@ fun TextPreferenceWidget(
)
}
@ThemePreviews
@PreviewLightDark
@Composable
private fun TextPreferenceWidgetPreview() {
TachiyomiTheme {

View file

@ -34,6 +34,7 @@ import androidx.compose.ui.text.Placeholder
import androidx.compose.ui.text.PlaceholderVerticalAlign
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.google.common.io.Files.append
@ -45,7 +46,6 @@ import eu.kanade.tachiyomi.data.database.models.manga.toDomainChapter
import eu.kanade.tachiyomi.ui.reader.model.ChapterTransition
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
import tachiyomi.domain.items.service.calculateChapterGap
import tachiyomi.presentation.core.util.ThemePreviews
import tachiyomi.presentation.core.util.secondaryItemAlpha
@Composable
@ -317,7 +317,7 @@ private val FakeChapterLongTitle = previewChapter(
chapterNumber = 1f,
)
@ThemePreviews
@PreviewLightDark
@Composable
private fun TransitionTextPreview() {
TachiyomiTheme {
@ -334,7 +334,7 @@ private fun TransitionTextPreview() {
}
}
@ThemePreviews
@PreviewLightDark
@Composable
private fun TransitionTextLongTitlePreview() {
TachiyomiTheme {
@ -351,7 +351,7 @@ private fun TransitionTextLongTitlePreview() {
}
}
@ThemePreviews
@PreviewLightDark
@Composable
private fun TransitionTextWithGapPreview() {
TachiyomiTheme {
@ -368,7 +368,7 @@ private fun TransitionTextWithGapPreview() {
}
}
@ThemePreviews
@PreviewLightDark
@Composable
private fun TransitionTextNoNextPreview() {
TachiyomiTheme {
@ -382,7 +382,7 @@ private fun TransitionTextNoNextPreview() {
}
}
@ThemePreviews
@PreviewLightDark
@Composable
private fun TransitionTextNoPreviousPreview() {
TachiyomiTheme {

View file

@ -1,46 +1,48 @@
package eu.kanade.presentation.reader
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.unit.dp
import eu.kanade.domain.entries.manga.model.orientationType
import androidx.compose.ui.tooling.preview.PreviewLightDark
import eu.kanade.domain.entries.manga.model.readerOrientation
import eu.kanade.presentation.components.AdaptiveSheet
import eu.kanade.presentation.reader.components.ModeSelectionDialog
import eu.kanade.presentation.theme.TachiyomiTheme
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation
import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel
import tachiyomi.presentation.core.components.SettingsIconGrid
import tachiyomi.presentation.core.components.material.IconToggleButton
import tachiyomi.presentation.core.util.ThemePreviews
private val orientationTypeOptions = OrientationType.entries.map { it.stringRes to it }
private val ReaderOrientationsWithoutDefault = ReaderOrientation.entries - ReaderOrientation.DEFAULT
@Composable
fun OrientationModeSelectDialog(
fun OrientationSelectDialog(
onDismissRequest: () -> Unit,
screenModel: ReaderSettingsScreenModel,
onChange: (Int) -> Unit,
) {
val manga by screenModel.mangaFlow.collectAsState()
val orientationType = remember(manga) {
OrientationType.fromPreference(
manga?.orientationType?.toInt(),
val orientation = remember(manga) {
ReaderOrientation.fromPreference(
manga?.readerOrientation?.toInt(),
)
}
AdaptiveSheet(onDismissRequest = onDismissRequest) {
DialogContent(
orientationType = orientationType,
orientation = orientation,
onChangeOrientation = {
screenModel.onChangeOrientation(it)
onChange(it.stringRes)
@ -52,16 +54,25 @@ fun OrientationModeSelectDialog(
@Composable
private fun DialogContent(
orientationType: OrientationType,
onChangeOrientation: (OrientationType) -> Unit,
orientation: ReaderOrientation,
onChangeOrientation: (ReaderOrientation) -> Unit,
) {
var selected by remember { mutableStateOf(orientation) }
ModeSelectionDialog(
onUseDefault = {
onChangeOrientation(
ReaderOrientation.DEFAULT,
)
}.takeIf { orientation != ReaderOrientation.DEFAULT },
onApply = { onChangeOrientation(selected) },
) {
Box(modifier = Modifier.padding(vertical = 16.dp)) {
SettingsIconGrid(R.string.rotation_type) {
items(OrientationType.entries) { mode ->
items(ReaderOrientationsWithoutDefault) { mode ->
IconToggleButton(
checked = mode == orientationType,
checked = mode == selected,
onCheckedChange = {
onChangeOrientation(mode)
selected = (mode)
},
modifier = Modifier.fillMaxWidth(),
imageVector = ImageVector.vectorResource(mode.iconRes),
@ -72,13 +83,22 @@ private fun DialogContent(
}
}
@ThemePreviews
@PreviewLightDark
@Composable
private fun DialogContentPreview() {
TachiyomiTheme {
Surface {
Column {
DialogContent(
orientationType = OrientationType.DEFAULT,
orientation = ReaderOrientation.DEFAULT,
onChangeOrientation = {},
)
DialogContent(
orientation = ReaderOrientation.FREE,
onChangeOrientation = {},
)
}
}
}
}

View file

@ -2,6 +2,7 @@ package eu.kanade.presentation.reader
import androidx.compose.foundation.layout.Box
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@ -9,9 +10,9 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.unit.sp
import eu.kanade.presentation.theme.TachiyomiTheme
import tachiyomi.presentation.core.util.ThemePreviews
@Composable
fun PageIndicatorText(
@ -48,10 +49,12 @@ fun PageIndicatorText(
}
}
@ThemePreviews
@PreviewLightDark
@Composable
private fun PageIndicatorTextPreview() {
TachiyomiTheme {
Surface {
PageIndicatorText(currentPage = 10, totalPages = 69)
}
}
}

View file

@ -1,30 +1,31 @@
package eu.kanade.presentation.reader
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import eu.kanade.domain.entries.manga.model.readingModeType
import androidx.compose.ui.tooling.preview.PreviewLightDark
import eu.kanade.domain.entries.manga.model.readingMode
import eu.kanade.presentation.components.AdaptiveSheet
import eu.kanade.presentation.reader.components.ModeSelectionDialog
import eu.kanade.presentation.theme.TachiyomiTheme
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode
import tachiyomi.presentation.core.components.SettingsIconGrid
import tachiyomi.presentation.core.components.material.IconToggleButton
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.util.ThemePreviews
private val readingModeOptions = ReadingModeType.entries.map { it.stringRes to it }
private val ReadingModesWithoutDefault = ReadingMode.entries - ReadingMode.DEFAULT
@Composable
fun ReadingModeSelectDialog(
@ -34,8 +35,8 @@ fun ReadingModeSelectDialog(
) {
val manga by screenModel.mangaFlow.collectAsState()
val readingMode = remember(manga) {
ReadingModeType.fromPreference(
manga?.readingModeType?.toInt(),
ReadingMode.fromPreference(
manga?.readingMode?.toInt(),
)
}
@ -53,16 +54,21 @@ fun ReadingModeSelectDialog(
@Composable
private fun DialogContent(
readingMode: ReadingModeType,
onChangeReadingMode: (ReadingModeType) -> Unit,
readingMode: ReadingMode,
onChangeReadingMode: (ReadingMode) -> Unit,
) {
var selected by remember { mutableStateOf(readingMode) }
ModeSelectionDialog(
onUseDefault = { onChangeReadingMode(ReadingMode.DEFAULT) }.takeIf { readingMode != ReadingMode.DEFAULT },
onApply = { onChangeReadingMode(selected) },
) {
Box(modifier = Modifier.padding(vertical = MaterialTheme.padding.medium)) {
SettingsIconGrid(R.string.pref_category_reading_mode) {
items(ReadingModeType.entries) { mode ->
items(ReadingModesWithoutDefault) { mode ->
IconToggleButton(
checked = mode == readingMode,
checked = mode == selected,
onCheckedChange = {
onChangeReadingMode(mode)
selected = (mode)
},
modifier = Modifier.fillMaxWidth(),
imageVector = ImageVector.vectorResource(mode.iconRes),
@ -73,13 +79,22 @@ private fun DialogContent(
}
}
@ThemePreviews
@PreviewLightDark
@Composable
private fun DialogContentPreview() {
TachiyomiTheme {
Surface {
Column {
DialogContent(
readingMode = ReadingModeType.DEFAULT,
readingMode = ReadingMode.DEFAULT,
onChangeReadingMode = {},
)
DialogContent(
readingMode = ReadingMode.LEFT_TO_RIGHT,
onChangeReadingMode = {},
)
}
}
}
}

View file

@ -17,16 +17,16 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation
import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode
@Composable
fun BottomReaderBar(
backgroundColor: Color,
readingMode: ReadingModeType,
readingMode: ReadingMode,
onClickReadingMode: () -> Unit,
orientationMode: OrientationType,
onClickOrientationMode: () -> Unit,
orientation: ReaderOrientation,
onClickOrientation: () -> Unit,
cropEnabled: Boolean,
onClickCropBorder: () -> Unit,
onClickSettings: () -> Unit,
@ -46,19 +46,17 @@ fun BottomReaderBar(
)
}
IconButton(onClick = onClickCropBorder) {
IconButton(onClick = onClickOrientation) {
Icon(
painter = painterResource(
if (cropEnabled) R.drawable.ic_crop_24dp else R.drawable.ic_crop_off_24dp,
),
contentDescription = stringResource(R.string.pref_crop_borders),
painter = painterResource(orientation.iconRes),
contentDescription = stringResource(R.string.rotation_type),
)
}
IconButton(onClick = onClickOrientationMode) {
IconButton(onClick = onClickCropBorder) {
Icon(
painter = painterResource(orientationMode.iconRes),
contentDescription = stringResource(R.string.pref_rotation_type),
painter = painterResource(if (cropEnabled) R.drawable.ic_crop_24dp else R.drawable.ic_crop_off_24dp),
contentDescription = stringResource(R.string.pref_crop_borders),
)
}

View file

@ -23,9 +23,10 @@ import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.AppBarActions
import eu.kanade.presentation.reader.components.ChapterNavigator
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation
import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode
import eu.kanade.tachiyomi.ui.reader.viewer.Viewer
import eu.kanade.tachiyomi.ui.reader.viewer.pager.R2LPagerViewer
@ -54,10 +55,10 @@ fun ReaderAppBars(
totalPages: Int,
onSliderValueChange: (Int) -> Unit,
readingMode: ReadingModeType,
readingMode: ReadingMode,
onClickReadingMode: () -> Unit,
orientationMode: OrientationType,
onClickOrientationMode: () -> Unit,
orientation: ReaderOrientation,
onClickOrientation: () -> Unit,
cropEnabled: Boolean,
onClickCropBorder: () -> Unit,
onClickSettings: () -> Unit,
@ -155,8 +156,8 @@ fun ReaderAppBars(
backgroundColor = backgroundColor,
readingMode = readingMode,
onClickReadingMode = onClickReadingMode,
orientationMode = orientationMode,
onClickOrientationMode = onClickOrientationMode,
orientation = orientation,
onClickOrientation = onClickOrientation,
cropEnabled = cropEnabled,
onClickCropBorder = onClickCropBorder,
onClickSettings = onClickSettings,

View file

@ -1,4 +1,4 @@
package eu.kanade.presentation.reader.appbars
package eu.kanade.presentation.reader.components
import androidx.compose.foundation.background
import androidx.compose.foundation.interaction.MutableInteractionSource

View file

@ -0,0 +1,89 @@
package eu.kanade.presentation.reader.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Check
import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.Icon
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.unit.dp
import eu.kanade.presentation.theme.TachiyomiTheme
import eu.kanade.tachiyomi.R
import tachiyomi.presentation.core.components.SettingsItemsPaddings
@Composable
fun ModeSelectionDialog(
onApply: () -> Unit,
onUseDefault: (() -> Unit)? = null,
content: @Composable () -> Unit,
) {
Box(modifier = Modifier.padding(vertical = 16.dp)) {
Column {
content()
Row(
modifier = Modifier.padding(
horizontal = SettingsItemsPaddings.Horizontal,
),
) {
onUseDefault?.let {
OutlinedButton(onClick = it) {
Text(text = stringResource(R.string.action_revert_to_default))
}
}
Spacer(modifier = Modifier.weight(1f))
FilledTonalButton(
onClick = onApply,
) {
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Icon(
imageVector = Icons.Outlined.Check,
contentDescription = null,
)
Text(text = stringResource(R.string.action_apply))
}
}
}
}
}
}
@PreviewLightDark
@Composable
private fun Preview() {
TachiyomiTheme {
Surface {
Column {
ModeSelectionDialog(
onApply = {},
onUseDefault = {},
) {
Text("Dummy content")
}
ModeSelectionDialog(
onApply = {},
) {
Text("Dummy content without default")
}
}
}
}
}

View file

@ -8,13 +8,13 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.res.stringResource
import eu.kanade.domain.entries.manga.model.orientationType
import eu.kanade.domain.entries.manga.model.readingModeType
import eu.kanade.domain.entries.manga.model.readerOrientation
import eu.kanade.domain.entries.manga.model.readingMode
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode
import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonViewer
import tachiyomi.presentation.core.components.CheckboxItem
import tachiyomi.presentation.core.components.HeadingItem
@ -23,41 +23,37 @@ import tachiyomi.presentation.core.components.SliderItem
import tachiyomi.presentation.core.util.collectAsState
import java.text.NumberFormat
private val readingModeOptions = ReadingModeType.entries.map { it.stringRes to it }
private val orientationTypeOptions = OrientationType.entries.map { it.stringRes to it }
private val tappingInvertModeOptions = ReaderPreferences.TappingInvertMode.entries.map { it.titleResId to it }
@Composable
internal fun ColumnScope.ReadingModePage(screenModel: ReaderSettingsScreenModel) {
HeadingItem(R.string.pref_category_for_this_series)
val manga by screenModel.mangaFlow.collectAsState()
val readingMode = remember(manga) {
ReadingModeType.fromPreference(
manga?.readingModeType?.toInt(),
ReadingMode.fromPreference(
manga?.readingMode?.toInt(),
)
}
SettingsChipRow(R.string.pref_category_reading_mode) {
readingModeOptions.map { (stringRes, it) ->
ReadingMode.entries.map {
FilterChip(
selected = it == readingMode,
onClick = { screenModel.onChangeReadingMode(it) },
label = { Text(stringResource(stringRes)) },
label = { Text(stringResource(it.stringRes)) },
)
}
}
val orientationType = remember(manga) {
OrientationType.fromPreference(
manga?.orientationType?.toInt(),
val orientation = remember(manga) {
ReaderOrientation.fromPreference(
manga?.readerOrientation?.toInt(),
)
}
SettingsChipRow(R.string.rotation_type) {
orientationTypeOptions.map { (stringRes, it) ->
ReaderOrientation.entries.map {
FilterChip(
selected = it == orientationType,
selected = it == orientation,
onClick = { screenModel.onChangeOrientation(it) },
label = { Text(stringResource(stringRes)) },
label = { Text(stringResource(it.stringRes)) },
)
}
}
@ -217,11 +213,11 @@ private fun ColumnScope.TapZonesItems(
if (selected != 5) {
SettingsChipRow(R.string.pref_read_with_tapping_inverted) {
tappingInvertModeOptions.map { (stringRes, mode) ->
ReaderPreferences.TappingInvertMode.entries.map {
FilterChip(
selected = mode == invertMode,
onClick = { onSelectInvertMode(mode) },
label = { Text(stringResource(stringRes)) },
selected = it == invertMode,
onClick = { onSelectInvertMode(it) },
label = { Text(stringResource(it.titleResId)) },
)
}
}

View file

@ -21,6 +21,7 @@ import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton
import androidx.compose.material3.SelectableDates
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.rememberDatePickerState
@ -31,6 +32,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.unit.dp
import eu.kanade.presentation.theme.TachiyomiTheme
import eu.kanade.tachiyomi.R
@ -39,7 +41,6 @@ import tachiyomi.presentation.core.components.WheelNumberPicker
import tachiyomi.presentation.core.components.WheelTextPicker
import tachiyomi.presentation.core.components.material.AlertDialogContent
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.util.ThemePreviews
import tachiyomi.presentation.core.util.isScrolledToEnd
import tachiyomi.presentation.core.util.isScrolledToStart
@ -238,10 +239,11 @@ fun BaseSelector(
)
}
@ThemePreviews
@PreviewLightDark
@Composable
private fun TrackStatusSelectorPreviews() {
TachiyomiTheme {
Surface {
TrackStatusSelector(
selection = 1,
onSelectionChange = {},
@ -261,3 +263,4 @@ private fun TrackStatusSelectorPreviews() {
)
}
}
}

View file

@ -32,6 +32,7 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import eu.kanade.domain.track.anime.model.toDbTrack
@ -43,7 +44,6 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.track.Tracker
import eu.kanade.tachiyomi.ui.entries.anime.track.AnimeTrackItem
import eu.kanade.tachiyomi.util.system.copyToClipboard
import tachiyomi.presentation.core.util.ThemePreviews
import java.text.DateFormat
private const val UnsetStatusTextAlpha = 0.5F
@ -250,7 +250,7 @@ private fun TrackInfoItemEmpty(
}
}
@ThemePreviews
@PreviewLightDark
@Composable
private fun TrackInfoDialogHomePreviews(
@PreviewParameter(AnimeTrackInfoDialogHomePreviewProvider::class)

View file

@ -42,6 +42,7 @@ import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.intl.Locale
import androidx.compose.ui.text.toLowerCase
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import eu.kanade.presentation.theme.TachiyomiTheme
@ -52,7 +53,6 @@ import tachiyomi.presentation.core.components.ScrollbarLazyColumn
import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.screens.EmptyScreen
import tachiyomi.presentation.core.screens.LoadingScreen
import tachiyomi.presentation.core.util.ThemePreviews
import tachiyomi.presentation.core.util.plus
import tachiyomi.presentation.core.util.runOnEnterKeyPressed
@ -201,7 +201,7 @@ fun AnimeTrackerSearch(
}
}
@ThemePreviews
@PreviewLightDark
@Composable
private fun TrackerSearchPreviews(
@PreviewParameter(AnimeTrackerSearchPreviewProvider::class)

View file

@ -11,11 +11,11 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import eu.kanade.presentation.theme.TachiyomiTheme
import eu.kanade.tachiyomi.data.track.Tracker
import tachiyomi.presentation.core.util.ThemePreviews
import tachiyomi.presentation.core.util.clickableNoIndication
@Composable
@ -43,7 +43,7 @@ fun TrackLogoIcon(
}
}
@ThemePreviews
@PreviewLightDark
@Composable
private fun TrackLogoIconPreviews(
@PreviewParameter(TrackLogoIconPreviewProvider::class)

View file

@ -44,6 +44,7 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import eu.kanade.domain.track.manga.model.toDbTrack
@ -54,7 +55,6 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.track.Tracker
import eu.kanade.tachiyomi.ui.entries.manga.track.MangaTrackItem
import eu.kanade.tachiyomi.util.system.copyToClipboard
import tachiyomi.presentation.core.util.ThemePreviews
import java.text.DateFormat
private const val UnsetStatusTextAlpha = 0.5F
@ -322,7 +322,7 @@ fun TrackInfoItemMenu(
}
}
@ThemePreviews
@PreviewLightDark
@Composable
private fun TrackInfoDialogHomePreviews(
@PreviewParameter(MangaTrackInfoDialogHomePreviewProvider::class)

View file

@ -38,6 +38,7 @@ import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
@ -57,6 +58,7 @@ import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.intl.Locale
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.text.toLowerCase
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import eu.kanade.presentation.entries.ItemCover
@ -68,7 +70,6 @@ import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.screens.EmptyScreen
import tachiyomi.presentation.core.screens.LoadingScreen
import tachiyomi.presentation.core.util.ThemePreviews
import tachiyomi.presentation.core.util.plus
import tachiyomi.presentation.core.util.runOnEnterKeyPressed
import tachiyomi.presentation.core.util.secondaryItemAlpha
@ -326,11 +327,15 @@ fun SearchResultItemDetails(
}
}
@ThemePreviews
@PreviewLightDark
@Composable
private fun TrackerSearchPreviews(
@PreviewParameter(MangaTrackerSearchPreviewProvider::class)
content: @Composable () -> Unit,
) {
TachiyomiTheme { content() }
TachiyomiTheme {
Surface {
content()
}
}
}

View file

@ -14,7 +14,7 @@ import eu.kanade.tachiyomi.data.track.TrackerManager
import eu.kanade.tachiyomi.network.NetworkPreferences
import eu.kanade.tachiyomi.network.PREF_DOH_CLOUDFLARE
import eu.kanade.tachiyomi.ui.player.settings.PlayerPreferences
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
import eu.kanade.tachiyomi.util.system.DeviceUtil
import eu.kanade.tachiyomi.util.system.toast
@ -190,12 +190,12 @@ object Migrations {
if (oldVersion < 60) {
// Migrate Rotation and Viewer values to default values for viewer_flags
val newOrientation = when (prefs.getInt("pref_rotation_type_key", 1)) {
1 -> OrientationType.FREE.flagValue
2 -> OrientationType.PORTRAIT.flagValue
3 -> OrientationType.LANDSCAPE.flagValue
4 -> OrientationType.LOCKED_PORTRAIT.flagValue
5 -> OrientationType.LOCKED_LANDSCAPE.flagValue
else -> OrientationType.FREE.flagValue
1 -> ReaderOrientation.FREE.flagValue
2 -> ReaderOrientation.PORTRAIT.flagValue
3 -> ReaderOrientation.LANDSCAPE.flagValue
4 -> ReaderOrientation.LOCKED_PORTRAIT.flagValue
5 -> ReaderOrientation.LOCKED_LANDSCAPE.flagValue
else -> ReaderOrientation.FREE.flagValue
}
// Reading mode flag and prefValue is the same value

View file

@ -13,6 +13,7 @@ import eu.kanade.tachiyomi.data.backup.models.BackupAnime
import eu.kanade.tachiyomi.data.backup.models.BackupAnimeHistory
import eu.kanade.tachiyomi.data.backup.models.BackupAnimeSource
import eu.kanade.tachiyomi.data.backup.models.BackupCategory
import eu.kanade.tachiyomi.data.backup.models.BackupChapter
import eu.kanade.tachiyomi.data.backup.models.BackupExtension
import eu.kanade.tachiyomi.data.backup.models.BackupExtensionPreferences
import eu.kanade.tachiyomi.data.backup.models.BackupHistory
@ -240,15 +241,15 @@ class BackupCreator(
// Check if user wants chapter information in backup
if (options and BackupConst.BACKUP_CHAPTER_MASK == BackupConst.BACKUP_CHAPTER) {
// Backup all the chapters
val chapters = mangaHandler.awaitList {
mangaHandler.awaitList {
chaptersQueries.getChaptersByMangaId(
manga.id,
backupChapterMapper,
mangaId = manga.id,
applyScanlatorFilter = 0, // false
mapper = backupChapterMapper,
)
}
if (chapters.isNotEmpty()) {
mangaObject.chapters = chapters
}
.takeUnless(List<BackupChapter>::isEmpty)
?.let { mangaObject.chapters = it }
}
// Check if user wants category information in backup

View file

@ -1,7 +1,7 @@
package eu.kanade.tachiyomi.data.backup.models
import eu.kanade.tachiyomi.model.UpdateStrategy
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import tachiyomi.domain.entries.manga.model.Manga
@ -89,7 +89,7 @@ data class BackupManga(
favorite = manga.favorite,
source = manga.source,
dateAdded = manga.dateAdded,
viewer = (manga.viewerFlags.toInt() and ReadingModeType.MASK),
viewer = (manga.viewerFlags.toInt() and ReadingMode.MASK),
viewer_flags = manga.viewerFlags.toInt(),
chapterFlags = manga.chapterFlags.toInt(),
updateStrategy = manga.updateStrategy,

View file

@ -1,7 +1,6 @@
package eu.kanade.tachiyomi.data.updater
import android.content.Context
import android.os.Build
import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.util.system.isInstalledFromFDroid
import tachiyomi.core.util.lang.withIOContext
@ -13,10 +12,10 @@ class AppUpdateChecker {
private val getApplicationRelease: GetApplicationRelease by injectLazy()
suspend fun checkForUpdate(context: Context, forceCheck: Boolean = false): GetApplicationRelease.Result {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
return GetApplicationRelease.Result.OsTooOld
}
// Disabling app update checks for older Android versions that we're going to drop support for
// if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
// return GetApplicationRelease.Result.OsTooOld
// }
return withIOContext {
val result = getApplicationRelease.await(

View file

@ -9,8 +9,10 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalContext
@ -31,6 +33,7 @@ import eu.kanade.presentation.entries.manga.ChapterSettingsDialog
import eu.kanade.presentation.entries.manga.DuplicateMangaDialog
import eu.kanade.presentation.entries.manga.MangaScreen
import eu.kanade.presentation.entries.manga.components.MangaCoverDialog
import eu.kanade.presentation.entries.manga.components.ScanlatorFilterDialog
import eu.kanade.presentation.util.AssistContentScreen
import eu.kanade.presentation.util.Screen
import eu.kanade.presentation.util.isTabletUi
@ -153,6 +156,8 @@ class MangaScreen(
onInvertSelection = screenModel::invertSelection,
)
var showScanlatorsDialog by remember { mutableStateOf(false) }
val onDismissRequest = {
screenModel.dismissDialog()
if (screenModel.autoOpenTrack && screenModel.isFromChangeCategory) {
@ -197,6 +202,8 @@ class MangaScreen(
onDisplayModeChanged = screenModel::setDisplayMode,
onSetAsDefault = screenModel::setCurrentSettingsAsDefault,
onResetToDefault = screenModel::resetToDefaultSettings,
scanlatorFilterActive = successState.scanlatorFilterActive,
onScanlatorFilterClicked = { showScanlatorsDialog = true },
)
MangaScreenModel.Dialog.TrackSheet -> {
NavigatorAdaptiveSheet(
@ -245,6 +252,15 @@ class MangaScreen(
)
}
}
if (showScanlatorsDialog) {
ScanlatorFilterDialog(
availableScanlators = successState.availableScanlators,
excludedScanlators = successState.excludedScanlators,
onDismissRequest = { showScanlatorsDialog = false },
onConfirm = screenModel::setExcludedScanlators,
)
}
}
private fun continueReading(context: Context, unreadChapter: Chapter?) {

View file

@ -11,9 +11,13 @@ import cafe.adriel.voyager.core.model.screenModelScope
import eu.kanade.core.preference.asState
import eu.kanade.core.util.addOrRemove
import eu.kanade.core.util.insertSeparators
import eu.kanade.domain.entries.manga.interactor.GetExcludedScanlators
import eu.kanade.domain.entries.manga.interactor.SetExcludedScanlators
import eu.kanade.domain.entries.manga.interactor.UpdateManga
import eu.kanade.domain.entries.manga.model.chaptersFiltered
import eu.kanade.domain.entries.manga.model.downloadedFilter
import eu.kanade.domain.entries.manga.model.toSManga
import eu.kanade.domain.items.chapter.interactor.GetAvailableScanlators
import eu.kanade.domain.items.chapter.interactor.SetReadStatus
import eu.kanade.domain.items.chapter.interactor.SyncChaptersWithSource
import eu.kanade.domain.track.manga.interactor.AddMangaTracks
@ -95,6 +99,9 @@ class MangaScreenModel(
private val downloadCache: MangaDownloadCache = Injekt.get(),
private val getMangaAndChapters: GetMangaWithChapters = Injekt.get(),
private val getDuplicateLibraryManga: GetDuplicateLibraryManga = Injekt.get(),
private val getAvailableScanlators: GetAvailableScanlators = Injekt.get(),
private val getExcludedScanlators: GetExcludedScanlators = Injekt.get(),
private val setExcludedScanlators: SetExcludedScanlators = Injekt.get(),
private val setMangaChapterFlags: SetMangaChapterFlags = Injekt.get(),
private val setMangaDefaultChapterFlags: SetMangaDefaultChapterFlags = Injekt.get(),
private val setReadStatus: SetReadStatus = Injekt.get(),
@ -162,7 +169,7 @@ class MangaScreenModel(
init {
screenModelScope.launchIO {
combine(
getMangaAndChapters.subscribe(mangaId).distinctUntilChanged(),
getMangaAndChapters.subscribe(mangaId, applyScanlatorFilter = true).distinctUntilChanged(),
downloadCache.changes,
downloadManager.queueState,
) { mangaAndChapters, _, _ -> mangaAndChapters }
@ -176,11 +183,31 @@ class MangaScreenModel(
}
}
screenModelScope.launchIO {
getExcludedScanlators.subscribe(mangaId)
.distinctUntilChanged()
.collectLatest { excludedScanlators ->
updateSuccessState {
it.copy(excludedScanlators = excludedScanlators)
}
}
}
screenModelScope.launchIO {
getAvailableScanlators.subscribe(mangaId)
.distinctUntilChanged()
.collectLatest { availableScanlators ->
updateSuccessState {
it.copy(availableScanlators = availableScanlators)
}
}
}
observeDownloads()
screenModelScope.launchIO {
val manga = getMangaAndChapters.awaitManga(mangaId)
val chapters = getMangaAndChapters.awaitChapters(mangaId)
val chapters = getMangaAndChapters.awaitChapters(mangaId, applyScanlatorFilter = true)
.toChapterListItems(manga)
if (!manga.favorite) {
@ -197,6 +224,8 @@ class MangaScreenModel(
source = Injekt.get<MangaSourceManager>().getOrStub(manga.source),
isFromSource = isFromSource,
chapters = chapters,
availableScanlators = getAvailableScanlators.await(mangaId),
excludedScanlators = getExcludedScanlators.await(mangaId),
isRefreshingData = needRefreshInfo || needRefreshChapter,
dialog = null,
)
@ -1028,6 +1057,12 @@ class MangaScreenModel(
updateSuccessState { it.copy(dialog = Dialog.FullCover) }
}
fun setExcludedScanlators(excludedScanlators: Set<String>) {
screenModelScope.launchIO {
setExcludedScanlators.await(mangaId, excludedScanlators)
}
}
sealed interface State {
@Immutable
data object Loading : State
@ -1038,12 +1073,13 @@ class MangaScreenModel(
val source: MangaSource,
val isFromSource: Boolean,
val chapters: List<ChapterList.Item>,
val availableScanlators: Set<String>,
val excludedScanlators: Set<String>,
val trackItems: List<MangaTrackItem> = emptyList(),
val isRefreshingData: Boolean = false,
val dialog: Dialog? = null,
val hasPromptedToAddBefore: Boolean = false,
) : State {
val processedChapters by lazy {
chapters.applyFilters(manga).toList()
}
@ -1075,6 +1111,12 @@ class MangaScreenModel(
}
}
val scanlatorFilterActive: Boolean
get() = excludedScanlators.intersect(availableScanlators).isNotEmpty()
val filterActive: Boolean
get() = scanlatorFilterActive || manga.chaptersFiltered()
val trackingAvailable: Boolean
get() = trackItems.isNotEmpty()

View file

@ -416,7 +416,7 @@ class MangaLibraryScreenModel(
}
suspend fun getNextUnreadChapter(manga: Manga): Chapter? {
return getChaptersByMangaId.await(manga.id).getNextUnread(manga, downloadManager)
return getChaptersByMangaId.await(manga.id, applyScanlatorFilter = true).getNextUnread(manga, downloadManager)
}
/**

View file

@ -43,7 +43,7 @@ import dev.chrisbanes.insetter.applyInsetter
import eu.kanade.domain.base.BasePreferences
import eu.kanade.presentation.reader.BrightnessOverlay
import eu.kanade.presentation.reader.DisplayRefreshHost
import eu.kanade.presentation.reader.OrientationModeSelectDialog
import eu.kanade.presentation.reader.OrientationSelectDialog
import eu.kanade.presentation.reader.PageIndicatorText
import eu.kanade.presentation.reader.ReaderPageActionsDialog
import eu.kanade.presentation.reader.ReadingModeSelectDialog
@ -63,10 +63,10 @@ import eu.kanade.tachiyomi.ui.reader.ReaderViewModel.SetAsCoverResult.Success
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode
import eu.kanade.tachiyomi.ui.reader.viewer.ReaderProgressIndicator
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
import eu.kanade.tachiyomi.util.system.hasDisplayCutout
@ -338,7 +338,7 @@ class ReaderActivity : BaseActivity() {
val cropBorderPaged by readerPreferences.cropBorders().collectAsState()
val cropBorderWebtoon by readerPreferences.cropBordersWebtoon().collectAsState()
val isPagerType = ReadingModeType.isPagerType(viewModel.getMangaReadingMode())
val isPagerType = ReadingMode.isPagerType(viewModel.getMangaReadingMode())
val cropEnabled = if (isPagerType) cropBorderPaged else cropBorderWebtoon
ReaderAppBars(
@ -367,14 +367,14 @@ class ReaderActivity : BaseActivity() {
moveToPageIndex(it)
},
readingMode = ReadingModeType.fromPreference(
readingMode = ReadingMode.fromPreference(
viewModel.getMangaReadingMode(resolveDefault = false),
),
onClickReadingMode = viewModel::openReadingModeSelectDialog,
orientationMode = OrientationType.fromPreference(
viewModel.getMangaOrientationType(resolveDefault = false),
orientation = ReaderOrientation.fromPreference(
viewModel.getMangaOrientation(resolveDefault = false),
),
onClickOrientationMode = viewModel::openOrientationModeSelectDialog,
onClickOrientation = viewModel::openOrientationModeSelectDialog,
cropEnabled = cropEnabled,
onClickCropBorder = {
val enabled = viewModel.toggleCropBorders()
@ -432,7 +432,7 @@ class ReaderActivity : BaseActivity() {
)
}
is ReaderViewModel.Dialog.OrientationModeSelect -> {
OrientationModeSelectDialog(
OrientationSelectDialog(
onDismissRequest = onDismissRequest,
screenModel = settingsScreenModel,
onChange = { stringRes ->
@ -489,15 +489,15 @@ class ReaderActivity : BaseActivity() {
*/
private fun setManga(manga: Manga) {
val prevViewer = viewModel.state.value.viewer
val newViewer = ReadingModeType.toViewer(viewModel.getMangaReadingMode(), this)
val newViewer = ReadingMode.toViewer(viewModel.getMangaReadingMode(), this)
if (window.sharedElementEnterTransition is MaterialContainerTransform) {
// Wait until transition is complete to avoid crash on API 26
window.sharedElementEnterTransition.doOnEnd {
setOrientation(viewModel.getMangaOrientationType())
setOrientation(viewModel.getMangaOrientation())
}
} else {
setOrientation(viewModel.getMangaOrientationType())
setOrientation(viewModel.getMangaOrientation())
}
// Destroy previous viewer if there was one
@ -550,7 +550,7 @@ class ReaderActivity : BaseActivity() {
private fun showReadingModeToast(mode: Int) {
try {
readingModeToast?.cancel()
readingModeToast = toast(ReadingModeType.fromPreference(mode).stringRes)
readingModeToast = toast(ReadingMode.fromPreference(mode).stringRes)
} catch (e: ArrayIndexOutOfBoundsException) {
logcat(LogPriority.ERROR) { "Unknown reading mode: $mode" }
}
@ -728,7 +728,7 @@ class ReaderActivity : BaseActivity() {
* Forces the user preferred [orientation] on the activity.
*/
private fun setOrientation(orientation: Int) {
val newOrientation = OrientationType.fromPreference(orientation)
val newOrientation = ReaderOrientation.fromPreference(orientation)
if (newOrientation.flag != requestedOrientation) {
requestedOrientation = newOrientation.flag
}

View file

@ -9,8 +9,8 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.entries.manga.interactor.SetMangaViewerFlags
import eu.kanade.domain.entries.manga.model.orientationType
import eu.kanade.domain.entries.manga.model.readingModeType
import eu.kanade.domain.entries.manga.model.readerOrientation
import eu.kanade.domain.entries.manga.model.readingMode
import eu.kanade.domain.items.chapter.model.toDbChapter
import eu.kanade.domain.track.manga.interactor.TrackChapter
import eu.kanade.domain.track.service.TrackPreferences
@ -29,9 +29,9 @@ import eu.kanade.tachiyomi.ui.reader.model.InsertPage
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode
import eu.kanade.tachiyomi.ui.reader.viewer.Viewer
import eu.kanade.tachiyomi.util.chapter.filterDownloadedChapters
import eu.kanade.tachiyomi.util.chapter.removeDuplicates
@ -147,7 +147,7 @@ class ReaderViewModel @JvmOverloads constructor(
*/
private val chapterList by lazy {
val manga = manga!!
val chapters = runBlocking { getChaptersByMangaId.await(manga.id) }
val chapters = runBlocking { getChaptersByMangaId.await(manga.id, applyScanlatorFilter = true) }
val selectedChapter = chapters.find { it.id == chapterId }
?: error("Requested chapter of id $chapterId not found in chapter list")
@ -642,22 +642,22 @@ class ReaderViewModel @JvmOverloads constructor(
*/
fun getMangaReadingMode(resolveDefault: Boolean = true): Int {
val default = readerPreferences.defaultReadingMode().get()
val readingMode = ReadingModeType.fromPreference(manga?.readingModeType?.toInt())
val readingMode = ReadingMode.fromPreference(manga?.readingMode?.toInt())
return when {
resolveDefault && readingMode == ReadingModeType.DEFAULT -> default
else -> manga?.readingModeType?.toInt() ?: default
resolveDefault && readingMode == ReadingMode.DEFAULT -> default
else -> manga?.readingMode?.toInt() ?: default
}
}
/**
* Updates the viewer position for the open manga.
*/
fun setMangaReadingMode(readingModeType: ReadingModeType) {
fun setMangaReadingMode(readingMode: ReadingMode) {
val manga = manga ?: return
runBlocking(Dispatchers.IO) {
setMangaViewerFlags.awaitSetReadingMode(
manga.id,
readingModeType.flagValue.toLong(),
readingMode.flagValue.toLong(),
)
val currChapters = state.value.viewerChapters
if (currChapters != null) {
@ -679,22 +679,22 @@ class ReaderViewModel @JvmOverloads constructor(
/**
* Returns the orientation type used by this manga or the default one.
*/
fun getMangaOrientationType(resolveDefault: Boolean = true): Int {
fun getMangaOrientation(resolveDefault: Boolean = true): Int {
val default = readerPreferences.defaultOrientationType().get()
val orientation = OrientationType.fromPreference(manga?.orientationType?.toInt())
val orientation = ReaderOrientation.fromPreference(manga?.readerOrientation?.toInt())
return when {
resolveDefault && orientation == OrientationType.DEFAULT -> default
else -> manga?.orientationType?.toInt() ?: default
resolveDefault && orientation == ReaderOrientation.DEFAULT -> default
else -> manga?.readerOrientation?.toInt() ?: default
}
}
/**
* Updates the orientation type for the open manga.
*/
fun setMangaOrientationType(rotationType: OrientationType) {
fun setMangaOrientationType(orientation: ReaderOrientation) {
val manga = manga ?: return
viewModelScope.launchIO {
setMangaViewerFlags.awaitSetOrientation(manga.id, rotationType.flagValue.toLong())
setMangaViewerFlags.awaitSetOrientation(manga.id, orientation.flagValue.toLong())
val currChapters = state.value.viewerChapters
if (currChapters != null) {
// Save current page
@ -707,14 +707,14 @@ class ReaderViewModel @JvmOverloads constructor(
viewerChapters = currChapters,
)
}
eventChannel.send(Event.SetOrientation(getMangaOrientationType()))
eventChannel.send(Event.SetOrientation(getMangaOrientation()))
eventChannel.send(Event.ReloadViewerChapters)
}
}
}
fun toggleCropBorders(): Boolean {
val isPagerType = ReadingModeType.isPagerType(getMangaReadingMode())
val isPagerType = ReadingMode.isPagerType(getMangaReadingMode())
return if (isPagerType) {
readerPreferences.cropBorders().toggle()
} else {

View file

@ -5,7 +5,7 @@ import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import eu.kanade.tachiyomi.R
enum class OrientationType(
enum class ReaderOrientation(
val flag: Int,
@StringRes val stringRes: Int,
@DrawableRes val iconRes: Int,
@ -58,6 +58,6 @@ enum class OrientationType(
companion object {
const val MASK = 0x00000038
fun fromPreference(preference: Int?): OrientationType = entries.find { it.flagValue == preference } ?: DEFAULT
fun fromPreference(preference: Int?): ReaderOrientation = entries.find { it.flagValue == preference } ?: DEFAULT
}
}

View file

@ -32,12 +32,12 @@ class ReaderPreferences(
fun defaultReadingMode() = preferenceStore.getInt(
"pref_default_reading_mode_key",
ReadingModeType.RIGHT_TO_LEFT.flagValue,
ReadingMode.RIGHT_TO_LEFT.flagValue,
)
fun defaultOrientationType() = preferenceStore.getInt(
"pref_default_orientation_type_key",
OrientationType.FREE.flagValue,
ReaderOrientation.FREE.flagValue,
)
fun webtoonDoubleTapZoomEnabled() = preferenceStore.getBoolean(

View file

@ -14,8 +14,8 @@ import uy.kohesive.injekt.api.get
class ReaderSettingsScreenModel(
readerState: StateFlow<ReaderViewModel.State>,
val hasDisplayCutout: Boolean,
val onChangeReadingMode: (ReadingModeType) -> Unit,
val onChangeOrientation: (OrientationType) -> Unit,
val onChangeReadingMode: (ReadingMode) -> Unit,
val onChangeOrientation: (ReaderOrientation) -> Unit,
val preferences: ReaderPreferences = Injekt.get(),
) : ScreenModel {

View file

@ -10,10 +10,12 @@ import eu.kanade.tachiyomi.ui.reader.viewer.pager.R2LPagerViewer
import eu.kanade.tachiyomi.ui.reader.viewer.pager.VerticalPagerViewer
import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonViewer
enum class ReadingModeType(
enum class ReadingMode(
@StringRes val stringRes: Int,
@DrawableRes val iconRes: Int,
val flagValue: Int,
val direction: Direction? = null,
val type: ViewerType? = null,
) {
DEFAULT(R.string.label_default, R.drawable.ic_reader_default_24dp, 0x00000000),
LEFT_TO_RIGHT(R.string.left_to_right_viewer, R.drawable.ic_reader_ltr_24dp, 0x00000001),
@ -30,11 +32,11 @@ enum class ReadingModeType(
companion object {
const val MASK = 0x00000007
fun fromPreference(preference: Int?): ReadingModeType = entries.find { it.flagValue == preference } ?: DEFAULT
fun fromPreference(preference: Int?): ReadingMode = entries.find { it.flagValue == preference } ?: DEFAULT
fun isPagerType(preference: Int): Boolean {
val mode = fromPreference(preference)
return mode == LEFT_TO_RIGHT || mode == RIGHT_TO_LEFT || mode == VERTICAL
return mode.type is ViewerType.Pager
}
fun toViewer(preference: Int?, activity: ReaderActivity): Viewer {
@ -50,4 +52,14 @@ enum class ReadingModeType(
}
}
}
sealed interface Direction {
data object Horizontal : Direction
data object Vertical : Direction
}
sealed interface ViewerType {
data object Pager : ViewerType
data object Webtoon : ViewerType
}
}

View file

@ -2,6 +2,7 @@ package tachiyomi.data.items.chapter
import kotlinx.coroutines.flow.Flow
import logcat.LogPriority
import tachiyomi.core.util.lang.toLong
import tachiyomi.core.util.system.logcat
import tachiyomi.data.handlers.manga.MangaDatabaseHandler
import tachiyomi.domain.items.chapter.model.Chapter
@ -76,8 +77,22 @@ class ChapterRepositoryImpl(
}
}
override suspend fun getChapterByMangaId(mangaId: Long): List<Chapter> {
return handler.awaitList { chaptersQueries.getChaptersByMangaId(mangaId, ::mapChapter) }
override suspend fun getChapterByMangaId(mangaId: Long, applyScanlatorFilter: Boolean): List<Chapter> {
return handler.awaitList {
chaptersQueries.getChaptersByMangaId(mangaId, applyScanlatorFilter.toLong(), ::mapChapter)
}
}
override suspend fun getScanlatorsByMangaId(mangaId: Long): List<String> {
return handler.awaitList {
chaptersQueries.getScanlatorsByMangaId(mangaId) { it.orEmpty() }
}
}
override fun getScanlatorsByMangaIdAsFlow(mangaId: Long): Flow<List<String>> {
return handler.subscribeToList {
chaptersQueries.getScanlatorsByMangaId(mangaId) { it.orEmpty() }
}
}
override suspend fun getBookmarkedChaptersByMangaId(mangaId: Long): List<Chapter> {
@ -93,12 +108,9 @@ class ChapterRepositoryImpl(
return handler.awaitOneOrNull { chaptersQueries.getChapterById(id, ::mapChapter) }
}
override suspend fun getChapterByMangaIdAsFlow(mangaId: Long): Flow<List<Chapter>> {
override suspend fun getChapterByMangaIdAsFlow(mangaId: Long, applyScanlatorFilter: Boolean): Flow<List<Chapter>> {
return handler.subscribeToList {
chaptersQueries.getChaptersByMangaId(
mangaId,
::mapChapter,
)
chaptersQueries.getChaptersByMangaId(mangaId, applyScanlatorFilter.toLong(), ::mapChapter)
}
}

View file

@ -36,7 +36,19 @@ FROM chapters
WHERE _id = :id;
getChaptersByMangaId:
SELECT *
SELECT C.*
FROM chapters C
LEFT JOIN excluded_scanlators ES
ON C.manga_id = ES.manga_id
AND C.scanlator = ES.scanlator
WHERE C.manga_id = :mangaId
AND (
:applyScanlatorFilter = 0
OR ES.scanlator IS NULL
);
getScanlatorsByMangaId:
SELECT scanlator
FROM chapters
WHERE manga_id = :mangaId;

View file

@ -0,0 +1,22 @@
CREATE TABLE excluded_scanlators(
manga_id INTEGER NOT NULL,
scanlator TEXT NOT NULL,
FOREIGN KEY(manga_id) REFERENCES mangas (_id)
ON DELETE CASCADE
);
CREATE INDEX excluded_scanlators_manga_id_index ON excluded_scanlators(manga_id);
insert:
INSERT INTO excluded_scanlators(manga_id, scanlator)
VALUES (:mangaId, :scanlator);
remove:
DELETE FROM excluded_scanlators
WHERE manga_id = :mangaId
AND scanlator IN :scanlators;
getExcludedScanlatorsByMangaId:
SELECT scanlator
FROM excluded_scanlators
WHERE manga_id = :mangaId;

View file

@ -0,0 +1,44 @@
CREATE TABLE excluded_scanlators(
manga_id INTEGER NOT NULL,
scanlator TEXT NOT NULL,
FOREIGN KEY(manga_id) REFERENCES mangas (_id)
ON DELETE CASCADE
);
CREATE INDEX excluded_scanlators_manga_id_index ON excluded_scanlators(manga_id);
DROP VIEW IF EXISTS libraryView;
CREATE VIEW libraryView AS
SELECT
M.*,
coalesce(C.total, 0) AS totalCount,
coalesce(C.readCount, 0) AS readCount,
coalesce(C.latestUpload, 0) AS latestUpload,
coalesce(C.fetchedAt, 0) AS chapterFetchedAt,
coalesce(C.lastRead, 0) AS lastRead,
coalesce(C.bookmarkCount, 0) AS bookmarkCount,
coalesce(MC.category_id, 0) AS category
FROM mangas M
LEFT JOIN(
SELECT
chapters.manga_id,
count(*) AS total,
sum(read) AS readCount,
coalesce(max(chapters.date_upload), 0) AS latestUpload,
coalesce(max(history.last_read), 0) AS lastRead,
coalesce(max(chapters.date_fetch), 0) AS fetchedAt,
sum(chapters.bookmark) AS bookmarkCount
FROM chapters
LEFT JOIN excluded_scanlators
ON chapters.manga_id = excluded_scanlators.manga_id
AND chapters.scanlator = excluded_scanlators.scanlator
LEFT JOIN history
ON chapters._id = history.chapter_id
WHERE excluded_scanlators.scanlator IS NULL
GROUP BY chapters.manga_id
) AS C
ON M._id = C.manga_id
LEFT JOIN mangas_categories AS MC
ON MC.manga_id = M._id
WHERE M.favorite = 1;

View file

@ -0,0 +1,3 @@
UPDATE chapters
SET scanlator = trim(scanlator)
WHERE scanlator IS NOT NULL;

View file

@ -19,8 +19,12 @@ LEFT JOIN(
coalesce(max(chapters.date_fetch), 0) AS fetchedAt,
sum(chapters.bookmark) AS bookmarkCount
FROM chapters
LEFT JOIN excluded_scanlators
ON chapters.manga_id = excluded_scanlators.manga_id
AND chapters.scanlator = excluded_scanlators.scanlator
LEFT JOIN history
ON chapters._id = history.chapter_id
WHERE excluded_scanlators.scanlator IS NULL
GROUP BY chapters.manga_id
) AS C
ON M._id = C.manga_id

View file

@ -12,10 +12,10 @@ class GetMangaWithChapters(
private val chapterRepository: ChapterRepository,
) {
suspend fun subscribe(id: Long): Flow<Pair<Manga, List<Chapter>>> {
suspend fun subscribe(id: Long, applyScanlatorFilter: Boolean = false): Flow<Pair<Manga, List<Chapter>>> {
return combine(
mangaRepository.getMangaByIdAsFlow(id),
chapterRepository.getChapterByMangaIdAsFlow(id),
chapterRepository.getChapterByMangaIdAsFlow(id, applyScanlatorFilter),
) { manga, chapters ->
Pair(manga, chapters)
}
@ -25,7 +25,7 @@ class GetMangaWithChapters(
return mangaRepository.getMangaById(id)
}
suspend fun awaitChapters(id: Long): List<Chapter> {
return chapterRepository.getChapterByMangaId(id)
suspend fun awaitChapters(id: Long, applyScanlatorFilter: Boolean = false): List<Chapter> {
return chapterRepository.getChapterByMangaId(id, applyScanlatorFilter)
}
}

View file

@ -24,7 +24,7 @@ class MangaFetchInterval(
} else {
window
}
val chapters = getChaptersByMangaId.await(manga.id)
val chapters = getChaptersByMangaId.await(manga.id, applyScanlatorFilter = true)
val interval = manga.fetchInterval.takeIf { it < 0 } ?: calculateInterval(
chapters,
dateTime.zone,

View file

@ -20,7 +20,7 @@ class GetNextChapters(
suspend fun await(mangaId: Long, onlyUnread: Boolean = true): List<Chapter> {
val manga = getManga.await(mangaId) ?: return emptyList()
val chapters = getChaptersByMangaId.await(mangaId)
val chapters = getChaptersByMangaId.await(mangaId, applyScanlatorFilter = true)
.sortedWith(getChapterSort(manga, sortDescending = false))
return if (onlyUnread) {

View file

@ -9,9 +9,9 @@ class GetChaptersByMangaId(
private val chapterRepository: ChapterRepository,
) {
suspend fun await(mangaId: Long): List<Chapter> {
suspend fun await(mangaId: Long, applyScanlatorFilter: Boolean = false): List<Chapter> {
return try {
chapterRepository.getChapterByMangaId(mangaId)
chapterRepository.getChapterByMangaId(mangaId, applyScanlatorFilter)
} catch (e: Exception) {
logcat(LogPriority.ERROR, e)
emptyList()

View file

@ -14,13 +14,17 @@ interface ChapterRepository {
suspend fun removeChaptersWithIds(chapterIds: List<Long>)
suspend fun getChapterByMangaId(mangaId: Long): List<Chapter>
suspend fun getChapterByMangaId(mangaId: Long, applyScanlatorFilter: Boolean = false): List<Chapter>
suspend fun getScanlatorsByMangaId(mangaId: Long): List<String>
fun getScanlatorsByMangaIdAsFlow(mangaId: Long): Flow<List<String>>
suspend fun getBookmarkedChaptersByMangaId(mangaId: Long): List<Chapter>
suspend fun getChapterById(id: Long): Chapter?
suspend fun getChapterByMangaIdAsFlow(mangaId: Long): Flow<List<Chapter>>
suspend fun getChapterByMangaIdAsFlow(mangaId: Long, applyScanlatorFilter: Boolean = false): Flow<List<Chapter>>
suspend fun getChapterByUrlAndMangaId(url: String, mangaId: Long): Chapter?
}

View file

@ -5,7 +5,7 @@ shizuku_version = "12.2.0"
sqlite = "2.4.0"
sqldelight = "2.0.0"
leakcanary = "2.12"
voyager = "1.0.0-rc08"
voyager = "1.0.0-rc09"
richtext = "0.17.0"
[libraries]
@ -81,7 +81,7 @@ sqldelight-android-paging = { module = "app.cash.sqldelight:androidx-paging3-ext
sqldelight-dialects-sql = { module = "app.cash.sqldelight:sqlite-3-38-dialect", version.ref = "sqldelight" }
sqldelight-gradle = { module = "app.cash.sqldelight:gradle-plugin", version.ref = "sqldelight" }
junit = "org.junit.jupiter:junit-jupiter:5.10.0"
junit = "org.junit.jupiter:junit-jupiter:5.10.1"
kotest-assertions = "io.kotest:kotest-assertions-core:5.8.0"
mockk = "io.mockk:mockk:1.13.8"

View file

@ -14,6 +14,7 @@
<string name="track">Tracking</string>
<string name="delete_downloaded">Delete downloaded</string>
<string name="history">History</string>
<string name="scanlator">Scanlator</string>
<!-- Screen titles -->
<string name="label_more">More</string>
@ -126,6 +127,7 @@
<string name="action_disable">Disable</string>
<string name="action_pin">Pin</string>
<string name="action_unpin">Unpin</string>
<string name="action_apply">Apply</string>
<string name="action_cancel">Cancel</string>
<string name="action_ok">OK</string>
<string name="action_cancel_all">Cancel all</string>
@ -146,6 +148,7 @@
<string name="action_share">Share</string>
<string name="action_save">Save</string>
<string name="action_reset">Reset</string>
<string name="action_revert_to_default">Revert to default</string>
<!-- missing undo feature after Compose rewrite #7454 -->
<string name="action_undo">Undo</string>
<string name="action_close">Close</string>
@ -686,6 +689,8 @@
<string name="set_chapter_settings_as_default">Set as default</string>
<string name="no_chapters_error">No chapters found</string>
<string name="are_you_sure">Are you sure?</string>
<string name="exclude_scanlators">Exclude scanlators</string>
<string name="no_scanlators_found">No scanlators found</string>
<!-- Tracking Screen -->
<string name="manga_tracking_tab">Tracking</string>

View file

@ -23,12 +23,12 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.util.ThemePreviews
import tachiyomi.presentation.core.util.secondaryItemAlpha
@Composable
@ -121,7 +121,7 @@ fun InfoScreen(
}
}
@ThemePreviews
@PreviewLightDark
@Composable
private fun InfoScaffoldPreview() {
InfoScreen(