mirror of
https://github.com/aniyomiorg/aniyomi.git
synced 2024-10-22 20:07:00 +03:00
parent
1b6301cc95
commit
a2a445fdc5
69 changed files with 445 additions and 325 deletions
|
@ -177,6 +177,7 @@ dependencies {
|
||||||
implementation(androidx.paging.compose)
|
implementation(androidx.paging.compose)
|
||||||
|
|
||||||
implementation(libs.bundles.sqlite)
|
implementation(libs.bundles.sqlite)
|
||||||
|
implementation(libs.sqldelight.primitive.adapters)
|
||||||
|
|
||||||
implementation(kotlinx.reflect)
|
implementation(kotlinx.reflect)
|
||||||
|
|
||||||
|
|
|
@ -81,7 +81,7 @@ import tachiyomi.domain.entries.anime.interactor.GetLibraryAnime
|
||||||
import tachiyomi.domain.entries.anime.interactor.NetworkToLocalAnime
|
import tachiyomi.domain.entries.anime.interactor.NetworkToLocalAnime
|
||||||
import tachiyomi.domain.entries.anime.interactor.ResetAnimeViewerFlags
|
import tachiyomi.domain.entries.anime.interactor.ResetAnimeViewerFlags
|
||||||
import tachiyomi.domain.entries.anime.interactor.SetAnimeEpisodeFlags
|
import tachiyomi.domain.entries.anime.interactor.SetAnimeEpisodeFlags
|
||||||
import tachiyomi.domain.entries.anime.interactor.SetAnimeUpdateInterval
|
import tachiyomi.domain.entries.anime.interactor.SetAnimeFetchInterval
|
||||||
import tachiyomi.domain.entries.anime.repository.AnimeRepository
|
import tachiyomi.domain.entries.anime.repository.AnimeRepository
|
||||||
import tachiyomi.domain.entries.manga.interactor.GetDuplicateLibraryManga
|
import tachiyomi.domain.entries.manga.interactor.GetDuplicateLibraryManga
|
||||||
import tachiyomi.domain.entries.manga.interactor.GetLibraryManga
|
import tachiyomi.domain.entries.manga.interactor.GetLibraryManga
|
||||||
|
@ -91,7 +91,7 @@ import tachiyomi.domain.entries.manga.interactor.GetMangaWithChapters
|
||||||
import tachiyomi.domain.entries.manga.interactor.NetworkToLocalManga
|
import tachiyomi.domain.entries.manga.interactor.NetworkToLocalManga
|
||||||
import tachiyomi.domain.entries.manga.interactor.ResetMangaViewerFlags
|
import tachiyomi.domain.entries.manga.interactor.ResetMangaViewerFlags
|
||||||
import tachiyomi.domain.entries.manga.interactor.SetMangaChapterFlags
|
import tachiyomi.domain.entries.manga.interactor.SetMangaChapterFlags
|
||||||
import tachiyomi.domain.entries.manga.interactor.SetMangaUpdateInterval
|
import tachiyomi.domain.entries.manga.interactor.SetMangaFetchInterval
|
||||||
import tachiyomi.domain.entries.manga.repository.MangaRepository
|
import tachiyomi.domain.entries.manga.repository.MangaRepository
|
||||||
import tachiyomi.domain.history.anime.interactor.GetAnimeHistory
|
import tachiyomi.domain.history.anime.interactor.GetAnimeHistory
|
||||||
import tachiyomi.domain.history.anime.interactor.GetNextEpisodes
|
import tachiyomi.domain.history.anime.interactor.GetNextEpisodes
|
||||||
|
@ -184,7 +184,7 @@ class DomainModule : InjektModule {
|
||||||
addFactory { GetNextEpisodes(get(), get(), get()) }
|
addFactory { GetNextEpisodes(get(), get(), get()) }
|
||||||
addFactory { ResetAnimeViewerFlags(get()) }
|
addFactory { ResetAnimeViewerFlags(get()) }
|
||||||
addFactory { SetAnimeEpisodeFlags(get()) }
|
addFactory { SetAnimeEpisodeFlags(get()) }
|
||||||
addFactory { SetAnimeUpdateInterval(get()) }
|
addFactory { SetAnimeFetchInterval(get()) }
|
||||||
addFactory { SetAnimeDefaultEpisodeFlags(get(), get(), get()) }
|
addFactory { SetAnimeDefaultEpisodeFlags(get(), get(), get()) }
|
||||||
addFactory { SetAnimeViewerFlags(get()) }
|
addFactory { SetAnimeViewerFlags(get()) }
|
||||||
addFactory { NetworkToLocalAnime(get()) }
|
addFactory { NetworkToLocalAnime(get()) }
|
||||||
|
@ -200,7 +200,7 @@ class DomainModule : InjektModule {
|
||||||
addFactory { GetNextChapters(get(), get(), get()) }
|
addFactory { GetNextChapters(get(), get(), get()) }
|
||||||
addFactory { ResetMangaViewerFlags(get()) }
|
addFactory { ResetMangaViewerFlags(get()) }
|
||||||
addFactory { SetMangaChapterFlags(get()) }
|
addFactory { SetMangaChapterFlags(get()) }
|
||||||
addFactory { SetMangaUpdateInterval(get()) }
|
addFactory { SetMangaFetchInterval(get()) }
|
||||||
addFactory {
|
addFactory {
|
||||||
SetMangaDefaultChapterFlags(
|
SetMangaDefaultChapterFlags(
|
||||||
get(),
|
get(),
|
||||||
|
|
|
@ -3,7 +3,7 @@ package eu.kanade.domain.entries.anime.interactor
|
||||||
import eu.kanade.domain.entries.anime.model.hasCustomCover
|
import eu.kanade.domain.entries.anime.model.hasCustomCover
|
||||||
import eu.kanade.tachiyomi.animesource.model.SAnime
|
import eu.kanade.tachiyomi.animesource.model.SAnime
|
||||||
import eu.kanade.tachiyomi.data.cache.AnimeCoverCache
|
import eu.kanade.tachiyomi.data.cache.AnimeCoverCache
|
||||||
import tachiyomi.domain.entries.anime.interactor.SetAnimeUpdateInterval
|
import tachiyomi.domain.entries.anime.interactor.SetAnimeFetchInterval
|
||||||
import tachiyomi.domain.entries.anime.model.Anime
|
import tachiyomi.domain.entries.anime.model.Anime
|
||||||
import tachiyomi.domain.entries.anime.model.AnimeUpdate
|
import tachiyomi.domain.entries.anime.model.AnimeUpdate
|
||||||
import tachiyomi.domain.entries.anime.repository.AnimeRepository
|
import tachiyomi.domain.entries.anime.repository.AnimeRepository
|
||||||
|
@ -16,7 +16,7 @@ import java.util.Date
|
||||||
|
|
||||||
class UpdateAnime(
|
class UpdateAnime(
|
||||||
private val animeRepository: AnimeRepository,
|
private val animeRepository: AnimeRepository,
|
||||||
private val setAnimeUpdateInterval: SetAnimeUpdateInterval,
|
private val setAnimeFetchInterval: SetAnimeFetchInterval,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun await(animeUpdate: AnimeUpdate): Boolean {
|
suspend fun await(animeUpdate: AnimeUpdate): Boolean {
|
||||||
|
@ -81,9 +81,9 @@ class UpdateAnime(
|
||||||
anime: Anime,
|
anime: Anime,
|
||||||
episodes: List<Episode>,
|
episodes: List<Episode>,
|
||||||
zonedDateTime: ZonedDateTime = ZonedDateTime.now(),
|
zonedDateTime: ZonedDateTime = ZonedDateTime.now(),
|
||||||
fetchRange: Pair<Long, Long> = setAnimeUpdateInterval.getCurrentFetchRange(zonedDateTime),
|
fetchRange: Pair<Long, Long> = setAnimeFetchInterval.getCurrent(zonedDateTime),
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val updateAnime = setAnimeUpdateInterval.updateInterval(anime, episodes, zonedDateTime, fetchRange)
|
val updateAnime = setAnimeFetchInterval.update(anime, episodes, zonedDateTime, fetchRange)
|
||||||
return if (updateAnime != null) {
|
return if (updateAnime != null) {
|
||||||
animeRepository.updateAnime(updateAnime)
|
animeRepository.updateAnime(updateAnime)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -3,7 +3,7 @@ package eu.kanade.domain.entries.manga.interactor
|
||||||
import eu.kanade.domain.entries.manga.model.hasCustomCover
|
import eu.kanade.domain.entries.manga.model.hasCustomCover
|
||||||
import eu.kanade.tachiyomi.data.cache.MangaCoverCache
|
import eu.kanade.tachiyomi.data.cache.MangaCoverCache
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import tachiyomi.domain.entries.manga.interactor.SetMangaUpdateInterval
|
import tachiyomi.domain.entries.manga.interactor.SetMangaFetchInterval
|
||||||
import tachiyomi.domain.entries.manga.model.Manga
|
import tachiyomi.domain.entries.manga.model.Manga
|
||||||
import tachiyomi.domain.entries.manga.model.MangaUpdate
|
import tachiyomi.domain.entries.manga.model.MangaUpdate
|
||||||
import tachiyomi.domain.entries.manga.repository.MangaRepository
|
import tachiyomi.domain.entries.manga.repository.MangaRepository
|
||||||
|
@ -16,7 +16,7 @@ import java.util.Date
|
||||||
|
|
||||||
class UpdateManga(
|
class UpdateManga(
|
||||||
private val mangaRepository: MangaRepository,
|
private val mangaRepository: MangaRepository,
|
||||||
private val setMangaUpdateInterval: SetMangaUpdateInterval,
|
private val setMangaFetchInterval: SetMangaFetchInterval,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun await(mangaUpdate: MangaUpdate): Boolean {
|
suspend fun await(mangaUpdate: MangaUpdate): Boolean {
|
||||||
|
@ -81,9 +81,9 @@ class UpdateManga(
|
||||||
manga: Manga,
|
manga: Manga,
|
||||||
chapters: List<Chapter>,
|
chapters: List<Chapter>,
|
||||||
zonedDateTime: ZonedDateTime = ZonedDateTime.now(),
|
zonedDateTime: ZonedDateTime = ZonedDateTime.now(),
|
||||||
fetchRange: Pair<Long, Long> = setMangaUpdateInterval.getCurrentFetchRange(zonedDateTime),
|
fetchRange: Pair<Long, Long> = setMangaFetchInterval.getCurrent(zonedDateTime),
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val updatedManga = setMangaUpdateInterval.updateInterval(manga, chapters, zonedDateTime, fetchRange)
|
val updatedManga = setMangaFetchInterval.update(manga, chapters, zonedDateTime, fetchRange)
|
||||||
return if (updatedManga != null) {
|
return if (updatedManga != null) {
|
||||||
mangaRepository.updateManga(updatedManga)
|
mangaRepository.updateManga(updatedManga)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -138,7 +138,7 @@ class SyncChaptersWithSource(
|
||||||
|
|
||||||
// Return if there's nothing to add, delete or change, avoiding unnecessary db transactions.
|
// Return if there's nothing to add, delete or change, avoiding unnecessary db transactions.
|
||||||
if (toAdd.isEmpty() && toDelete.isEmpty() && toChange.isEmpty()) {
|
if (toAdd.isEmpty() && toDelete.isEmpty() && toChange.isEmpty()) {
|
||||||
if (manualFetch || manga.calculateInterval == 0 || manga.nextUpdate < fetchRange.first) {
|
if (manualFetch || manga.fetchInterval == 0 || manga.nextUpdate < fetchRange.first) {
|
||||||
updateManga.awaitUpdateFetchInterval(
|
updateManga.awaitUpdateFetchInterval(
|
||||||
manga,
|
manga,
|
||||||
dbChapters,
|
dbChapters,
|
||||||
|
|
|
@ -138,7 +138,7 @@ class SyncEpisodesWithSource(
|
||||||
|
|
||||||
// Return if there's nothing to add, delete or change, avoiding unnecessary db transactions.
|
// Return if there's nothing to add, delete or change, avoiding unnecessary db transactions.
|
||||||
if (toAdd.isEmpty() && toDelete.isEmpty() && toChange.isEmpty()) {
|
if (toAdd.isEmpty() && toDelete.isEmpty() && toChange.isEmpty()) {
|
||||||
if (manualFetch || anime.calculateInterval == 0 || anime.nextUpdate < fetchRange.first) {
|
if (manualFetch || anime.fetchInterval == 0 || anime.nextUpdate < fetchRange.first) {
|
||||||
updateAnime.awaitUpdateFetchInterval(
|
updateAnime.awaitUpdateFetchInterval(
|
||||||
anime,
|
anime,
|
||||||
dbEpisodes,
|
dbEpisodes,
|
||||||
|
|
|
@ -74,6 +74,7 @@ import eu.kanade.tachiyomi.source.anime.getNameForAnimeInfo
|
||||||
import eu.kanade.tachiyomi.ui.browse.anime.extension.details.SourcePreferencesScreen
|
import eu.kanade.tachiyomi.ui.browse.anime.extension.details.SourcePreferencesScreen
|
||||||
import eu.kanade.tachiyomi.ui.entries.anime.AnimeScreenModel
|
import eu.kanade.tachiyomi.ui.entries.anime.AnimeScreenModel
|
||||||
import eu.kanade.tachiyomi.ui.entries.anime.EpisodeItem
|
import eu.kanade.tachiyomi.ui.entries.anime.EpisodeItem
|
||||||
|
import eu.kanade.tachiyomi.ui.entries.anime.FetchAnimeInterval
|
||||||
import eu.kanade.tachiyomi.util.lang.toRelativeString
|
import eu.kanade.tachiyomi.util.lang.toRelativeString
|
||||||
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
@ -97,7 +98,7 @@ import java.util.concurrent.TimeUnit
|
||||||
fun AnimeScreen(
|
fun AnimeScreen(
|
||||||
state: AnimeScreenModel.State.Success,
|
state: AnimeScreenModel.State.Success,
|
||||||
snackbarHostState: SnackbarHostState,
|
snackbarHostState: SnackbarHostState,
|
||||||
intervalDisplay: () -> Pair<Int, Int>?,
|
fetchInterval: FetchAnimeInterval?,
|
||||||
dateFormat: DateFormat,
|
dateFormat: DateFormat,
|
||||||
isTabletUi: Boolean,
|
isTabletUi: Boolean,
|
||||||
episodeSwipeStartAction: LibraryPreferences.EpisodeSwipeAction,
|
episodeSwipeStartAction: LibraryPreferences.EpisodeSwipeAction,
|
||||||
|
@ -127,7 +128,7 @@ fun AnimeScreen(
|
||||||
onShareClicked: (() -> Unit)?,
|
onShareClicked: (() -> Unit)?,
|
||||||
onDownloadActionClicked: ((DownloadAction) -> Unit)?,
|
onDownloadActionClicked: ((DownloadAction) -> Unit)?,
|
||||||
onEditCategoryClicked: (() -> Unit)?,
|
onEditCategoryClicked: (() -> Unit)?,
|
||||||
onEditIntervalClicked: (() -> Unit)?,
|
onEditFetchIntervalClicked: (() -> Unit)?,
|
||||||
onMigrateClicked: (() -> Unit)?,
|
onMigrateClicked: (() -> Unit)?,
|
||||||
changeAnimeSkipIntro: (() -> Unit)?,
|
changeAnimeSkipIntro: (() -> Unit)?,
|
||||||
|
|
||||||
|
@ -162,7 +163,7 @@ fun AnimeScreen(
|
||||||
state = state,
|
state = state,
|
||||||
snackbarHostState = snackbarHostState,
|
snackbarHostState = snackbarHostState,
|
||||||
dateFormat = dateFormat,
|
dateFormat = dateFormat,
|
||||||
intervalDisplay = intervalDisplay,
|
fetchInterval = fetchInterval,
|
||||||
episodeSwipeStartAction = episodeSwipeStartAction,
|
episodeSwipeStartAction = episodeSwipeStartAction,
|
||||||
episodeSwipeEndAction = episodeSwipeEndAction,
|
episodeSwipeEndAction = episodeSwipeEndAction,
|
||||||
showNextEpisodeAirTime = showNextEpisodeAirTime,
|
showNextEpisodeAirTime = showNextEpisodeAirTime,
|
||||||
|
@ -184,7 +185,7 @@ fun AnimeScreen(
|
||||||
onShareClicked = onShareClicked,
|
onShareClicked = onShareClicked,
|
||||||
onDownloadActionClicked = onDownloadActionClicked,
|
onDownloadActionClicked = onDownloadActionClicked,
|
||||||
onEditCategoryClicked = onEditCategoryClicked,
|
onEditCategoryClicked = onEditCategoryClicked,
|
||||||
onEditIntervalClicked = onEditIntervalClicked,
|
onEditIntervalClicked = onEditFetchIntervalClicked,
|
||||||
onMigrateClicked = onMigrateClicked,
|
onMigrateClicked = onMigrateClicked,
|
||||||
changeAnimeSkipIntro = changeAnimeSkipIntro,
|
changeAnimeSkipIntro = changeAnimeSkipIntro,
|
||||||
onMultiBookmarkClicked = onMultiBookmarkClicked,
|
onMultiBookmarkClicked = onMultiBookmarkClicked,
|
||||||
|
@ -206,7 +207,7 @@ fun AnimeScreen(
|
||||||
showNextEpisodeAirTime = showNextEpisodeAirTime,
|
showNextEpisodeAirTime = showNextEpisodeAirTime,
|
||||||
alwaysUseExternalPlayer = alwaysUseExternalPlayer,
|
alwaysUseExternalPlayer = alwaysUseExternalPlayer,
|
||||||
dateFormat = dateFormat,
|
dateFormat = dateFormat,
|
||||||
intervalDisplay = intervalDisplay,
|
fetchInterval = fetchInterval,
|
||||||
onBackClicked = onBackClicked,
|
onBackClicked = onBackClicked,
|
||||||
onEpisodeClicked = onEpisodeClicked,
|
onEpisodeClicked = onEpisodeClicked,
|
||||||
onDownloadEpisode = onDownloadEpisode,
|
onDownloadEpisode = onDownloadEpisode,
|
||||||
|
@ -224,7 +225,7 @@ fun AnimeScreen(
|
||||||
onShareClicked = onShareClicked,
|
onShareClicked = onShareClicked,
|
||||||
onDownloadActionClicked = onDownloadActionClicked,
|
onDownloadActionClicked = onDownloadActionClicked,
|
||||||
onEditCategoryClicked = onEditCategoryClicked,
|
onEditCategoryClicked = onEditCategoryClicked,
|
||||||
onEditIntervalClicked = onEditIntervalClicked,
|
onEditIntervalClicked = onEditFetchIntervalClicked,
|
||||||
changeAnimeSkipIntro = changeAnimeSkipIntro,
|
changeAnimeSkipIntro = changeAnimeSkipIntro,
|
||||||
onMigrateClicked = onMigrateClicked,
|
onMigrateClicked = onMigrateClicked,
|
||||||
onMultiBookmarkClicked = onMultiBookmarkClicked,
|
onMultiBookmarkClicked = onMultiBookmarkClicked,
|
||||||
|
@ -246,7 +247,7 @@ private fun AnimeScreenSmallImpl(
|
||||||
state: AnimeScreenModel.State.Success,
|
state: AnimeScreenModel.State.Success,
|
||||||
snackbarHostState: SnackbarHostState,
|
snackbarHostState: SnackbarHostState,
|
||||||
dateFormat: DateFormat,
|
dateFormat: DateFormat,
|
||||||
intervalDisplay: () -> Pair<Int, Int>?,
|
fetchInterval: FetchAnimeInterval?,
|
||||||
episodeSwipeStartAction: LibraryPreferences.EpisodeSwipeAction,
|
episodeSwipeStartAction: LibraryPreferences.EpisodeSwipeAction,
|
||||||
episodeSwipeEndAction: LibraryPreferences.EpisodeSwipeAction,
|
episodeSwipeEndAction: LibraryPreferences.EpisodeSwipeAction,
|
||||||
showNextEpisodeAirTime: Boolean,
|
showNextEpisodeAirTime: Boolean,
|
||||||
|
@ -432,8 +433,8 @@ private fun AnimeScreenSmallImpl(
|
||||||
AnimeActionRow(
|
AnimeActionRow(
|
||||||
favorite = state.anime.favorite,
|
favorite = state.anime.favorite,
|
||||||
trackingCount = state.trackingCount,
|
trackingCount = state.trackingCount,
|
||||||
intervalDisplay = intervalDisplay,
|
fetchInterval = fetchInterval,
|
||||||
isUserIntervalMode = state.anime.calculateInterval < 0,
|
isUserIntervalMode = state.anime.fetchInterval < 0,
|
||||||
onAddToLibraryClicked = onAddToLibraryClicked,
|
onAddToLibraryClicked = onAddToLibraryClicked,
|
||||||
onWebViewClicked = onWebViewClicked,
|
onWebViewClicked = onWebViewClicked,
|
||||||
onWebViewLongClicked = onWebViewLongClicked,
|
onWebViewLongClicked = onWebViewLongClicked,
|
||||||
|
@ -517,7 +518,7 @@ fun AnimeScreenLargeImpl(
|
||||||
state: AnimeScreenModel.State.Success,
|
state: AnimeScreenModel.State.Success,
|
||||||
snackbarHostState: SnackbarHostState,
|
snackbarHostState: SnackbarHostState,
|
||||||
dateFormat: DateFormat,
|
dateFormat: DateFormat,
|
||||||
intervalDisplay: () -> Pair<Int, Int>?,
|
fetchInterval: FetchAnimeInterval?,
|
||||||
episodeSwipeStartAction: LibraryPreferences.EpisodeSwipeAction,
|
episodeSwipeStartAction: LibraryPreferences.EpisodeSwipeAction,
|
||||||
episodeSwipeEndAction: LibraryPreferences.EpisodeSwipeAction,
|
episodeSwipeEndAction: LibraryPreferences.EpisodeSwipeAction,
|
||||||
showNextEpisodeAirTime: Boolean,
|
showNextEpisodeAirTime: Boolean,
|
||||||
|
@ -685,8 +686,8 @@ fun AnimeScreenLargeImpl(
|
||||||
AnimeActionRow(
|
AnimeActionRow(
|
||||||
favorite = state.anime.favorite,
|
favorite = state.anime.favorite,
|
||||||
trackingCount = state.trackingCount,
|
trackingCount = state.trackingCount,
|
||||||
intervalDisplay = intervalDisplay,
|
fetchInterval = fetchInterval,
|
||||||
isUserIntervalMode = state.anime.calculateInterval < 0,
|
isUserIntervalMode = state.anime.fetchInterval < 0,
|
||||||
onAddToLibraryClicked = onAddToLibraryClicked,
|
onAddToLibraryClicked = onAddToLibraryClicked,
|
||||||
onWebViewClicked = onWebViewClicked,
|
onWebViewClicked = onWebViewClicked,
|
||||||
onWebViewLongClicked = onWebViewLongClicked,
|
onWebViewLongClicked = onWebViewLongClicked,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package eu.kanade.presentation.entries.anime
|
package eu.kanade.presentation.entries.anime
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.FlowRow
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
@ -8,6 +9,7 @@ import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -18,15 +20,27 @@ fun DuplicateAnimeDialog(
|
||||||
) {
|
) {
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
|
title = {
|
||||||
|
Text(text = stringResource(R.string.are_you_sure))
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Text(text = stringResource(R.string.confirm_add_duplicate_manga))
|
||||||
|
},
|
||||||
confirmButton = {
|
confirmButton = {
|
||||||
Row {
|
FlowRow(
|
||||||
TextButton(onClick = {
|
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
|
) {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
onDismissRequest()
|
onDismissRequest()
|
||||||
onOpenAnime()
|
onOpenAnime()
|
||||||
},) {
|
},
|
||||||
|
) {
|
||||||
Text(text = stringResource(R.string.action_show_anime))
|
Text(text = stringResource(R.string.action_show_anime))
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
|
||||||
TextButton(onClick = onDismissRequest) {
|
TextButton(onClick = onDismissRequest) {
|
||||||
Text(text = stringResource(R.string.action_cancel))
|
Text(text = stringResource(R.string.action_cancel))
|
||||||
}
|
}
|
||||||
|
@ -40,11 +54,5 @@ fun DuplicateAnimeDialog(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
title = {
|
|
||||||
Text(text = stringResource(R.string.are_you_sure))
|
|
||||||
},
|
|
||||||
text = {
|
|
||||||
Text(text = stringResource(R.string.confirm_add_duplicate_manga))
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,6 +80,7 @@ import eu.kanade.presentation.entries.DotSeparatorText
|
||||||
import eu.kanade.presentation.entries.ItemCover
|
import eu.kanade.presentation.entries.ItemCover
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.animesource.model.SAnime
|
import eu.kanade.tachiyomi.animesource.model.SAnime
|
||||||
|
import eu.kanade.tachiyomi.ui.entries.anime.FetchAnimeInterval
|
||||||
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
||||||
import tachiyomi.domain.entries.anime.model.Anime
|
import tachiyomi.domain.entries.anime.model.Anime
|
||||||
import tachiyomi.presentation.core.components.material.TextButton
|
import tachiyomi.presentation.core.components.material.TextButton
|
||||||
|
@ -167,7 +168,7 @@ fun AnimeActionRow(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
favorite: Boolean,
|
favorite: Boolean,
|
||||||
trackingCount: Int,
|
trackingCount: Int,
|
||||||
intervalDisplay: () -> Pair<Int, Int>?,
|
fetchInterval: FetchAnimeInterval?,
|
||||||
isUserIntervalMode: Boolean,
|
isUserIntervalMode: Boolean,
|
||||||
onAddToLibraryClicked: () -> Unit,
|
onAddToLibraryClicked: () -> Unit,
|
||||||
onWebViewClicked: (() -> Unit)?,
|
onWebViewClicked: (() -> Unit)?,
|
||||||
|
@ -176,7 +177,6 @@ fun AnimeActionRow(
|
||||||
onEditIntervalClicked: (() -> Unit)?,
|
onEditIntervalClicked: (() -> Unit)?,
|
||||||
onEditCategory: (() -> Unit)?,
|
onEditCategory: (() -> Unit)?,
|
||||||
) {
|
) {
|
||||||
val interval: Pair<Int, Int>? = intervalDisplay()
|
|
||||||
val defaultActionButtonColor = MaterialTheme.colorScheme.onSurface.copy(alpha = .38f)
|
val defaultActionButtonColor = MaterialTheme.colorScheme.onSurface.copy(alpha = .38f)
|
||||||
|
|
||||||
Row(modifier = modifier.padding(start = 16.dp, top = 8.dp, end = 16.dp)) {
|
Row(modifier = modifier.padding(start = 16.dp, top = 8.dp, end = 16.dp)) {
|
||||||
|
@ -191,13 +191,14 @@ fun AnimeActionRow(
|
||||||
onClick = onAddToLibraryClicked,
|
onClick = onAddToLibraryClicked,
|
||||||
onLongClick = onEditCategory,
|
onLongClick = onEditCategory,
|
||||||
)
|
)
|
||||||
if (onEditIntervalClicked != null && interval != null) {
|
if (onEditIntervalClicked != null && fetchInterval != null) {
|
||||||
|
val intervalPair = 1.coerceAtLeast(fetchInterval.interval - fetchInterval.leadDays) to (fetchInterval.interval + fetchInterval.followDays)
|
||||||
AnimeActionButton(
|
AnimeActionButton(
|
||||||
title =
|
title =
|
||||||
if (interval.first == interval.second) {
|
if (intervalPair.first == intervalPair.second) {
|
||||||
pluralStringResource(id = R.plurals.day, count = interval.second, interval.second)
|
pluralStringResource(id = R.plurals.day, count = intervalPair.second, intervalPair.second)
|
||||||
} else {
|
} else {
|
||||||
pluralStringResource(id = R.plurals.range_interval_day, count = interval.second, interval.first, interval.second)
|
pluralStringResource(id = R.plurals.range_interval_day, count = intervalPair.second, intervalPair.first, intervalPair.second)
|
||||||
},
|
},
|
||||||
icon = Icons.Default.HourglassEmpty,
|
icon = Icons.Default.HourglassEmpty,
|
||||||
color = if (isUserIntervalMode) MaterialTheme.colorScheme.primary else defaultActionButtonColor,
|
color = if (isUserIntervalMode) MaterialTheme.colorScheme.primary else defaultActionButtonColor,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package eu.kanade.presentation.entries.manga
|
package eu.kanade.presentation.entries.manga
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.FlowRow
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
@ -8,6 +9,7 @@ import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -18,15 +20,27 @@ fun DuplicateMangaDialog(
|
||||||
) {
|
) {
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
|
title = {
|
||||||
|
Text(text = stringResource(R.string.are_you_sure))
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Text(text = stringResource(R.string.confirm_add_duplicate_manga))
|
||||||
|
},
|
||||||
confirmButton = {
|
confirmButton = {
|
||||||
Row {
|
FlowRow(
|
||||||
TextButton(onClick = {
|
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
|
) {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
onDismissRequest()
|
onDismissRequest()
|
||||||
onOpenManga()
|
onOpenManga()
|
||||||
},) {
|
},
|
||||||
|
) {
|
||||||
Text(text = stringResource(R.string.action_show_manga))
|
Text(text = stringResource(R.string.action_show_manga))
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
|
||||||
TextButton(onClick = onDismissRequest) {
|
TextButton(onClick = onDismissRequest) {
|
||||||
Text(text = stringResource(R.string.action_cancel))
|
Text(text = stringResource(R.string.action_cancel))
|
||||||
}
|
}
|
||||||
|
@ -40,11 +54,5 @@ fun DuplicateMangaDialog(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
title = {
|
|
||||||
Text(text = stringResource(R.string.are_you_sure))
|
|
||||||
},
|
|
||||||
text = {
|
|
||||||
Text(text = stringResource(R.string.confirm_add_duplicate_manga))
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,6 +68,7 @@ import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||||
import eu.kanade.tachiyomi.source.manga.getNameForMangaInfo
|
import eu.kanade.tachiyomi.source.manga.getNameForMangaInfo
|
||||||
import eu.kanade.tachiyomi.ui.browse.manga.extension.details.MangaSourcePreferencesScreen
|
import eu.kanade.tachiyomi.ui.browse.manga.extension.details.MangaSourcePreferencesScreen
|
||||||
import eu.kanade.tachiyomi.ui.entries.manga.ChapterItem
|
import eu.kanade.tachiyomi.ui.entries.manga.ChapterItem
|
||||||
|
import eu.kanade.tachiyomi.ui.entries.manga.FetchMangaInterval
|
||||||
import eu.kanade.tachiyomi.ui.entries.manga.MangaScreenModel
|
import eu.kanade.tachiyomi.ui.entries.manga.MangaScreenModel
|
||||||
import eu.kanade.tachiyomi.util.lang.toRelativeString
|
import eu.kanade.tachiyomi.util.lang.toRelativeString
|
||||||
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
||||||
|
@ -90,7 +91,7 @@ import java.util.Date
|
||||||
fun MangaScreen(
|
fun MangaScreen(
|
||||||
state: MangaScreenModel.State.Success,
|
state: MangaScreenModel.State.Success,
|
||||||
snackbarHostState: SnackbarHostState,
|
snackbarHostState: SnackbarHostState,
|
||||||
intervalDisplay: () -> Pair<Int, Int>?,
|
fetchInterval: FetchMangaInterval?,
|
||||||
dateFormat: DateFormat,
|
dateFormat: DateFormat,
|
||||||
isTabletUi: Boolean,
|
isTabletUi: Boolean,
|
||||||
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
|
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
|
||||||
|
@ -118,7 +119,7 @@ fun MangaScreen(
|
||||||
onShareClicked: (() -> Unit)?,
|
onShareClicked: (() -> Unit)?,
|
||||||
onDownloadActionClicked: ((DownloadAction) -> Unit)?,
|
onDownloadActionClicked: ((DownloadAction) -> Unit)?,
|
||||||
onEditCategoryClicked: (() -> Unit)?,
|
onEditCategoryClicked: (() -> Unit)?,
|
||||||
onEditIntervalClicked: (() -> Unit)?,
|
onEditFetchIntervalClicked: (() -> Unit)?,
|
||||||
onMigrateClicked: (() -> Unit)?,
|
onMigrateClicked: (() -> Unit)?,
|
||||||
|
|
||||||
// For bottom action menu
|
// For bottom action menu
|
||||||
|
@ -152,7 +153,7 @@ fun MangaScreen(
|
||||||
state = state,
|
state = state,
|
||||||
snackbarHostState = snackbarHostState,
|
snackbarHostState = snackbarHostState,
|
||||||
dateFormat = dateFormat,
|
dateFormat = dateFormat,
|
||||||
intervalDisplay = intervalDisplay,
|
fetchInterval = fetchInterval,
|
||||||
chapterSwipeStartAction = chapterSwipeStartAction,
|
chapterSwipeStartAction = chapterSwipeStartAction,
|
||||||
chapterSwipeEndAction = chapterSwipeEndAction,
|
chapterSwipeEndAction = chapterSwipeEndAction,
|
||||||
onBackClicked = onBackClicked,
|
onBackClicked = onBackClicked,
|
||||||
|
@ -172,7 +173,7 @@ fun MangaScreen(
|
||||||
onShareClicked = onShareClicked,
|
onShareClicked = onShareClicked,
|
||||||
onDownloadActionClicked = onDownloadActionClicked,
|
onDownloadActionClicked = onDownloadActionClicked,
|
||||||
onEditCategoryClicked = onEditCategoryClicked,
|
onEditCategoryClicked = onEditCategoryClicked,
|
||||||
onEditIntervalClicked = onEditIntervalClicked,
|
onEditIntervalClicked = onEditFetchIntervalClicked,
|
||||||
onMigrateClicked = onMigrateClicked,
|
onMigrateClicked = onMigrateClicked,
|
||||||
onMultiBookmarkClicked = onMultiBookmarkClicked,
|
onMultiBookmarkClicked = onMultiBookmarkClicked,
|
||||||
onMultiMarkAsReadClicked = onMultiMarkAsReadClicked,
|
onMultiMarkAsReadClicked = onMultiMarkAsReadClicked,
|
||||||
|
@ -191,7 +192,7 @@ fun MangaScreen(
|
||||||
chapterSwipeStartAction = chapterSwipeStartAction,
|
chapterSwipeStartAction = chapterSwipeStartAction,
|
||||||
chapterSwipeEndAction = chapterSwipeEndAction,
|
chapterSwipeEndAction = chapterSwipeEndAction,
|
||||||
dateFormat = dateFormat,
|
dateFormat = dateFormat,
|
||||||
intervalDisplay = intervalDisplay,
|
fetchInterval = fetchInterval,
|
||||||
onBackClicked = onBackClicked,
|
onBackClicked = onBackClicked,
|
||||||
onChapterClicked = onChapterClicked,
|
onChapterClicked = onChapterClicked,
|
||||||
onDownloadChapter = onDownloadChapter,
|
onDownloadChapter = onDownloadChapter,
|
||||||
|
@ -209,7 +210,7 @@ fun MangaScreen(
|
||||||
onShareClicked = onShareClicked,
|
onShareClicked = onShareClicked,
|
||||||
onDownloadActionClicked = onDownloadActionClicked,
|
onDownloadActionClicked = onDownloadActionClicked,
|
||||||
onEditCategoryClicked = onEditCategoryClicked,
|
onEditCategoryClicked = onEditCategoryClicked,
|
||||||
onEditIntervalClicked = onEditIntervalClicked,
|
onEditIntervalClicked = onEditFetchIntervalClicked,
|
||||||
onMigrateClicked = onMigrateClicked,
|
onMigrateClicked = onMigrateClicked,
|
||||||
onMultiBookmarkClicked = onMultiBookmarkClicked,
|
onMultiBookmarkClicked = onMultiBookmarkClicked,
|
||||||
onMultiMarkAsReadClicked = onMultiMarkAsReadClicked,
|
onMultiMarkAsReadClicked = onMultiMarkAsReadClicked,
|
||||||
|
@ -229,7 +230,7 @@ private fun MangaScreenSmallImpl(
|
||||||
state: MangaScreenModel.State.Success,
|
state: MangaScreenModel.State.Success,
|
||||||
snackbarHostState: SnackbarHostState,
|
snackbarHostState: SnackbarHostState,
|
||||||
dateFormat: DateFormat,
|
dateFormat: DateFormat,
|
||||||
intervalDisplay: () -> Pair<Int, Int>?,
|
fetchInterval: FetchMangaInterval?,
|
||||||
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
|
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
|
||||||
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
|
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
|
||||||
onBackClicked: () -> Unit,
|
onBackClicked: () -> Unit,
|
||||||
|
@ -406,8 +407,8 @@ private fun MangaScreenSmallImpl(
|
||||||
MangaActionRow(
|
MangaActionRow(
|
||||||
favorite = state.manga.favorite,
|
favorite = state.manga.favorite,
|
||||||
trackingCount = state.trackingCount,
|
trackingCount = state.trackingCount,
|
||||||
intervalDisplay = intervalDisplay,
|
fetchInterval = fetchInterval,
|
||||||
isUserIntervalMode = state.manga.calculateInterval < 0,
|
isUserIntervalMode = state.manga.fetchInterval < 0,
|
||||||
onAddToLibraryClicked = onAddToLibraryClicked,
|
onAddToLibraryClicked = onAddToLibraryClicked,
|
||||||
onWebViewClicked = onWebViewClicked,
|
onWebViewClicked = onWebViewClicked,
|
||||||
onWebViewLongClicked = onWebViewLongClicked,
|
onWebViewLongClicked = onWebViewLongClicked,
|
||||||
|
@ -465,7 +466,7 @@ fun MangaScreenLargeImpl(
|
||||||
state: MangaScreenModel.State.Success,
|
state: MangaScreenModel.State.Success,
|
||||||
snackbarHostState: SnackbarHostState,
|
snackbarHostState: SnackbarHostState,
|
||||||
dateFormat: DateFormat,
|
dateFormat: DateFormat,
|
||||||
intervalDisplay: () -> Pair<Int, Int>?,
|
fetchInterval: FetchMangaInterval?,
|
||||||
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
|
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
|
||||||
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
|
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
|
||||||
onBackClicked: () -> Unit,
|
onBackClicked: () -> Unit,
|
||||||
|
@ -627,8 +628,8 @@ fun MangaScreenLargeImpl(
|
||||||
MangaActionRow(
|
MangaActionRow(
|
||||||
favorite = state.manga.favorite,
|
favorite = state.manga.favorite,
|
||||||
trackingCount = state.trackingCount,
|
trackingCount = state.trackingCount,
|
||||||
intervalDisplay = intervalDisplay,
|
fetchInterval = fetchInterval,
|
||||||
isUserIntervalMode = state.manga.calculateInterval < 0,
|
isUserIntervalMode = state.manga.fetchInterval < 0,
|
||||||
onAddToLibraryClicked = onAddToLibraryClicked,
|
onAddToLibraryClicked = onAddToLibraryClicked,
|
||||||
onWebViewClicked = onWebViewClicked,
|
onWebViewClicked = onWebViewClicked,
|
||||||
onWebViewLongClicked = onWebViewLongClicked,
|
onWebViewLongClicked = onWebViewLongClicked,
|
||||||
|
|
|
@ -80,6 +80,7 @@ import eu.kanade.presentation.entries.DotSeparatorText
|
||||||
import eu.kanade.presentation.entries.ItemCover
|
import eu.kanade.presentation.entries.ItemCover
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
|
import eu.kanade.tachiyomi.ui.entries.manga.FetchMangaInterval
|
||||||
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
||||||
import tachiyomi.domain.entries.manga.model.Manga
|
import tachiyomi.domain.entries.manga.model.Manga
|
||||||
import tachiyomi.presentation.core.components.material.TextButton
|
import tachiyomi.presentation.core.components.material.TextButton
|
||||||
|
@ -167,7 +168,7 @@ fun MangaActionRow(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
favorite: Boolean,
|
favorite: Boolean,
|
||||||
trackingCount: Int,
|
trackingCount: Int,
|
||||||
intervalDisplay: () -> Pair<Int, Int>?,
|
fetchInterval: FetchMangaInterval?,
|
||||||
isUserIntervalMode: Boolean,
|
isUserIntervalMode: Boolean,
|
||||||
onAddToLibraryClicked: () -> Unit,
|
onAddToLibraryClicked: () -> Unit,
|
||||||
onWebViewClicked: (() -> Unit)?,
|
onWebViewClicked: (() -> Unit)?,
|
||||||
|
@ -176,7 +177,6 @@ fun MangaActionRow(
|
||||||
onEditIntervalClicked: (() -> Unit)?,
|
onEditIntervalClicked: (() -> Unit)?,
|
||||||
onEditCategory: (() -> Unit)?,
|
onEditCategory: (() -> Unit)?,
|
||||||
) {
|
) {
|
||||||
val interval: Pair<Int, Int>? = intervalDisplay()
|
|
||||||
val defaultActionButtonColor = MaterialTheme.colorScheme.onSurface.copy(alpha = .38f)
|
val defaultActionButtonColor = MaterialTheme.colorScheme.onSurface.copy(alpha = .38f)
|
||||||
|
|
||||||
Row(modifier = modifier.padding(start = 16.dp, top = 8.dp, end = 16.dp)) {
|
Row(modifier = modifier.padding(start = 16.dp, top = 8.dp, end = 16.dp)) {
|
||||||
|
@ -191,13 +191,14 @@ fun MangaActionRow(
|
||||||
onClick = onAddToLibraryClicked,
|
onClick = onAddToLibraryClicked,
|
||||||
onLongClick = onEditCategory,
|
onLongClick = onEditCategory,
|
||||||
)
|
)
|
||||||
if (onEditIntervalClicked != null && interval != null) {
|
if (onEditIntervalClicked != null && fetchInterval != null) {
|
||||||
|
val intervalPair = 1.coerceAtLeast(fetchInterval.interval - fetchInterval.leadDays) to (fetchInterval.interval + fetchInterval.followDays)
|
||||||
MangaActionButton(
|
MangaActionButton(
|
||||||
title =
|
title =
|
||||||
if (interval.first == interval.second) {
|
if (intervalPair.first == intervalPair.second) {
|
||||||
pluralStringResource(id = R.plurals.day, count = interval.second, interval.second)
|
pluralStringResource(id = R.plurals.day, count = intervalPair.second, intervalPair.second)
|
||||||
} else {
|
} else {
|
||||||
pluralStringResource(id = R.plurals.range_interval_day, count = interval.second, interval.first, interval.second)
|
pluralStringResource(id = R.plurals.range_interval_day, count = intervalPair.second, intervalPair.first, intervalPair.second)
|
||||||
},
|
},
|
||||||
icon = Icons.Default.HourglassEmpty,
|
icon = Icons.Default.HourglassEmpty,
|
||||||
color = if (isUserIntervalMode) MaterialTheme.colorScheme.primary else defaultActionButtonColor,
|
color = if (isUserIntervalMode) MaterialTheme.colorScheme.primary else defaultActionButtonColor,
|
||||||
|
|
|
@ -235,6 +235,7 @@ object SettingsAdvancedScreen : SearchableSettings {
|
||||||
onClick = {
|
onClick = {
|
||||||
Injekt.get<MangaDownloadCache>().invalidateCache()
|
Injekt.get<MangaDownloadCache>().invalidateCache()
|
||||||
Injekt.get<AnimeDownloadCache>().invalidateCache()
|
Injekt.get<AnimeDownloadCache>().invalidateCache()
|
||||||
|
context.toast(R.string.download_cache_invalidated)
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.TextPreference(
|
Preference.PreferenceItem.TextPreference(
|
||||||
|
|
|
@ -5,11 +5,16 @@ import android.os.Build
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||||
import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory
|
import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory
|
||||||
import com.squareup.sqldelight.android.AndroidSqliteDriver
|
import app.cash.sqldelight.adapter.primitive.FloatColumnAdapter
|
||||||
|
import app.cash.sqldelight.driver.android.AndroidSqliteDriver
|
||||||
|
import data.Chapters
|
||||||
import data.History
|
import data.History
|
||||||
|
import data.Manga_sync
|
||||||
import data.Mangas
|
import data.Mangas
|
||||||
|
import dataanime.Anime_sync
|
||||||
import dataanime.Animehistory
|
import dataanime.Animehistory
|
||||||
import dataanime.Animes
|
import dataanime.Animes
|
||||||
|
import dataanime.Episodes
|
||||||
import eu.kanade.domain.base.BasePreferences
|
import eu.kanade.domain.base.BasePreferences
|
||||||
import eu.kanade.domain.source.service.SourcePreferences
|
import eu.kanade.domain.source.service.SourcePreferences
|
||||||
import eu.kanade.domain.track.anime.store.DelayedAnimeTrackingStore
|
import eu.kanade.domain.track.anime.store.DelayedAnimeTrackingStore
|
||||||
|
@ -131,9 +136,15 @@ class AppModule(val app: Application) : InjektModule {
|
||||||
addSingletonFactory {
|
addSingletonFactory {
|
||||||
Database(
|
Database(
|
||||||
driver = sqlDriverManga,
|
driver = sqlDriverManga,
|
||||||
|
chaptersAdapter = Chapters.Adapter(
|
||||||
|
chapter_numberAdapter = FloatColumnAdapter,
|
||||||
|
),
|
||||||
historyAdapter = History.Adapter(
|
historyAdapter = History.Adapter(
|
||||||
last_readAdapter = dateAdapter,
|
last_readAdapter = dateAdapter,
|
||||||
),
|
),
|
||||||
|
manga_syncAdapter = Manga_sync.Adapter(
|
||||||
|
scoreAdapter = FloatColumnAdapter,
|
||||||
|
),
|
||||||
mangasAdapter = Mangas.Adapter(
|
mangasAdapter = Mangas.Adapter(
|
||||||
genreAdapter = listOfStringsAdapter,
|
genreAdapter = listOfStringsAdapter,
|
||||||
update_strategyAdapter = updateStrategyAdapter,
|
update_strategyAdapter = updateStrategyAdapter,
|
||||||
|
@ -144,9 +155,15 @@ class AppModule(val app: Application) : InjektModule {
|
||||||
addSingletonFactory {
|
addSingletonFactory {
|
||||||
AnimeDatabase(
|
AnimeDatabase(
|
||||||
driver = sqlDriverAnime,
|
driver = sqlDriverAnime,
|
||||||
|
episodesAdapter = Episodes.Adapter(
|
||||||
|
episode_numberAdapter = FloatColumnAdapter,
|
||||||
|
),
|
||||||
animehistoryAdapter = Animehistory.Adapter(
|
animehistoryAdapter = Animehistory.Adapter(
|
||||||
last_seenAdapter = dateAdapter,
|
last_seenAdapter = dateAdapter,
|
||||||
),
|
),
|
||||||
|
anime_syncAdapter = Anime_sync.Adapter(
|
||||||
|
scoreAdapter = FloatColumnAdapter,
|
||||||
|
),
|
||||||
animesAdapter = Animes.Adapter(
|
animesAdapter = Animes.Adapter(
|
||||||
genreAdapter = listOfStringsAdapter,
|
genreAdapter = listOfStringsAdapter,
|
||||||
update_strategyAdapter = updateStrategyAdapter,
|
update_strategyAdapter = updateStrategyAdapter,
|
||||||
|
|
|
@ -511,7 +511,7 @@ class BackupManager(
|
||||||
}
|
}
|
||||||
if (!found) {
|
if (!found) {
|
||||||
// Let the db assign the id
|
// Let the db assign the id
|
||||||
val id = mangaHandler.awaitOne {
|
val id = mangaHandler.awaitOneExecutable {
|
||||||
categoriesQueries.insert(category.name, category.order, category.flags)
|
categoriesQueries.insert(category.name, category.order, category.flags)
|
||||||
categoriesQueries.selectLastInsertedRowId()
|
categoriesQueries.selectLastInsertedRowId()
|
||||||
}
|
}
|
||||||
|
@ -551,7 +551,7 @@ class BackupManager(
|
||||||
}
|
}
|
||||||
if (!found) {
|
if (!found) {
|
||||||
// Let the db assign the id
|
// Let the db assign the id
|
||||||
val id = animeHandler.awaitOne {
|
val id = animeHandler.awaitOneExecutable {
|
||||||
categoriesQueries.insert(category.name, category.order, category.flags)
|
categoriesQueries.insert(category.name, category.order, category.flags)
|
||||||
categoriesQueries.selectLastInsertedRowId()
|
categoriesQueries.selectLastInsertedRowId()
|
||||||
}
|
}
|
||||||
|
@ -970,7 +970,7 @@ class BackupManager(
|
||||||
* @return id of [Manga], null if not found
|
* @return id of [Manga], null if not found
|
||||||
*/
|
*/
|
||||||
private suspend fun insertManga(manga: Manga): Long {
|
private suspend fun insertManga(manga: Manga): Long {
|
||||||
return mangaHandler.awaitOne(true) {
|
return mangaHandler.awaitOneExecutable(true) {
|
||||||
mangasQueries.insert(
|
mangasQueries.insert(
|
||||||
source = manga.source,
|
source = manga.source,
|
||||||
url = manga.url,
|
url = manga.url,
|
||||||
|
@ -1002,7 +1002,7 @@ class BackupManager(
|
||||||
* @return id of [Anime], null if not found
|
* @return id of [Anime], null if not found
|
||||||
*/
|
*/
|
||||||
private suspend fun insertAnime(anime: Anime): Long {
|
private suspend fun insertAnime(anime: Anime): Long {
|
||||||
return animeHandler.awaitOne(true) {
|
return animeHandler.awaitOneExecutable(true) {
|
||||||
animesQueries.insert(
|
animesQueries.insert(
|
||||||
source = anime.source,
|
source = anime.source,
|
||||||
url = anime.url,
|
url = anime.url,
|
||||||
|
@ -1040,11 +1040,11 @@ class BackupManager(
|
||||||
title = manga.title,
|
title = manga.title,
|
||||||
status = manga.status,
|
status = manga.status,
|
||||||
thumbnailUrl = manga.thumbnailUrl,
|
thumbnailUrl = manga.thumbnailUrl,
|
||||||
favorite = manga.favorite.toLong(),
|
favorite = manga.favorite,
|
||||||
lastUpdate = manga.lastUpdate,
|
lastUpdate = manga.lastUpdate,
|
||||||
nextUpdate = null,
|
nextUpdate = null,
|
||||||
calculateInterval = null,
|
calculateInterval = null,
|
||||||
initialized = manga.initialized.toLong(),
|
initialized = manga.initialized,
|
||||||
viewer = manga.viewerFlags,
|
viewer = manga.viewerFlags,
|
||||||
chapterFlags = manga.chapterFlags,
|
chapterFlags = manga.chapterFlags,
|
||||||
coverLastModified = manga.coverLastModified,
|
coverLastModified = manga.coverLastModified,
|
||||||
|
@ -1068,11 +1068,11 @@ class BackupManager(
|
||||||
title = anime.title,
|
title = anime.title,
|
||||||
status = anime.status,
|
status = anime.status,
|
||||||
thumbnailUrl = anime.thumbnailUrl,
|
thumbnailUrl = anime.thumbnailUrl,
|
||||||
favorite = anime.favorite.toLong(),
|
favorite = anime.favorite,
|
||||||
lastUpdate = anime.lastUpdate,
|
lastUpdate = anime.lastUpdate,
|
||||||
nextUpdate = null,
|
nextUpdate = null,
|
||||||
calculateInterval = null,
|
calculateInterval = null,
|
||||||
initialized = anime.initialized.toLong(),
|
initialized = anime.initialized,
|
||||||
viewer = anime.viewerFlags,
|
viewer = anime.viewerFlags,
|
||||||
episodeFlags = anime.episodeFlags,
|
episodeFlags = anime.episodeFlags,
|
||||||
coverLastModified = anime.coverLastModified,
|
coverLastModified = anime.coverLastModified,
|
||||||
|
@ -1142,8 +1142,8 @@ class BackupManager(
|
||||||
url = null,
|
url = null,
|
||||||
name = null,
|
name = null,
|
||||||
scanlator = null,
|
scanlator = null,
|
||||||
read = chapter.read.toLong(),
|
read = chapter.read,
|
||||||
bookmark = chapter.bookmark.toLong(),
|
bookmark = chapter.bookmark,
|
||||||
lastPageRead = chapter.lastPageRead,
|
lastPageRead = chapter.lastPageRead,
|
||||||
chapterNumber = null,
|
chapterNumber = null,
|
||||||
sourceOrder = null,
|
sourceOrder = null,
|
||||||
|
@ -1166,8 +1166,8 @@ class BackupManager(
|
||||||
url = null,
|
url = null,
|
||||||
name = null,
|
name = null,
|
||||||
scanlator = null,
|
scanlator = null,
|
||||||
seen = episode.seen.toLong(),
|
seen = episode.seen,
|
||||||
bookmark = episode.bookmark.toLong(),
|
bookmark = episode.bookmark,
|
||||||
lastSecondSeen = episode.lastSecondSeen,
|
lastSecondSeen = episode.lastSecondSeen,
|
||||||
totalSeconds = episode.totalSeconds,
|
totalSeconds = episode.totalSeconds,
|
||||||
episodeNumber = null,
|
episodeNumber = null,
|
||||||
|
|
|
@ -30,9 +30,9 @@ import eu.kanade.tachiyomi.util.system.createFileInCacheDir
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import tachiyomi.core.util.system.logcat
|
import tachiyomi.core.util.system.logcat
|
||||||
import tachiyomi.domain.entries.anime.interactor.SetAnimeUpdateInterval
|
import tachiyomi.domain.entries.anime.interactor.SetAnimeFetchInterval
|
||||||
import tachiyomi.domain.entries.anime.model.Anime
|
import tachiyomi.domain.entries.anime.model.Anime
|
||||||
import tachiyomi.domain.entries.manga.interactor.SetMangaUpdateInterval
|
import tachiyomi.domain.entries.manga.interactor.SetMangaFetchInterval
|
||||||
import tachiyomi.domain.entries.manga.model.Manga
|
import tachiyomi.domain.entries.manga.model.Manga
|
||||||
import tachiyomi.domain.items.chapter.model.Chapter
|
import tachiyomi.domain.items.chapter.model.Chapter
|
||||||
import tachiyomi.domain.items.chapter.repository.ChapterRepository
|
import tachiyomi.domain.items.chapter.repository.ChapterRepository
|
||||||
|
@ -54,15 +54,15 @@ class BackupRestorer(
|
||||||
) {
|
) {
|
||||||
private val updateManga: UpdateManga = Injekt.get()
|
private val updateManga: UpdateManga = Injekt.get()
|
||||||
private val chapterRepository: ChapterRepository = Injekt.get()
|
private val chapterRepository: ChapterRepository = Injekt.get()
|
||||||
private val setMangaUpdateInterval: SetMangaUpdateInterval = Injekt.get()
|
private val setMangaFetchInterval: SetMangaFetchInterval = Injekt.get()
|
||||||
|
|
||||||
private val updateAnime: UpdateAnime = Injekt.get()
|
private val updateAnime: UpdateAnime = Injekt.get()
|
||||||
private val episodeRepository: EpisodeRepository = Injekt.get()
|
private val episodeRepository: EpisodeRepository = Injekt.get()
|
||||||
private val setAnimeUpdateInterval: SetAnimeUpdateInterval = Injekt.get()
|
private val setAnimeFetchInterval: SetAnimeFetchInterval = Injekt.get()
|
||||||
|
|
||||||
private var zonedDateTime = ZonedDateTime.now()
|
private var zonedDateTime = ZonedDateTime.now()
|
||||||
private var currentMangaRange = setMangaUpdateInterval.getCurrentFetchRange(zonedDateTime)
|
private var currentMangaFetchInterval = setMangaFetchInterval.getCurrent(zonedDateTime)
|
||||||
private var currentAnimeRange = setAnimeUpdateInterval.getCurrentFetchRange(zonedDateTime)
|
private var currentAnimeFetchInterval = setAnimeFetchInterval.getCurrent(zonedDateTime)
|
||||||
|
|
||||||
private var backupManager = BackupManager(context)
|
private var backupManager = BackupManager(context)
|
||||||
|
|
||||||
|
@ -141,8 +141,8 @@ class BackupRestorer(
|
||||||
animeSourceMapping = backupAnimeMaps.associate { it.sourceId to it.name }
|
animeSourceMapping = backupAnimeMaps.associate { it.sourceId to it.name }
|
||||||
|
|
||||||
zonedDateTime = ZonedDateTime.now()
|
zonedDateTime = ZonedDateTime.now()
|
||||||
currentMangaRange = setMangaUpdateInterval.getCurrentFetchRange(zonedDateTime)
|
currentMangaFetchInterval = setMangaFetchInterval.getCurrent(zonedDateTime)
|
||||||
currentAnimeRange = setAnimeUpdateInterval.getCurrentFetchRange(zonedDateTime)
|
currentAnimeFetchInterval = setAnimeFetchInterval.getCurrent(zonedDateTime)
|
||||||
|
|
||||||
return coroutineScope {
|
return coroutineScope {
|
||||||
// Restore individual manga
|
// Restore individual manga
|
||||||
|
@ -217,7 +217,7 @@ class BackupRestorer(
|
||||||
restoreNewManga(updateManga, chapters, categories, history, tracks, backupCategories)
|
restoreNewManga(updateManga, chapters, categories, history, tracks, backupCategories)
|
||||||
}
|
}
|
||||||
val updatedChapters = chapterRepository.getChapterByMangaId(restoredManga.id)
|
val updatedChapters = chapterRepository.getChapterByMangaId(restoredManga.id)
|
||||||
updateManga.awaitUpdateFetchInterval(restoredManga, updatedChapters, zonedDateTime, currentMangaRange)
|
updateManga.awaitUpdateFetchInterval(restoredManga, updatedChapters, zonedDateTime, currentMangaFetchInterval)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
val sourceName = sourceMapping[manga.source] ?: manga.source.toString()
|
val sourceName = sourceMapping[manga.source] ?: manga.source.toString()
|
||||||
errors.add(Date() to "${manga.title} [$sourceName]: ${e.message}")
|
errors.add(Date() to "${manga.title} [$sourceName]: ${e.message}")
|
||||||
|
@ -292,7 +292,7 @@ class BackupRestorer(
|
||||||
restoreNewAnime(updateAnime, episodes, categories, history, tracks, backupCategories)
|
restoreNewAnime(updateAnime, episodes, categories, history, tracks, backupCategories)
|
||||||
}
|
}
|
||||||
val updatedEpisodes = episodeRepository.getEpisodeByAnimeId(restoredAnime.id)
|
val updatedEpisodes = episodeRepository.getEpisodeByAnimeId(restoredAnime.id)
|
||||||
updateAnime.awaitUpdateFetchInterval(restoredAnime, updatedEpisodes, zonedDateTime, currentAnimeRange)
|
updateAnime.awaitUpdateFetchInterval(restoredAnime, updatedEpisodes, zonedDateTime, currentAnimeFetchInterval)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
val sourceName = sourceMapping[anime.source] ?: anime.source.toString()
|
val sourceName = sourceMapping[anime.source] ?: anime.source.toString()
|
||||||
errors.add(Date() to "${anime.title} [$sourceName]: ${e.message}")
|
errors.add(Date() to "${anime.title} [$sourceName]: ${e.message}")
|
||||||
|
|
|
@ -56,7 +56,7 @@ import tachiyomi.domain.category.model.Category
|
||||||
import tachiyomi.domain.download.service.DownloadPreferences
|
import tachiyomi.domain.download.service.DownloadPreferences
|
||||||
import tachiyomi.domain.entries.anime.interactor.GetAnime
|
import tachiyomi.domain.entries.anime.interactor.GetAnime
|
||||||
import tachiyomi.domain.entries.anime.interactor.GetLibraryAnime
|
import tachiyomi.domain.entries.anime.interactor.GetLibraryAnime
|
||||||
import tachiyomi.domain.entries.anime.interactor.SetAnimeUpdateInterval
|
import tachiyomi.domain.entries.anime.interactor.SetAnimeFetchInterval
|
||||||
import tachiyomi.domain.entries.anime.model.Anime
|
import tachiyomi.domain.entries.anime.model.Anime
|
||||||
import tachiyomi.domain.entries.anime.model.toAnimeUpdate
|
import tachiyomi.domain.entries.anime.model.toAnimeUpdate
|
||||||
import tachiyomi.domain.items.episode.interactor.GetEpisodeByAnimeId
|
import tachiyomi.domain.items.episode.interactor.GetEpisodeByAnimeId
|
||||||
|
@ -104,7 +104,7 @@ class AnimeLibraryUpdateJob(private val context: Context, workerParams: WorkerPa
|
||||||
private val getTracks: GetAnimeTracks = Injekt.get()
|
private val getTracks: GetAnimeTracks = Injekt.get()
|
||||||
private val insertTrack: InsertAnimeTrack = Injekt.get()
|
private val insertTrack: InsertAnimeTrack = Injekt.get()
|
||||||
private val syncEpisodesWithTrackServiceTwoWay: SyncEpisodesWithTrackServiceTwoWay = Injekt.get()
|
private val syncEpisodesWithTrackServiceTwoWay: SyncEpisodesWithTrackServiceTwoWay = Injekt.get()
|
||||||
private val setAnimeUpdateInterval: SetAnimeUpdateInterval = Injekt.get()
|
private val setAnimeFetchInterval: SetAnimeFetchInterval = Injekt.get()
|
||||||
|
|
||||||
private val notifier = AnimeLibraryUpdateNotifier(context)
|
private val notifier = AnimeLibraryUpdateNotifier(context)
|
||||||
|
|
||||||
|
@ -232,8 +232,8 @@ class AnimeLibraryUpdateJob(private val context: Context, workerParams: WorkerPa
|
||||||
val restrictions = libraryPreferences.libraryUpdateItemRestriction().get()
|
val restrictions = libraryPreferences.libraryUpdateItemRestriction().get()
|
||||||
|
|
||||||
val now = ZonedDateTime.now()
|
val now = ZonedDateTime.now()
|
||||||
val fetchRange = setAnimeUpdateInterval.getCurrentFetchRange(now)
|
val fetchInterval = setAnimeFetchInterval.getCurrent(now)
|
||||||
val higherLimit = fetchRange.second
|
val higherLimit = fetchInterval.second
|
||||||
|
|
||||||
coroutineScope {
|
coroutineScope {
|
||||||
animeToUpdate.groupBy { it.anime.source }.values
|
animeToUpdate.groupBy { it.anime.source }.values
|
||||||
|
@ -272,7 +272,7 @@ class AnimeLibraryUpdateJob(private val context: Context, workerParams: WorkerPa
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
try {
|
try {
|
||||||
val newEpisodes = updateAnime(anime, now, fetchRange)
|
val newEpisodes = updateAnime(anime, now, fetchInterval)
|
||||||
.sortedByDescending { it.sourceOrder }
|
.sortedByDescending { it.sourceOrder }
|
||||||
|
|
||||||
if (newEpisodes.isNotEmpty()) {
|
if (newEpisodes.isNotEmpty()) {
|
||||||
|
|
|
@ -56,7 +56,7 @@ import tachiyomi.domain.category.model.Category
|
||||||
import tachiyomi.domain.download.service.DownloadPreferences
|
import tachiyomi.domain.download.service.DownloadPreferences
|
||||||
import tachiyomi.domain.entries.manga.interactor.GetLibraryManga
|
import tachiyomi.domain.entries.manga.interactor.GetLibraryManga
|
||||||
import tachiyomi.domain.entries.manga.interactor.GetManga
|
import tachiyomi.domain.entries.manga.interactor.GetManga
|
||||||
import tachiyomi.domain.entries.manga.interactor.SetMangaUpdateInterval
|
import tachiyomi.domain.entries.manga.interactor.SetMangaFetchInterval
|
||||||
import tachiyomi.domain.entries.manga.model.Manga
|
import tachiyomi.domain.entries.manga.model.Manga
|
||||||
import tachiyomi.domain.entries.manga.model.toMangaUpdate
|
import tachiyomi.domain.entries.manga.model.toMangaUpdate
|
||||||
import tachiyomi.domain.items.chapter.interactor.GetChapterByMangaId
|
import tachiyomi.domain.items.chapter.interactor.GetChapterByMangaId
|
||||||
|
@ -104,7 +104,7 @@ class MangaLibraryUpdateJob(private val context: Context, workerParams: WorkerPa
|
||||||
private val getTracks: GetMangaTracks = Injekt.get()
|
private val getTracks: GetMangaTracks = Injekt.get()
|
||||||
private val insertTrack: InsertMangaTrack = Injekt.get()
|
private val insertTrack: InsertMangaTrack = Injekt.get()
|
||||||
private val syncChaptersWithTrackServiceTwoWay: SyncChaptersWithTrackServiceTwoWay = Injekt.get()
|
private val syncChaptersWithTrackServiceTwoWay: SyncChaptersWithTrackServiceTwoWay = Injekt.get()
|
||||||
private val setMangaUpdateInterval: SetMangaUpdateInterval = Injekt.get()
|
private val setMangaFetchInterval: SetMangaFetchInterval = Injekt.get()
|
||||||
|
|
||||||
private val notifier = MangaLibraryUpdateNotifier(context)
|
private val notifier = MangaLibraryUpdateNotifier(context)
|
||||||
|
|
||||||
|
@ -232,8 +232,8 @@ class MangaLibraryUpdateJob(private val context: Context, workerParams: WorkerPa
|
||||||
val restrictions = libraryPreferences.libraryUpdateItemRestriction().get()
|
val restrictions = libraryPreferences.libraryUpdateItemRestriction().get()
|
||||||
|
|
||||||
val now = ZonedDateTime.now()
|
val now = ZonedDateTime.now()
|
||||||
val fetchRange = setMangaUpdateInterval.getCurrentFetchRange(now)
|
val fetchInterval = setMangaFetchInterval.getCurrent(now)
|
||||||
val higherLimit = fetchRange.second
|
val higherLimit = fetchInterval.second
|
||||||
|
|
||||||
coroutineScope {
|
coroutineScope {
|
||||||
mangaToUpdate.groupBy { it.manga.source }.values
|
mangaToUpdate.groupBy { it.manga.source }.values
|
||||||
|
@ -272,7 +272,7 @@ class MangaLibraryUpdateJob(private val context: Context, workerParams: WorkerPa
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
try {
|
try {
|
||||||
val newChapters = updateManga(manga, now, fetchRange)
|
val newChapters = updateManga(manga, now, fetchInterval)
|
||||||
.sortedByDescending { it.sourceOrder }
|
.sortedByDescending { it.sourceOrder }
|
||||||
|
|
||||||
if (newChapters.isNotEmpty()) {
|
if (newChapters.isNotEmpty()) {
|
||||||
|
|
|
@ -121,8 +121,8 @@ internal fun MigrateAnimeDialog(
|
||||||
) {
|
) {
|
||||||
TextButton(
|
TextButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
onClickTitle()
|
|
||||||
onDismissRequest()
|
onDismissRequest()
|
||||||
|
onClickTitle()
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
Text(text = stringResource(R.string.action_show_anime))
|
Text(text = stringResource(R.string.action_show_anime))
|
||||||
|
|
|
@ -329,7 +329,7 @@ class BrowseAnimeSourceScreenModel(
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getDuplicateAnimelibAnime(anime: Anime): Anime? {
|
suspend fun getDuplicateAnimelibAnime(anime: Anime): Anime? {
|
||||||
return getDuplicateAnimelibAnime.await(anime.title)
|
return getDuplicateAnimelibAnime.await(anime).getOrNull(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun moveAnimeToCategories(anime: Anime, vararg categories: Category) {
|
private fun moveAnimeToCategories(anime: Anime, vararg categories: Category) {
|
||||||
|
|
|
@ -121,8 +121,8 @@ internal fun MigrateMangaDialog(
|
||||||
) {
|
) {
|
||||||
TextButton(
|
TextButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
onClickTitle()
|
|
||||||
onDismissRequest()
|
onDismissRequest()
|
||||||
|
onClickTitle()
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
Text(text = stringResource(R.string.action_show_manga))
|
Text(text = stringResource(R.string.action_show_manga))
|
||||||
|
|
|
@ -323,7 +323,7 @@ class BrowseMangaSourceScreenModel(
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getDuplicateLibraryManga(manga: Manga): Manga? {
|
suspend fun getDuplicateLibraryManga(manga: Manga): Manga? {
|
||||||
return getDuplicateLibraryManga.await(manga.title)
|
return getDuplicateLibraryManga.await(manga).getOrNull(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun moveMangaToCategories(manga: Manga, vararg categories: Category) {
|
private fun moveMangaToCategories(manga: Manga, vararg categories: Category) {
|
||||||
|
|
|
@ -89,6 +89,13 @@ class AnimeScreen(
|
||||||
|
|
||||||
val successState = state as AnimeScreenModel.State.Success
|
val successState = state as AnimeScreenModel.State.Success
|
||||||
val isAnimeHttpSource = remember { successState.source is AnimeHttpSource }
|
val isAnimeHttpSource = remember { successState.source is AnimeHttpSource }
|
||||||
|
val fetchInterval = remember(successState.anime.fetchInterval) {
|
||||||
|
FetchAnimeInterval(
|
||||||
|
interval = successState.anime.fetchInterval,
|
||||||
|
leadDays = screenModel.leadDay,
|
||||||
|
followDays = screenModel.followDay,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
LaunchedEffect(successState.anime, screenModel.source) {
|
LaunchedEffect(successState.anime, screenModel.source) {
|
||||||
if (isAnimeHttpSource) {
|
if (isAnimeHttpSource) {
|
||||||
|
@ -106,7 +113,7 @@ class AnimeScreen(
|
||||||
state = successState,
|
state = successState,
|
||||||
snackbarHostState = screenModel.snackbarHostState,
|
snackbarHostState = screenModel.snackbarHostState,
|
||||||
dateFormat = screenModel.dateFormat,
|
dateFormat = screenModel.dateFormat,
|
||||||
intervalDisplay = screenModel::intervalDisplay,
|
fetchInterval = fetchInterval,
|
||||||
isTabletUi = isTabletUi(),
|
isTabletUi = isTabletUi(),
|
||||||
episodeSwipeStartAction = screenModel.episodeSwipeStartAction,
|
episodeSwipeStartAction = screenModel.episodeSwipeStartAction,
|
||||||
episodeSwipeEndAction = screenModel.episodeSwipeEndAction,
|
episodeSwipeEndAction = screenModel.episodeSwipeEndAction,
|
||||||
|
@ -141,7 +148,7 @@ class AnimeScreen(
|
||||||
onShareClicked = { shareAnime(context, screenModel.anime, screenModel.source) }.takeIf { isAnimeHttpSource },
|
onShareClicked = { shareAnime(context, screenModel.anime, screenModel.source) }.takeIf { isAnimeHttpSource },
|
||||||
onDownloadActionClicked = screenModel::runDownloadAction.takeIf { !successState.source.isLocalOrStub() },
|
onDownloadActionClicked = screenModel::runDownloadAction.takeIf { !successState.source.isLocalOrStub() },
|
||||||
onEditCategoryClicked = screenModel::showChangeCategoryDialog.takeIf { successState.anime.favorite },
|
onEditCategoryClicked = screenModel::showChangeCategoryDialog.takeIf { successState.anime.favorite },
|
||||||
onEditIntervalClicked = screenModel::showSetAnimeIntervalDialog.takeIf { screenModel.isIntervalEnabled && successState.anime.favorite },
|
onEditFetchIntervalClicked = screenModel::showSetAnimeFetchIntervalDialog.takeIf { screenModel.isUpdateIntervalEnabled && successState.anime.favorite },
|
||||||
onMigrateClicked = { navigator.push(MigrateAnimeSearchScreen(successState.anime.id)) }.takeIf { successState.anime.favorite },
|
onMigrateClicked = { navigator.push(MigrateAnimeSearchScreen(successState.anime.id)) }.takeIf { successState.anime.favorite },
|
||||||
changeAnimeSkipIntro = screenModel::showAnimeSkipIntroDialog.takeIf { successState.anime.favorite },
|
changeAnimeSkipIntro = screenModel::showAnimeSkipIntroDialog.takeIf { successState.anime.favorite },
|
||||||
onMultiBookmarkClicked = screenModel::bookmarkEpisodes,
|
onMultiBookmarkClicked = screenModel::bookmarkEpisodes,
|
||||||
|
@ -235,11 +242,11 @@ class AnimeScreen(
|
||||||
LoadingScreen(Modifier.systemBarsPadding())
|
LoadingScreen(Modifier.systemBarsPadding())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is AnimeScreenModel.Dialog.SetAnimeInterval -> {
|
is AnimeScreenModel.Dialog.SetAnimeFetchInterval -> {
|
||||||
SetIntervalDialog(
|
SetIntervalDialog(
|
||||||
interval = if (dialog.anime.calculateInterval < 0) -dialog.anime.calculateInterval else 0,
|
interval = if (dialog.anime.fetchInterval < 0) -dialog.anime.fetchInterval else 0,
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
onValueChanged = { screenModel.setFetchRangeInterval(dialog.anime, it) },
|
onValueChanged = { screenModel.setFetchInterval(dialog.anime, it) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
AnimeScreenModel.Dialog.ChangeAnimeSkipIntro -> {
|
AnimeScreenModel.Dialog.ChangeAnimeSkipIntro -> {
|
||||||
|
|
|
@ -79,7 +79,6 @@ import tachiyomi.source.local.entries.anime.isLocal
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import java.util.Calendar
|
import java.util.Calendar
|
||||||
import kotlin.math.absoluteValue
|
|
||||||
|
|
||||||
class AnimeScreenModel(
|
class AnimeScreenModel(
|
||||||
val context: Context,
|
val context: Context,
|
||||||
|
@ -135,9 +134,9 @@ class AnimeScreenModel(
|
||||||
|
|
||||||
val dateFormat by mutableStateOf(UiPreferences.dateFormat(uiPreferences.dateFormat().get()))
|
val dateFormat by mutableStateOf(UiPreferences.dateFormat(uiPreferences.dateFormat().get()))
|
||||||
|
|
||||||
val isIntervalEnabled = LibraryPreferences.ENTRY_OUTSIDE_RELEASE_PERIOD in libraryPreferences.libraryUpdateItemRestriction().get()
|
val isUpdateIntervalEnabled = LibraryPreferences.ENTRY_OUTSIDE_RELEASE_PERIOD in libraryPreferences.libraryUpdateItemRestriction().get()
|
||||||
private val leadDay = libraryPreferences.leadingAnimeExpectedDays().get()
|
val leadDay = libraryPreferences.leadingAnimeExpectedDays().get()
|
||||||
private val followDay = libraryPreferences.followingAnimeExpectedDays().get()
|
val followDay = libraryPreferences.followingAnimeExpectedDays().get()
|
||||||
|
|
||||||
private val selectedPositions: Array<Int> = arrayOf(-1, -1) // first and last selected index in list
|
private val selectedPositions: Array<Int> = arrayOf(-1, -1) // first and last selected index in list
|
||||||
private val selectedEpisodeIds: HashSet<Long> = HashSet()
|
private val selectedEpisodeIds: HashSet<Long> = HashSet()
|
||||||
|
@ -296,7 +295,7 @@ class AnimeScreenModel(
|
||||||
// Add to library
|
// Add to library
|
||||||
// First, check if duplicate exists if callback is provided
|
// First, check if duplicate exists if callback is provided
|
||||||
if (checkDuplicate) {
|
if (checkDuplicate) {
|
||||||
val duplicate = getDuplicateLibraryAnime.await(anime.title)
|
val duplicate = getDuplicateLibraryAnime.await(anime).getOrNull(0)
|
||||||
if (duplicate != null) {
|
if (duplicate != null) {
|
||||||
updateSuccessState { it.copy(dialog = Dialog.DuplicateAnime(anime, duplicate)) }
|
updateSuccessState { it.copy(dialog = Dialog.DuplicateAnime(anime, duplicate)) }
|
||||||
return@launchIO
|
return@launchIO
|
||||||
|
@ -371,30 +370,23 @@ class AnimeScreenModel(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showSetAnimeIntervalDialog() {
|
fun showSetAnimeFetchIntervalDialog() {
|
||||||
val anime = successState?.anime ?: return
|
val anime = successState?.anime ?: return
|
||||||
updateSuccessState {
|
updateSuccessState {
|
||||||
it.copy(dialog = Dialog.SetAnimeInterval(anime))
|
it.copy(dialog = Dialog.SetAnimeFetchInterval(anime))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: this should be in the state/composables
|
fun setFetchInterval(anime: Anime, newInterval: Int) {
|
||||||
fun intervalDisplay(): Pair<Int, Int>? {
|
|
||||||
val anime = successState?.anime ?: return null
|
|
||||||
val effInterval = anime.calculateInterval
|
|
||||||
return 1.coerceAtLeast(effInterval.absoluteValue - leadDay) to (effInterval.absoluteValue + followDay)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setFetchRangeInterval(anime: Anime, newInterval: Int) {
|
|
||||||
val interval = when (newInterval) {
|
val interval = when (newInterval) {
|
||||||
// reset interval 0 default to trigger recalculation
|
// reset interval 0 default to trigger recalculation
|
||||||
// only reset if interval is custom, which is negative
|
// only reset if interval is custom, which is negative
|
||||||
0 -> if (anime.calculateInterval < 0) 0 else anime.calculateInterval
|
0 -> if (anime.fetchInterval < 0) 0 else anime.fetchInterval
|
||||||
else -> -newInterval
|
else -> -newInterval
|
||||||
}
|
}
|
||||||
coroutineScope.launchIO {
|
coroutineScope.launchIO {
|
||||||
updateAnime.awaitUpdateFetchInterval(
|
updateAnime.awaitUpdateFetchInterval(
|
||||||
anime.copy(calculateInterval = interval),
|
anime.copy(fetchInterval = interval),
|
||||||
successState?.episodes?.map { it.episode }.orEmpty(),
|
successState?.episodes?.map { it.episode }.orEmpty(),
|
||||||
)
|
)
|
||||||
val newAnime = animeRepository.getAnimeById(animeId)
|
val newAnime = animeRepository.getAnimeById(animeId)
|
||||||
|
@ -1011,7 +1003,7 @@ class AnimeScreenModel(
|
||||||
data class ChangeCategory(val anime: Anime, val initialSelection: List<CheckboxState<Category>>) : Dialog
|
data class ChangeCategory(val anime: Anime, val initialSelection: List<CheckboxState<Category>>) : Dialog
|
||||||
data class DeleteEpisodes(val episodes: List<Episode>) : Dialog
|
data class DeleteEpisodes(val episodes: List<Episode>) : Dialog
|
||||||
data class DuplicateAnime(val anime: Anime, val duplicate: Anime) : Dialog
|
data class DuplicateAnime(val anime: Anime, val duplicate: Anime) : Dialog
|
||||||
data class SetAnimeInterval(val anime: Anime) : Dialog
|
data class SetAnimeFetchInterval(val anime: Anime) : Dialog
|
||||||
data class ShowQualities(val episode: Episode, val anime: Anime, val source: AnimeSource) : Dialog
|
data class ShowQualities(val episode: Episode, val anime: Anime, val source: AnimeSource) : Dialog
|
||||||
data object ChangeAnimeSkipIntro : Dialog
|
data object ChangeAnimeSkipIntro : Dialog
|
||||||
data object SettingsSheet : Dialog
|
data object SettingsSheet : Dialog
|
||||||
|
@ -1113,3 +1105,10 @@ data class EpisodeItem(
|
||||||
) {
|
) {
|
||||||
val isDownloaded = downloadState == AnimeDownload.State.DOWNLOADED
|
val isDownloaded = downloadState == AnimeDownload.State.DOWNLOADED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
data class FetchAnimeInterval(
|
||||||
|
val interval: Int,
|
||||||
|
val leadDays: Int,
|
||||||
|
val followDays: Int,
|
||||||
|
)
|
||||||
|
|
|
@ -84,6 +84,13 @@ class MangaScreen(
|
||||||
|
|
||||||
val successState = state as MangaScreenModel.State.Success
|
val successState = state as MangaScreenModel.State.Success
|
||||||
val isHttpSource = remember { successState.source is HttpSource }
|
val isHttpSource = remember { successState.source is HttpSource }
|
||||||
|
val fetchInterval = remember(successState.manga.fetchInterval) {
|
||||||
|
FetchMangaInterval(
|
||||||
|
interval = successState.manga.fetchInterval,
|
||||||
|
leadDays = screenModel.leadDay,
|
||||||
|
followDays = screenModel.followDay,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
LaunchedEffect(successState.manga, screenModel.source) {
|
LaunchedEffect(successState.manga, screenModel.source) {
|
||||||
if (isHttpSource) {
|
if (isHttpSource) {
|
||||||
|
@ -101,7 +108,7 @@ class MangaScreen(
|
||||||
state = successState,
|
state = successState,
|
||||||
snackbarHostState = screenModel.snackbarHostState,
|
snackbarHostState = screenModel.snackbarHostState,
|
||||||
dateFormat = screenModel.dateFormat,
|
dateFormat = screenModel.dateFormat,
|
||||||
intervalDisplay = screenModel::intervalDisplay,
|
fetchInterval = fetchInterval,
|
||||||
isTabletUi = isTabletUi(),
|
isTabletUi = isTabletUi(),
|
||||||
chapterSwipeStartAction = screenModel.chapterSwipeStartAction,
|
chapterSwipeStartAction = screenModel.chapterSwipeStartAction,
|
||||||
chapterSwipeEndAction = screenModel.chapterSwipeEndAction,
|
chapterSwipeEndAction = screenModel.chapterSwipeEndAction,
|
||||||
|
@ -124,7 +131,7 @@ class MangaScreen(
|
||||||
onShareClicked = { shareManga(context, screenModel.manga, screenModel.source) }.takeIf { isHttpSource },
|
onShareClicked = { shareManga(context, screenModel.manga, screenModel.source) }.takeIf { isHttpSource },
|
||||||
onDownloadActionClicked = screenModel::runDownloadAction.takeIf { !successState.source.isLocalOrStub() },
|
onDownloadActionClicked = screenModel::runDownloadAction.takeIf { !successState.source.isLocalOrStub() },
|
||||||
onEditCategoryClicked = screenModel::showChangeCategoryDialog.takeIf { successState.manga.favorite },
|
onEditCategoryClicked = screenModel::showChangeCategoryDialog.takeIf { successState.manga.favorite },
|
||||||
onEditIntervalClicked = screenModel::showSetMangaIntervalDialog.takeIf { screenModel.isIntervalEnabled && successState.manga.favorite },
|
onEditFetchIntervalClicked = screenModel::showSetMangaFetchIntervalDialog.takeIf { screenModel.isUpdateIntervalEnabled && successState.manga.favorite },
|
||||||
onMigrateClicked = { navigator.push(MigrateSearchScreen(successState.manga.id)) }.takeIf { successState.manga.favorite },
|
onMigrateClicked = { navigator.push(MigrateSearchScreen(successState.manga.id)) }.takeIf { successState.manga.favorite },
|
||||||
onMultiBookmarkClicked = screenModel::bookmarkChapters,
|
onMultiBookmarkClicked = screenModel::bookmarkChapters,
|
||||||
onMultiMarkAsReadClicked = screenModel::markChaptersRead,
|
onMultiMarkAsReadClicked = screenModel::markChaptersRead,
|
||||||
|
@ -217,11 +224,11 @@ class MangaScreen(
|
||||||
LoadingScreen(Modifier.systemBarsPadding())
|
LoadingScreen(Modifier.systemBarsPadding())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is MangaScreenModel.Dialog.SetMangaInterval -> {
|
is MangaScreenModel.Dialog.SetMangaFetchInterval -> {
|
||||||
SetIntervalDialog(
|
SetIntervalDialog(
|
||||||
interval = if (dialog.manga.calculateInterval < 0) -dialog.manga.calculateInterval else 0,
|
interval = if (dialog.manga.fetchInterval < 0) -dialog.manga.fetchInterval else 0,
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
onValueChanged = { screenModel.setFetchRangeInterval(dialog.manga, it) },
|
onValueChanged = { screenModel.setFetchInterval(dialog.manga, it) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,7 +76,6 @@ import tachiyomi.domain.track.manga.interactor.GetMangaTracks
|
||||||
import tachiyomi.source.local.entries.manga.isLocal
|
import tachiyomi.source.local.entries.manga.isLocal
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import kotlin.math.absoluteValue
|
|
||||||
|
|
||||||
class MangaScreenModel(
|
class MangaScreenModel(
|
||||||
val context: Context,
|
val context: Context,
|
||||||
|
@ -131,9 +130,9 @@ class MangaScreenModel(
|
||||||
val dateFormat by mutableStateOf(UiPreferences.dateFormat(uiPreferences.dateFormat().get()))
|
val dateFormat by mutableStateOf(UiPreferences.dateFormat(uiPreferences.dateFormat().get()))
|
||||||
val skipFiltered by readerPreferences.skipFiltered().asState(coroutineScope)
|
val skipFiltered by readerPreferences.skipFiltered().asState(coroutineScope)
|
||||||
|
|
||||||
val isIntervalEnabled = LibraryPreferences.ENTRY_OUTSIDE_RELEASE_PERIOD in libraryPreferences.libraryUpdateItemRestriction().get()
|
val isUpdateIntervalEnabled = LibraryPreferences.ENTRY_OUTSIDE_RELEASE_PERIOD in libraryPreferences.libraryUpdateItemRestriction().get()
|
||||||
private val leadDay = libraryPreferences.leadingMangaExpectedDays().get()
|
val leadDay = libraryPreferences.leadingMangaExpectedDays().get()
|
||||||
private val followDay = libraryPreferences.followingMangaExpectedDays().get()
|
val followDay = libraryPreferences.followingMangaExpectedDays().get()
|
||||||
|
|
||||||
private val selectedPositions: Array<Int> = arrayOf(-1, -1) // first and last selected index in list
|
private val selectedPositions: Array<Int> = arrayOf(-1, -1) // first and last selected index in list
|
||||||
private val selectedChapterIds: HashSet<Long> = HashSet()
|
private val selectedChapterIds: HashSet<Long> = HashSet()
|
||||||
|
@ -292,7 +291,7 @@ class MangaScreenModel(
|
||||||
// Add to library
|
// Add to library
|
||||||
// First, check if duplicate exists if callback is provided
|
// First, check if duplicate exists if callback is provided
|
||||||
if (checkDuplicate) {
|
if (checkDuplicate) {
|
||||||
val duplicate = getDuplicateLibraryManga.await(manga.title)
|
val duplicate = getDuplicateLibraryManga.await(manga).getOrNull(0)
|
||||||
|
|
||||||
if (duplicate != null) {
|
if (duplicate != null) {
|
||||||
updateSuccessState { it.copy(dialog = Dialog.DuplicateManga(manga, duplicate)) }
|
updateSuccessState { it.copy(dialog = Dialog.DuplicateManga(manga, duplicate)) }
|
||||||
|
@ -368,30 +367,23 @@ class MangaScreenModel(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showSetMangaIntervalDialog() {
|
fun showSetMangaFetchIntervalDialog() {
|
||||||
val manga = successState?.manga ?: return
|
val manga = successState?.manga ?: return
|
||||||
updateSuccessState {
|
updateSuccessState {
|
||||||
it.copy(dialog = Dialog.SetMangaInterval(manga))
|
it.copy(dialog = Dialog.SetMangaFetchInterval(manga))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: this should be in the state/composables
|
fun setFetchInterval(manga: Manga, newInterval: Int) {
|
||||||
fun intervalDisplay(): Pair<Int, Int>? {
|
|
||||||
val manga = successState?.manga ?: return null
|
|
||||||
val effInterval = manga.calculateInterval
|
|
||||||
return 1.coerceAtLeast(effInterval.absoluteValue - leadDay) to (effInterval.absoluteValue + followDay)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setFetchRangeInterval(manga: Manga, newInterval: Int) {
|
|
||||||
val interval = when (newInterval) {
|
val interval = when (newInterval) {
|
||||||
// reset interval 0 default to trigger recalculation
|
// reset interval 0 default to trigger recalculation
|
||||||
// only reset if interval is custom, which is negative
|
// only reset if interval is custom, which is negative
|
||||||
0 -> if (manga.calculateInterval < 0) 0 else manga.calculateInterval
|
0 -> if (manga.fetchInterval < 0) 0 else manga.fetchInterval
|
||||||
else -> -newInterval
|
else -> -newInterval
|
||||||
}
|
}
|
||||||
coroutineScope.launchIO {
|
coroutineScope.launchIO {
|
||||||
updateManga.awaitUpdateFetchInterval(
|
updateManga.awaitUpdateFetchInterval(
|
||||||
manga.copy(calculateInterval = interval),
|
manga.copy(fetchInterval = interval),
|
||||||
successState?.chapters?.map { it.chapter }.orEmpty(),
|
successState?.chapters?.map { it.chapter }.orEmpty(),
|
||||||
)
|
)
|
||||||
val newManga = mangaRepository.getMangaById(mangaId)
|
val newManga = mangaRepository.getMangaById(mangaId)
|
||||||
|
@ -996,7 +988,7 @@ class MangaScreenModel(
|
||||||
data class ChangeCategory(val manga: Manga, val initialSelection: List<CheckboxState<Category>>) : Dialog
|
data class ChangeCategory(val manga: Manga, val initialSelection: List<CheckboxState<Category>>) : Dialog
|
||||||
data class DeleteChapters(val chapters: List<Chapter>) : Dialog
|
data class DeleteChapters(val chapters: List<Chapter>) : Dialog
|
||||||
data class DuplicateManga(val manga: Manga, val duplicate: Manga) : Dialog
|
data class DuplicateManga(val manga: Manga, val duplicate: Manga) : Dialog
|
||||||
data class SetMangaInterval(val manga: Manga) : Dialog
|
data class SetMangaFetchInterval(val manga: Manga) : Dialog
|
||||||
data object SettingsSheet : Dialog
|
data object SettingsSheet : Dialog
|
||||||
data object TrackSheet : Dialog
|
data object TrackSheet : Dialog
|
||||||
data object FullCover : Dialog
|
data object FullCover : Dialog
|
||||||
|
@ -1081,3 +1073,10 @@ data class ChapterItem(
|
||||||
) {
|
) {
|
||||||
val isDownloaded = downloadState == MangaDownload.State.DOWNLOADED
|
val isDownloaded = downloadState == MangaDownload.State.DOWNLOADED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
data class FetchMangaInterval(
|
||||||
|
val interval: Int,
|
||||||
|
val leadDays: Int,
|
||||||
|
val followDays: Int,
|
||||||
|
)
|
||||||
|
|
|
@ -85,7 +85,8 @@ class AnimeUpdatesScreenModel(
|
||||||
combine(
|
combine(
|
||||||
getUpdates.subscribe(calendar).distinctUntilChanged(),
|
getUpdates.subscribe(calendar).distinctUntilChanged(),
|
||||||
downloadCache.changes,
|
downloadCache.changes,
|
||||||
) { updates, _ -> updates }
|
downloadManager.queueState,
|
||||||
|
) { updates, _, _ -> updates }
|
||||||
.catch {
|
.catch {
|
||||||
logcat(LogPriority.ERROR, it)
|
logcat(LogPriority.ERROR, it)
|
||||||
_events.send(Event.InternalError)
|
_events.send(Event.InternalError)
|
||||||
|
|
|
@ -81,7 +81,8 @@ class MangaUpdatesScreenModel(
|
||||||
combine(
|
combine(
|
||||||
getUpdates.subscribe(calendar).distinctUntilChanged(),
|
getUpdates.subscribe(calendar).distinctUntilChanged(),
|
||||||
downloadCache.changes,
|
downloadCache.changes,
|
||||||
) { updates, _ -> updates }
|
downloadManager.queueState,
|
||||||
|
) { updates, _, _ -> updates }
|
||||||
.catch {
|
.catch {
|
||||||
logcat(LogPriority.ERROR, it)
|
logcat(LogPriority.ERROR, it)
|
||||||
_events.send(Event.InternalError)
|
_events.send(Event.InternalError)
|
||||||
|
|
|
@ -120,7 +120,7 @@ fun Date.toRelativeString(
|
||||||
val difference = now.timeWithOffset.floorNearest(MILLISECONDS_IN_DAY) - this.timeWithOffset.floorNearest(MILLISECONDS_IN_DAY)
|
val difference = now.timeWithOffset.floorNearest(MILLISECONDS_IN_DAY) - this.timeWithOffset.floorNearest(MILLISECONDS_IN_DAY)
|
||||||
val days = difference.floorDiv(MILLISECONDS_IN_DAY).toInt()
|
val days = difference.floorDiv(MILLISECONDS_IN_DAY).toInt()
|
||||||
return when {
|
return when {
|
||||||
difference < 0 -> context.getString(R.string.recently)
|
difference < 0 -> dateFormat.format(this)
|
||||||
difference < MILLISECONDS_IN_DAY -> context.getString(R.string.relative_time_today)
|
difference < MILLISECONDS_IN_DAY -> context.getString(R.string.relative_time_today)
|
||||||
difference < MILLISECONDS_IN_DAY.times(7) -> context.resources.getQuantityString(
|
difference < MILLISECONDS_IN_DAY.times(7) -> context.resources.getQuantityString(
|
||||||
R.plurals.relative_time,
|
R.plurals.relative_time,
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
|
import org.jetbrains.kotlin.cli.jvm.main
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("com.android.library")
|
id("com.android.library")
|
||||||
kotlin("android")
|
kotlin("android")
|
||||||
kotlin("plugin.serialization")
|
kotlin("plugin.serialization")
|
||||||
id("com.squareup.sqldelight")
|
id("app.cash.sqldelight")
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
@ -13,17 +15,19 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
sqldelight {
|
sqldelight {
|
||||||
database("Database") {
|
databases {
|
||||||
packageName = "tachiyomi.data"
|
create("Database") {
|
||||||
dialect = "sqlite:3.24"
|
packageName.set("tachiyomi.data")
|
||||||
schemaOutputDirectory = project.file("./src/main/sqldelight")
|
dialect(libs.sqldelight.dialects.sql)
|
||||||
sourceFolders = listOf("sqldelight")
|
schemaOutputDirectory.set(project.file("./src/main/sqldelight"))
|
||||||
|
srcDirs.from(project.file("./src/main/sqldelight"))
|
||||||
|
}
|
||||||
|
create("AnimeDatabase") {
|
||||||
|
packageName.set("tachiyomi.mi.data")
|
||||||
|
dialect(libs.sqldelight.dialects.sql)
|
||||||
|
schemaOutputDirectory.set(project.file("./src/main/sqldelightanime"))
|
||||||
|
srcDirs.from(project.file("./src/main/sqldelightanime"))
|
||||||
}
|
}
|
||||||
database("AnimeDatabase") {
|
|
||||||
packageName = "tachiyomi.mi.data"
|
|
||||||
dialect = "sqlite:3.24"
|
|
||||||
schemaOutputDirectory = project.file("./src/main/sqldelightanime")
|
|
||||||
sourceFolders = listOf("sqldelightanime")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,9 +37,7 @@ dependencies {
|
||||||
implementation(project(":domain"))
|
implementation(project(":domain"))
|
||||||
implementation(project(":core"))
|
implementation(project(":core"))
|
||||||
|
|
||||||
api(libs.sqldelight.android.driver)
|
api(libs.bundles.sqldelight)
|
||||||
api(libs.sqldelight.coroutines)
|
|
||||||
api(libs.sqldelight.android.paging)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package tachiyomi.data
|
package tachiyomi.data
|
||||||
|
|
||||||
import com.squareup.sqldelight.ColumnAdapter
|
import app.cash.sqldelight.ColumnAdapter
|
||||||
import eu.kanade.tachiyomi.source.model.UpdateStrategy
|
import eu.kanade.tachiyomi.source.model.UpdateStrategy
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ val animeMapper: (Long, Long, String, String?, String?, String?, List<String>?,
|
||||||
favorite = favorite,
|
favorite = favorite,
|
||||||
lastUpdate = lastUpdate ?: 0,
|
lastUpdate = lastUpdate ?: 0,
|
||||||
nextUpdate = nextUpdate ?: 0,
|
nextUpdate = nextUpdate ?: 0,
|
||||||
calculateInterval = calculateInterval.toInt(),
|
fetchInterval = calculateInterval.toInt(),
|
||||||
dateAdded = dateAdded,
|
dateAdded = dateAdded,
|
||||||
viewerFlags = viewerFlags,
|
viewerFlags = viewerFlags,
|
||||||
episodeFlags = episodeFlags,
|
episodeFlags = episodeFlags,
|
||||||
|
@ -32,7 +32,7 @@ val animeMapper: (Long, Long, String, String?, String?, String?, List<String>?,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val libraryAnime: (Long, Long, String, String?, String?, String?, List<String>?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, UpdateStrategy, Long, Long, Long?, Long, Long, Long, Long, Long, Long, Long) -> LibraryAnime =
|
val libraryAnime: (Long, Long, String, String?, String?, String?, List<String>?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, UpdateStrategy, Long, Long, Long?, Long, Double, Long, Long, Long, Double, Long) -> LibraryAnime =
|
||||||
{ id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, episodeFlags, coverLastModified, dateAdded, updateStrategy, calculateInterval, lastModifiedAt, favoriteModifiedAt, totalCount, seenCount, latestUpload, episodeFetchedAt, lastSeen, bookmarkCount, category ->
|
{ id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, episodeFlags, coverLastModified, dateAdded, updateStrategy, calculateInterval, lastModifiedAt, favoriteModifiedAt, totalCount, seenCount, latestUpload, episodeFetchedAt, lastSeen, bookmarkCount, category ->
|
||||||
LibraryAnime(
|
LibraryAnime(
|
||||||
anime = animeMapper(
|
anime = animeMapper(
|
||||||
|
@ -61,8 +61,8 @@ val libraryAnime: (Long, Long, String, String?, String?, String?, List<String>?,
|
||||||
),
|
),
|
||||||
category = category,
|
category = category,
|
||||||
totalEpisodes = totalCount,
|
totalEpisodes = totalCount,
|
||||||
seenCount = seenCount,
|
seenCount = seenCount.toLong(),
|
||||||
bookmarkCount = bookmarkCount,
|
bookmarkCount = bookmarkCount.toLong(),
|
||||||
latestUpload = latestUpload,
|
latestUpload = latestUpload,
|
||||||
episodeFetchedAt = episodeFetchedAt,
|
episodeFetchedAt = episodeFetchedAt,
|
||||||
lastSeen = lastSeen,
|
lastSeen = lastSeen,
|
||||||
|
|
|
@ -48,9 +48,9 @@ class AnimeRepositoryImpl(
|
||||||
return handler.subscribeToList { animesQueries.getFavoriteBySourceId(sourceId, animeMapper) }
|
return handler.subscribeToList { animesQueries.getFavoriteBySourceId(sourceId, animeMapper) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getDuplicateLibraryAnime(title: String): Anime? {
|
override suspend fun getDuplicateLibraryAnime(id: Long, title: String): List<Anime> {
|
||||||
return handler.awaitOneOrNull {
|
return handler.awaitList {
|
||||||
animesQueries.getDuplicateLibraryAnime(title, animeMapper)
|
animesQueries.getDuplicateLibraryAnime(title, id, animeMapper)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ class AnimeRepositoryImpl(
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun insertAnime(anime: Anime): Long? {
|
override suspend fun insertAnime(anime: Anime): Long? {
|
||||||
return handler.awaitOneOrNull(inTransaction = true) {
|
return handler.awaitOneOrNullExecutable(inTransaction = true) {
|
||||||
animesQueries.insert(
|
animesQueries.insert(
|
||||||
source = anime.source,
|
source = anime.source,
|
||||||
url = anime.url,
|
url = anime.url,
|
||||||
|
@ -88,7 +88,7 @@ class AnimeRepositoryImpl(
|
||||||
favorite = anime.favorite,
|
favorite = anime.favorite,
|
||||||
lastUpdate = anime.lastUpdate,
|
lastUpdate = anime.lastUpdate,
|
||||||
nextUpdate = anime.nextUpdate,
|
nextUpdate = anime.nextUpdate,
|
||||||
calculateInterval = anime.calculateInterval.toLong(),
|
calculateInterval = anime.fetchInterval.toLong(),
|
||||||
initialized = anime.initialized,
|
initialized = anime.initialized,
|
||||||
viewerFlags = anime.viewerFlags,
|
viewerFlags = anime.viewerFlags,
|
||||||
episodeFlags = anime.episodeFlags,
|
episodeFlags = anime.episodeFlags,
|
||||||
|
@ -133,11 +133,11 @@ class AnimeRepositoryImpl(
|
||||||
title = value.title,
|
title = value.title,
|
||||||
status = value.status,
|
status = value.status,
|
||||||
thumbnailUrl = value.thumbnailUrl,
|
thumbnailUrl = value.thumbnailUrl,
|
||||||
favorite = value.favorite?.toLong(),
|
favorite = value.favorite,
|
||||||
lastUpdate = value.lastUpdate,
|
lastUpdate = value.lastUpdate,
|
||||||
nextUpdate = value.nextUpdate,
|
nextUpdate = value.nextUpdate,
|
||||||
calculateInterval = value.calculateInterval?.toLong(),
|
calculateInterval = value.fetchInterval?.toLong(),
|
||||||
initialized = value.initialized?.toLong(),
|
initialized = value.initialized,
|
||||||
viewer = value.viewerFlags,
|
viewer = value.viewerFlags,
|
||||||
episodeFlags = value.episodeFlags,
|
episodeFlags = value.episodeFlags,
|
||||||
coverLastModified = value.coverLastModified,
|
coverLastModified = value.coverLastModified,
|
||||||
|
|
|
@ -12,7 +12,7 @@ val mangaMapper: (Long, Long, String, String?, String?, String?, List<String>?,
|
||||||
favorite = favorite,
|
favorite = favorite,
|
||||||
lastUpdate = lastUpdate ?: 0,
|
lastUpdate = lastUpdate ?: 0,
|
||||||
nextUpdate = nextUpdate ?: 0,
|
nextUpdate = nextUpdate ?: 0,
|
||||||
calculateInterval = calculateInterval.toInt(),
|
fetchInterval = calculateInterval.toInt(),
|
||||||
dateAdded = dateAdded,
|
dateAdded = dateAdded,
|
||||||
viewerFlags = viewerFlags,
|
viewerFlags = viewerFlags,
|
||||||
chapterFlags = chapterFlags,
|
chapterFlags = chapterFlags,
|
||||||
|
@ -32,7 +32,7 @@ val mangaMapper: (Long, Long, String, String?, String?, String?, List<String>?,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val libraryManga: (Long, Long, String, String?, String?, String?, List<String>?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, UpdateStrategy, Long, Long, Long?, Long, Long, Long, Long, Long, Long, Long) -> LibraryManga =
|
val libraryManga: (Long, Long, String, String?, String?, String?, List<String>?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, UpdateStrategy, Long, Long, Long?, Long, Double, Long, Long, Long, Double, Long) -> LibraryManga =
|
||||||
{ id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, chapterFlags, coverLastModified, dateAdded, updateStrategy, calculateInterval, lastModifiedAt, favoriteModifiedAt, totalCount, readCount, latestUpload, chapterFetchedAt, lastRead, bookmarkCount, category ->
|
{ id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, chapterFlags, coverLastModified, dateAdded, updateStrategy, calculateInterval, lastModifiedAt, favoriteModifiedAt, totalCount, readCount, latestUpload, chapterFetchedAt, lastRead, bookmarkCount, category ->
|
||||||
LibraryManga(
|
LibraryManga(
|
||||||
manga = mangaMapper(
|
manga = mangaMapper(
|
||||||
|
@ -61,8 +61,8 @@ val libraryManga: (Long, Long, String, String?, String?, String?, List<String>?,
|
||||||
),
|
),
|
||||||
category = category,
|
category = category,
|
||||||
totalChapters = totalCount,
|
totalChapters = totalCount,
|
||||||
readCount = readCount,
|
readCount = readCount.toLong(),
|
||||||
bookmarkCount = bookmarkCount,
|
bookmarkCount = bookmarkCount.toLong(),
|
||||||
latestUpload = latestUpload,
|
latestUpload = latestUpload,
|
||||||
chapterFetchedAt = chapterFetchedAt,
|
chapterFetchedAt = chapterFetchedAt,
|
||||||
lastRead = lastRead,
|
lastRead = lastRead,
|
||||||
|
|
|
@ -48,9 +48,9 @@ class MangaRepositoryImpl(
|
||||||
return handler.subscribeToList { mangasQueries.getFavoriteBySourceId(sourceId, mangaMapper) }
|
return handler.subscribeToList { mangasQueries.getFavoriteBySourceId(sourceId, mangaMapper) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getDuplicateLibraryManga(title: String): Manga? {
|
override suspend fun getDuplicateLibraryManga(id: Long, title: String): List<Manga> {
|
||||||
return handler.awaitOneOrNull {
|
return handler.awaitList {
|
||||||
mangasQueries.getDuplicateLibraryManga(title, mangaMapper)
|
mangasQueries.getDuplicateLibraryManga(title, id, mangaMapper)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ class MangaRepositoryImpl(
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun insertManga(manga: Manga): Long? {
|
override suspend fun insertManga(manga: Manga): Long? {
|
||||||
return handler.awaitOneOrNull(inTransaction = true) {
|
return handler.awaitOneOrNullExecutable(inTransaction = true) {
|
||||||
mangasQueries.insert(
|
mangasQueries.insert(
|
||||||
source = manga.source,
|
source = manga.source,
|
||||||
url = manga.url,
|
url = manga.url,
|
||||||
|
@ -88,7 +88,7 @@ class MangaRepositoryImpl(
|
||||||
favorite = manga.favorite,
|
favorite = manga.favorite,
|
||||||
lastUpdate = manga.lastUpdate,
|
lastUpdate = manga.lastUpdate,
|
||||||
nextUpdate = manga.nextUpdate,
|
nextUpdate = manga.nextUpdate,
|
||||||
calculateInterval = manga.calculateInterval.toLong(),
|
calculateInterval = manga.fetchInterval.toLong(),
|
||||||
initialized = manga.initialized,
|
initialized = manga.initialized,
|
||||||
viewerFlags = manga.viewerFlags,
|
viewerFlags = manga.viewerFlags,
|
||||||
chapterFlags = manga.chapterFlags,
|
chapterFlags = manga.chapterFlags,
|
||||||
|
@ -133,11 +133,11 @@ class MangaRepositoryImpl(
|
||||||
title = value.title,
|
title = value.title,
|
||||||
status = value.status,
|
status = value.status,
|
||||||
thumbnailUrl = value.thumbnailUrl,
|
thumbnailUrl = value.thumbnailUrl,
|
||||||
favorite = value.favorite?.toLong(),
|
favorite = value.favorite,
|
||||||
lastUpdate = value.lastUpdate,
|
lastUpdate = value.lastUpdate,
|
||||||
nextUpdate = value.nextUpdate,
|
nextUpdate = value.nextUpdate,
|
||||||
calculateInterval = value.calculateInterval?.toLong(),
|
calculateInterval = value.fetchInterval?.toLong(),
|
||||||
initialized = value.initialized?.toLong(),
|
initialized = value.initialized,
|
||||||
viewer = value.viewerFlags,
|
viewer = value.viewerFlags,
|
||||||
chapterFlags = value.chapterFlags,
|
chapterFlags = value.chapterFlags,
|
||||||
coverLastModified = value.coverLastModified,
|
coverLastModified = value.coverLastModified,
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
package tachiyomi.data.handlers.anime
|
package tachiyomi.data.handlers.anime
|
||||||
|
|
||||||
import androidx.paging.PagingSource
|
import androidx.paging.PagingSource
|
||||||
import com.squareup.sqldelight.Query
|
import app.cash.sqldelight.ExecutableQuery
|
||||||
import com.squareup.sqldelight.db.SqlDriver
|
import app.cash.sqldelight.Query
|
||||||
import com.squareup.sqldelight.runtime.coroutines.asFlow
|
import app.cash.sqldelight.coroutines.asFlow
|
||||||
import com.squareup.sqldelight.runtime.coroutines.mapToList
|
import app.cash.sqldelight.coroutines.mapToList
|
||||||
import com.squareup.sqldelight.runtime.coroutines.mapToOne
|
import app.cash.sqldelight.coroutines.mapToOne
|
||||||
import com.squareup.sqldelight.runtime.coroutines.mapToOneOrNull
|
import app.cash.sqldelight.coroutines.mapToOneOrNull
|
||||||
|
import app.cash.sqldelight.db.SqlDriver
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
@ -40,6 +41,13 @@ class AndroidAnimeDatabaseHandler(
|
||||||
return dispatch(inTransaction) { block(db).executeAsOne() }
|
return dispatch(inTransaction) { block(db).executeAsOne() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun <T : Any> awaitOneExecutable(
|
||||||
|
inTransaction: Boolean,
|
||||||
|
block: suspend AnimeDatabase.() -> ExecutableQuery<T>,
|
||||||
|
): T {
|
||||||
|
return dispatch(inTransaction) { block(db).executeAsOne() }
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun <T : Any> awaitOneOrNull(
|
override suspend fun <T : Any> awaitOneOrNull(
|
||||||
inTransaction: Boolean,
|
inTransaction: Boolean,
|
||||||
block: suspend AnimeDatabase.() -> Query<T>,
|
block: suspend AnimeDatabase.() -> Query<T>,
|
||||||
|
@ -47,6 +55,13 @@ class AndroidAnimeDatabaseHandler(
|
||||||
return dispatch(inTransaction) { block(db).executeAsOneOrNull() }
|
return dispatch(inTransaction) { block(db).executeAsOneOrNull() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun <T : Any> awaitOneOrNullExecutable(
|
||||||
|
inTransaction: Boolean,
|
||||||
|
block: suspend AnimeDatabase.() -> ExecutableQuery<T>,
|
||||||
|
): T? {
|
||||||
|
return dispatch(inTransaction) { block(db).executeAsOneOrNull() }
|
||||||
|
}
|
||||||
|
|
||||||
override fun <T : Any> subscribeToList(block: AnimeDatabase.() -> Query<T>): Flow<List<T>> {
|
override fun <T : Any> subscribeToList(block: AnimeDatabase.() -> Query<T>): Flow<List<T>> {
|
||||||
return block(db).asFlow().mapToList(queryDispatcher)
|
return block(db).asFlow().mapToList(queryDispatcher)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
package tachiyomi.data.handlers.anime
|
package tachiyomi.data.handlers.anime
|
||||||
|
|
||||||
import androidx.paging.PagingSource
|
import androidx.paging.PagingSource
|
||||||
import com.squareup.sqldelight.Query
|
import app.cash.sqldelight.ExecutableQuery
|
||||||
|
import app.cash.sqldelight.Query
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import tachiyomi.mi.data.AnimeDatabase
|
import tachiyomi.mi.data.AnimeDatabase
|
||||||
|
|
||||||
|
@ -19,11 +20,21 @@ interface AnimeDatabaseHandler {
|
||||||
block: suspend AnimeDatabase.() -> Query<T>,
|
block: suspend AnimeDatabase.() -> Query<T>,
|
||||||
): T
|
): T
|
||||||
|
|
||||||
|
suspend fun <T : Any> awaitOneExecutable(
|
||||||
|
inTransaction: Boolean = false,
|
||||||
|
block: suspend AnimeDatabase.() -> ExecutableQuery<T>,
|
||||||
|
): T
|
||||||
|
|
||||||
suspend fun <T : Any> awaitOneOrNull(
|
suspend fun <T : Any> awaitOneOrNull(
|
||||||
inTransaction: Boolean = false,
|
inTransaction: Boolean = false,
|
||||||
block: suspend AnimeDatabase.() -> Query<T>,
|
block: suspend AnimeDatabase.() -> Query<T>,
|
||||||
): T?
|
): T?
|
||||||
|
|
||||||
|
suspend fun <T : Any> awaitOneOrNullExecutable(
|
||||||
|
inTransaction: Boolean = false,
|
||||||
|
block: suspend AnimeDatabase.() -> ExecutableQuery<T>,
|
||||||
|
): T?
|
||||||
|
|
||||||
fun <T : Any> subscribeToList(block: AnimeDatabase.() -> Query<T>): Flow<List<T>>
|
fun <T : Any> subscribeToList(block: AnimeDatabase.() -> Query<T>): Flow<List<T>>
|
||||||
|
|
||||||
fun <T : Any> subscribeToOne(block: AnimeDatabase.() -> Query<T>): Flow<T>
|
fun <T : Any> subscribeToOne(block: AnimeDatabase.() -> Query<T>): Flow<T>
|
||||||
|
|
|
@ -2,7 +2,7 @@ package tachiyomi.data.handlers.anime
|
||||||
|
|
||||||
import androidx.paging.PagingSource
|
import androidx.paging.PagingSource
|
||||||
import androidx.paging.PagingState
|
import androidx.paging.PagingState
|
||||||
import com.squareup.sqldelight.Query
|
import app.cash.sqldelight.Query
|
||||||
import tachiyomi.mi.data.AnimeDatabase
|
import tachiyomi.mi.data.AnimeDatabase
|
||||||
import kotlin.properties.Delegates
|
import kotlin.properties.Delegates
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
package tachiyomi.data.handlers.manga
|
package tachiyomi.data.handlers.manga
|
||||||
|
|
||||||
import androidx.paging.PagingSource
|
import androidx.paging.PagingSource
|
||||||
import com.squareup.sqldelight.Query
|
import app.cash.sqldelight.ExecutableQuery
|
||||||
import com.squareup.sqldelight.db.SqlDriver
|
import app.cash.sqldelight.Query
|
||||||
import com.squareup.sqldelight.runtime.coroutines.asFlow
|
import app.cash.sqldelight.coroutines.asFlow
|
||||||
import com.squareup.sqldelight.runtime.coroutines.mapToList
|
import app.cash.sqldelight.coroutines.mapToList
|
||||||
import com.squareup.sqldelight.runtime.coroutines.mapToOne
|
import app.cash.sqldelight.coroutines.mapToOne
|
||||||
import com.squareup.sqldelight.runtime.coroutines.mapToOneOrNull
|
import app.cash.sqldelight.coroutines.mapToOneOrNull
|
||||||
|
import app.cash.sqldelight.db.SqlDriver
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
@ -40,6 +41,13 @@ class AndroidMangaDatabaseHandler(
|
||||||
return dispatch(inTransaction) { block(db).executeAsOne() }
|
return dispatch(inTransaction) { block(db).executeAsOne() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun <T : Any> awaitOneExecutable(
|
||||||
|
inTransaction: Boolean,
|
||||||
|
block: suspend Database.() -> ExecutableQuery<T>,
|
||||||
|
): T {
|
||||||
|
return dispatch(inTransaction) { block(db).executeAsOne() }
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun <T : Any> awaitOneOrNull(
|
override suspend fun <T : Any> awaitOneOrNull(
|
||||||
inTransaction: Boolean,
|
inTransaction: Boolean,
|
||||||
block: suspend Database.() -> Query<T>,
|
block: suspend Database.() -> Query<T>,
|
||||||
|
@ -47,6 +55,13 @@ class AndroidMangaDatabaseHandler(
|
||||||
return dispatch(inTransaction) { block(db).executeAsOneOrNull() }
|
return dispatch(inTransaction) { block(db).executeAsOneOrNull() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun <T : Any> awaitOneOrNullExecutable(
|
||||||
|
inTransaction: Boolean,
|
||||||
|
block: suspend Database.() -> ExecutableQuery<T>,
|
||||||
|
): T? {
|
||||||
|
return dispatch(inTransaction) { block(db).executeAsOneOrNull() }
|
||||||
|
}
|
||||||
|
|
||||||
override fun <T : Any> subscribeToList(block: Database.() -> Query<T>): Flow<List<T>> {
|
override fun <T : Any> subscribeToList(block: Database.() -> Query<T>): Flow<List<T>> {
|
||||||
return block(db).asFlow().mapToList(queryDispatcher)
|
return block(db).asFlow().mapToList(queryDispatcher)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
package tachiyomi.data.handlers.manga
|
package tachiyomi.data.handlers.manga
|
||||||
|
|
||||||
import androidx.paging.PagingSource
|
import androidx.paging.PagingSource
|
||||||
import com.squareup.sqldelight.Query
|
import app.cash.sqldelight.ExecutableQuery
|
||||||
|
import app.cash.sqldelight.Query
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import tachiyomi.data.Database
|
import tachiyomi.data.Database
|
||||||
|
|
||||||
|
@ -19,11 +20,21 @@ interface MangaDatabaseHandler {
|
||||||
block: suspend Database.() -> Query<T>,
|
block: suspend Database.() -> Query<T>,
|
||||||
): T
|
): T
|
||||||
|
|
||||||
|
suspend fun <T : Any> awaitOneExecutable(
|
||||||
|
inTransaction: Boolean = false,
|
||||||
|
block: suspend Database.() -> ExecutableQuery<T>,
|
||||||
|
): T
|
||||||
|
|
||||||
suspend fun <T : Any> awaitOneOrNull(
|
suspend fun <T : Any> awaitOneOrNull(
|
||||||
inTransaction: Boolean = false,
|
inTransaction: Boolean = false,
|
||||||
block: suspend Database.() -> Query<T>,
|
block: suspend Database.() -> Query<T>,
|
||||||
): T?
|
): T?
|
||||||
|
|
||||||
|
suspend fun <T : Any> awaitOneOrNullExecutable(
|
||||||
|
inTransaction: Boolean = false,
|
||||||
|
block: suspend Database.() -> ExecutableQuery<T>,
|
||||||
|
): T?
|
||||||
|
|
||||||
fun <T : Any> subscribeToList(block: Database.() -> Query<T>): Flow<List<T>>
|
fun <T : Any> subscribeToList(block: Database.() -> Query<T>): Flow<List<T>>
|
||||||
|
|
||||||
fun <T : Any> subscribeToOne(block: Database.() -> Query<T>): Flow<T>
|
fun <T : Any> subscribeToOne(block: Database.() -> Query<T>): Flow<T>
|
||||||
|
|
|
@ -2,7 +2,7 @@ package tachiyomi.data.handlers.manga
|
||||||
|
|
||||||
import androidx.paging.PagingSource
|
import androidx.paging.PagingSource
|
||||||
import androidx.paging.PagingState
|
import androidx.paging.PagingState
|
||||||
import com.squareup.sqldelight.Query
|
import app.cash.sqldelight.Query
|
||||||
import tachiyomi.data.Database
|
import tachiyomi.data.Database
|
||||||
import kotlin.properties.Delegates
|
import kotlin.properties.Delegates
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@ package tachiyomi.data.items.chapter
|
||||||
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import tachiyomi.core.util.lang.toLong
|
|
||||||
import tachiyomi.core.util.system.logcat
|
import tachiyomi.core.util.system.logcat
|
||||||
import tachiyomi.data.handlers.manga.MangaDatabaseHandler
|
import tachiyomi.data.handlers.manga.MangaDatabaseHandler
|
||||||
import tachiyomi.domain.items.chapter.model.Chapter
|
import tachiyomi.domain.items.chapter.model.Chapter
|
||||||
|
@ -56,8 +55,8 @@ class ChapterRepositoryImpl(
|
||||||
url = chapterUpdate.url,
|
url = chapterUpdate.url,
|
||||||
name = chapterUpdate.name,
|
name = chapterUpdate.name,
|
||||||
scanlator = chapterUpdate.scanlator,
|
scanlator = chapterUpdate.scanlator,
|
||||||
read = chapterUpdate.read?.toLong(),
|
read = chapterUpdate.read,
|
||||||
bookmark = chapterUpdate.bookmark?.toLong(),
|
bookmark = chapterUpdate.bookmark,
|
||||||
lastPageRead = chapterUpdate.lastPageRead,
|
lastPageRead = chapterUpdate.lastPageRead,
|
||||||
chapterNumber = chapterUpdate.chapterNumber?.toDouble(),
|
chapterNumber = chapterUpdate.chapterNumber?.toDouble(),
|
||||||
sourceOrder = chapterUpdate.sourceOrder,
|
sourceOrder = chapterUpdate.sourceOrder,
|
||||||
|
|
|
@ -2,7 +2,6 @@ package tachiyomi.data.items.episode
|
||||||
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import tachiyomi.core.util.lang.toLong
|
|
||||||
import tachiyomi.core.util.system.logcat
|
import tachiyomi.core.util.system.logcat
|
||||||
import tachiyomi.data.handlers.anime.AnimeDatabaseHandler
|
import tachiyomi.data.handlers.anime.AnimeDatabaseHandler
|
||||||
import tachiyomi.domain.items.episode.model.Episode
|
import tachiyomi.domain.items.episode.model.Episode
|
||||||
|
@ -57,8 +56,8 @@ class EpisodeRepositoryImpl(
|
||||||
url = episodeUpdate.url,
|
url = episodeUpdate.url,
|
||||||
name = episodeUpdate.name,
|
name = episodeUpdate.name,
|
||||||
scanlator = episodeUpdate.scanlator,
|
scanlator = episodeUpdate.scanlator,
|
||||||
seen = episodeUpdate.seen?.toLong(),
|
seen = episodeUpdate.seen,
|
||||||
bookmark = episodeUpdate.bookmark?.toLong(),
|
bookmark = episodeUpdate.bookmark,
|
||||||
lastSecondSeen = episodeUpdate.lastSecondSeen,
|
lastSecondSeen = episodeUpdate.lastSecondSeen,
|
||||||
totalSeconds = episodeUpdate.totalSeconds,
|
totalSeconds = episodeUpdate.totalSeconds,
|
||||||
episodeNumber = episodeUpdate.episodeNumber?.toDouble(),
|
episodeNumber = episodeUpdate.episodeNumber?.toDouble(),
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
import kotlin.Boolean;
|
||||||
|
import kotlin.Float;
|
||||||
|
|
||||||
CREATE TABLE chapters(
|
CREATE TABLE chapters(
|
||||||
_id INTEGER NOT NULL PRIMARY KEY,
|
_id INTEGER NOT NULL PRIMARY KEY,
|
||||||
manga_id INTEGER NOT NULL,
|
manga_id INTEGER NOT NULL,
|
||||||
|
@ -9,9 +12,9 @@ CREATE TABLE chapters(
|
||||||
last_page_read INTEGER NOT NULL,
|
last_page_read INTEGER NOT NULL,
|
||||||
chapter_number REAL AS Float NOT NULL,
|
chapter_number REAL AS Float NOT NULL,
|
||||||
source_order INTEGER NOT NULL,
|
source_order INTEGER NOT NULL,
|
||||||
date_fetch INTEGER AS Long NOT NULL,
|
date_fetch INTEGER NOT NULL,
|
||||||
date_upload INTEGER AS Long NOT NULL,
|
date_upload INTEGER NOT NULL,
|
||||||
last_modified_at INTEGER AS Long NOT NULL DEFAULT 0,
|
last_modified_at INTEGER NOT NULL DEFAULT 0,
|
||||||
FOREIGN KEY(manga_id) REFERENCES mangas (_id)
|
FOREIGN KEY(manga_id) REFERENCES mangas (_id)
|
||||||
ON DELETE CASCADE
|
ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import kotlin.Float;
|
||||||
|
|
||||||
CREATE TABLE manga_sync(
|
CREATE TABLE manga_sync(
|
||||||
_id INTEGER NOT NULL PRIMARY KEY,
|
_id INTEGER NOT NULL PRIMARY KEY,
|
||||||
manga_id INTEGER NOT NULL,
|
manga_id INTEGER NOT NULL,
|
||||||
|
@ -10,8 +12,8 @@ CREATE TABLE manga_sync(
|
||||||
status INTEGER NOT NULL,
|
status INTEGER NOT NULL,
|
||||||
score REAL AS Float NOT NULL,
|
score REAL AS Float NOT NULL,
|
||||||
remote_url TEXT NOT NULL,
|
remote_url TEXT NOT NULL,
|
||||||
start_date INTEGER AS Long NOT NULL,
|
start_date INTEGER NOT NULL,
|
||||||
finish_date INTEGER AS Long NOT NULL,
|
finish_date INTEGER NOT NULL,
|
||||||
UNIQUE (manga_id, sync_id) ON CONFLICT REPLACE,
|
UNIQUE (manga_id, sync_id) ON CONFLICT REPLACE,
|
||||||
FOREIGN KEY(manga_id) REFERENCES mangas (_id)
|
FOREIGN KEY(manga_id) REFERENCES mangas (_id)
|
||||||
ON DELETE CASCADE
|
ON DELETE CASCADE
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import eu.kanade.tachiyomi.source.model.UpdateStrategy;
|
import eu.kanade.tachiyomi.source.model.UpdateStrategy;
|
||||||
import java.lang.String;
|
|
||||||
import kotlin.collections.List;
|
import kotlin.collections.List;
|
||||||
|
import kotlin.Boolean;
|
||||||
|
import kotlin.String;
|
||||||
|
|
||||||
CREATE TABLE mangas(
|
CREATE TABLE mangas(
|
||||||
_id INTEGER NOT NULL PRIMARY KEY,
|
_id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
@ -14,17 +15,17 @@ CREATE TABLE mangas(
|
||||||
status INTEGER NOT NULL,
|
status INTEGER NOT NULL,
|
||||||
thumbnail_url TEXT,
|
thumbnail_url TEXT,
|
||||||
favorite INTEGER AS Boolean NOT NULL,
|
favorite INTEGER AS Boolean NOT NULL,
|
||||||
last_update INTEGER AS Long,
|
last_update INTEGER,
|
||||||
next_update INTEGER AS Long,
|
next_update INTEGER,
|
||||||
initialized INTEGER AS Boolean NOT NULL,
|
initialized INTEGER AS Boolean NOT NULL,
|
||||||
viewer INTEGER NOT NULL,
|
viewer INTEGER NOT NULL,
|
||||||
chapter_flags INTEGER NOT NULL,
|
chapter_flags INTEGER NOT NULL,
|
||||||
cover_last_modified INTEGER AS Long NOT NULL,
|
cover_last_modified INTEGER NOT NULL,
|
||||||
date_added INTEGER AS Long NOT NULL,
|
date_added INTEGER NOT NULL,
|
||||||
update_strategy INTEGER AS UpdateStrategy NOT NULL DEFAULT 0,
|
update_strategy INTEGER AS UpdateStrategy NOT NULL DEFAULT 0,
|
||||||
calculate_interval INTEGER DEFAULT 0 NOT NULL,
|
calculate_interval INTEGER DEFAULT 0 NOT NULL,
|
||||||
last_modified_at INTEGER AS Long NOT NULL DEFAULT 0,
|
last_modified_at INTEGER NOT NULL DEFAULT 0,
|
||||||
favorite_modified_at INTEGER AS Long
|
favorite_modified_at INTEGER
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX library_favorite_index ON mangas(favorite) WHERE favorite = 1;
|
CREATE INDEX library_favorite_index ON mangas(favorite) WHERE favorite = 1;
|
||||||
|
@ -92,7 +93,7 @@ SELECT *
|
||||||
FROM mangas
|
FROM mangas
|
||||||
WHERE favorite = 1
|
WHERE favorite = 1
|
||||||
AND LOWER(title) = :title
|
AND LOWER(title) = :title
|
||||||
LIMIT 1;
|
AND _id != :id;
|
||||||
|
|
||||||
resetViewerFlags:
|
resetViewerFlags:
|
||||||
UPDATE mangas
|
UPDATE mangas
|
||||||
|
|
|
@ -2,7 +2,7 @@ CREATE TABLE mangas_categories(
|
||||||
_id INTEGER NOT NULL PRIMARY KEY,
|
_id INTEGER NOT NULL PRIMARY KEY,
|
||||||
manga_id INTEGER NOT NULL,
|
manga_id INTEGER NOT NULL,
|
||||||
category_id INTEGER NOT NULL,
|
category_id INTEGER NOT NULL,
|
||||||
last_modified_at INTEGER AS Long NOT NULL DEFAULT 0,
|
last_modified_at INTEGER NOT NULL DEFAULT 0,
|
||||||
FOREIGN KEY(category_id) REFERENCES categories (_id)
|
FOREIGN KEY(category_id) REFERENCES categories (_id)
|
||||||
ON DELETE CASCADE,
|
ON DELETE CASCADE,
|
||||||
FOREIGN KEY(manga_id) REFERENCES mangas (_id)
|
FOREIGN KEY(manga_id) REFERENCES mangas (_id)
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import kotlin.Float;
|
||||||
|
|
||||||
CREATE TABLE anime_sync(
|
CREATE TABLE anime_sync(
|
||||||
_id INTEGER NOT NULL PRIMARY KEY,
|
_id INTEGER NOT NULL PRIMARY KEY,
|
||||||
anime_id INTEGER NOT NULL,
|
anime_id INTEGER NOT NULL,
|
||||||
|
@ -10,8 +12,8 @@ CREATE TABLE anime_sync(
|
||||||
status INTEGER NOT NULL,
|
status INTEGER NOT NULL,
|
||||||
score REAL AS Float NOT NULL,
|
score REAL AS Float NOT NULL,
|
||||||
remote_url TEXT NOT NULL,
|
remote_url TEXT NOT NULL,
|
||||||
start_date INTEGER AS Long NOT NULL,
|
start_date INTEGER NOT NULL,
|
||||||
finish_date INTEGER AS Long NOT NULL,
|
finish_date INTEGER NOT NULL,
|
||||||
UNIQUE (anime_id, sync_id) ON CONFLICT REPLACE,
|
UNIQUE (anime_id, sync_id) ON CONFLICT REPLACE,
|
||||||
FOREIGN KEY(anime_id) REFERENCES animes (_id)
|
FOREIGN KEY(anime_id) REFERENCES animes (_id)
|
||||||
ON DELETE CASCADE
|
ON DELETE CASCADE
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import eu.kanade.tachiyomi.source.model.UpdateStrategy;
|
import eu.kanade.tachiyomi.source.model.UpdateStrategy;
|
||||||
import java.lang.String;
|
|
||||||
import kotlin.collections.List;
|
import kotlin.collections.List;
|
||||||
|
import kotlin.Boolean;
|
||||||
|
import kotlin.String;
|
||||||
|
|
||||||
CREATE TABLE animes(
|
CREATE TABLE animes(
|
||||||
_id INTEGER NOT NULL PRIMARY KEY,
|
_id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
@ -14,17 +15,17 @@ CREATE TABLE animes(
|
||||||
status INTEGER NOT NULL,
|
status INTEGER NOT NULL,
|
||||||
thumbnail_url TEXT,
|
thumbnail_url TEXT,
|
||||||
favorite INTEGER AS Boolean NOT NULL,
|
favorite INTEGER AS Boolean NOT NULL,
|
||||||
last_update INTEGER AS Long,
|
last_update INTEGER,
|
||||||
next_update INTEGER AS Long,
|
next_update INTEGER,
|
||||||
initialized INTEGER AS Boolean NOT NULL,
|
initialized INTEGER AS Boolean NOT NULL,
|
||||||
viewer INTEGER NOT NULL,
|
viewer INTEGER NOT NULL,
|
||||||
episode_flags INTEGER NOT NULL,
|
episode_flags INTEGER NOT NULL,
|
||||||
cover_last_modified INTEGER AS Long NOT NULL,
|
cover_last_modified INTEGER NOT NULL,
|
||||||
date_added INTEGER AS Long NOT NULL,
|
date_added INTEGER NOT NULL,
|
||||||
update_strategy INTEGER AS UpdateStrategy NOT NULL DEFAULT 0,
|
update_strategy INTEGER AS UpdateStrategy NOT NULL DEFAULT 0,
|
||||||
calculate_interval INTEGER DEFAULT 0 NOT NULL,
|
calculate_interval INTEGER DEFAULT 0 NOT NULL,
|
||||||
last_modified_at INTEGER AS Long NOT NULL DEFAULT 0,
|
last_modified_at INTEGER NOT NULL DEFAULT 0,
|
||||||
favorite_modified_at INTEGER AS Long
|
favorite_modified_at INTEGER
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX animelib_favorite_index ON animes(favorite) WHERE favorite = 1;
|
CREATE INDEX animelib_favorite_index ON animes(favorite) WHERE favorite = 1;
|
||||||
|
@ -92,7 +93,7 @@ SELECT *
|
||||||
FROM animes
|
FROM animes
|
||||||
WHERE favorite = 1
|
WHERE favorite = 1
|
||||||
AND LOWER(title) = :title
|
AND LOWER(title) = :title
|
||||||
LIMIT 1;
|
AND _id != :id;
|
||||||
|
|
||||||
resetViewerFlags:
|
resetViewerFlags:
|
||||||
UPDATE animes
|
UPDATE animes
|
||||||
|
|
|
@ -2,7 +2,7 @@ CREATE TABLE animes_categories(
|
||||||
_id INTEGER NOT NULL PRIMARY KEY,
|
_id INTEGER NOT NULL PRIMARY KEY,
|
||||||
anime_id INTEGER NOT NULL,
|
anime_id INTEGER NOT NULL,
|
||||||
category_id INTEGER NOT NULL,
|
category_id INTEGER NOT NULL,
|
||||||
last_modified_at INTEGER AS Long NOT NULL DEFAULT 0,
|
last_modified_at INTEGER NOT NULL DEFAULT 0,
|
||||||
FOREIGN KEY(category_id) REFERENCES categories (_id)
|
FOREIGN KEY(category_id) REFERENCES categories (_id)
|
||||||
ON DELETE CASCADE,
|
ON DELETE CASCADE,
|
||||||
FOREIGN KEY(anime_id) REFERENCES animes (_id)
|
FOREIGN KEY(anime_id) REFERENCES animes (_id)
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
import kotlin.Boolean;
|
||||||
|
import kotlin.Float;
|
||||||
|
|
||||||
CREATE TABLE episodes(
|
CREATE TABLE episodes(
|
||||||
_id INTEGER NOT NULL PRIMARY KEY,
|
_id INTEGER NOT NULL PRIMARY KEY,
|
||||||
anime_id INTEGER NOT NULL,
|
anime_id INTEGER NOT NULL,
|
||||||
|
@ -10,9 +13,9 @@ CREATE TABLE episodes(
|
||||||
total_seconds INTEGER NOT NULL,
|
total_seconds INTEGER NOT NULL,
|
||||||
episode_number REAL AS Float NOT NULL,
|
episode_number REAL AS Float NOT NULL,
|
||||||
source_order INTEGER NOT NULL,
|
source_order INTEGER NOT NULL,
|
||||||
date_fetch INTEGER AS Long NOT NULL,
|
date_fetch INTEGER NOT NULL,
|
||||||
date_upload INTEGER AS Long NOT NULL,
|
date_upload INTEGER NOT NULL,
|
||||||
last_modified_at INTEGER AS Long NOT NULL DEFAULT 0,
|
last_modified_at INTEGER NOT NULL DEFAULT 0,
|
||||||
FOREIGN KEY(anime_id) REFERENCES animes (_id)
|
FOREIGN KEY(anime_id) REFERENCES animes (_id)
|
||||||
ON DELETE CASCADE
|
ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
@ -54,7 +57,7 @@ WHERE url = :episodeUrl;
|
||||||
getEpisodeByUrlAndAnimeId:
|
getEpisodeByUrlAndAnimeId:
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM episodes
|
FROM episodes
|
||||||
WHERE url = :chapterUrl
|
WHERE url = :episodeUrl
|
||||||
AND anime_id = :animeId;
|
AND anime_id = :animeId;
|
||||||
|
|
||||||
removeEpisodesWithIds:
|
removeEpisodesWithIds:
|
||||||
|
|
|
@ -30,7 +30,7 @@ SELECT
|
||||||
episodes._id AS episodeId,
|
episodes._id AS episodeId,
|
||||||
animes.title,
|
animes.title,
|
||||||
animes.thumbnail_url AS thumbnailUrl,
|
animes.thumbnail_url AS thumbnailUrl,
|
||||||
episodes.chapter_number AS episodeNumber,
|
episodes.episode_number AS episodeNumber,
|
||||||
animehistory.last_seen AS seenAt,
|
animehistory.last_seen AS seenAt,
|
||||||
max_last_seen.last_seen AS maxSeenAt,
|
max_last_seen.last_seen AS maxSeenAt,
|
||||||
max_last_seen.episode_id AS maxSeenAtEpisodeId
|
max_last_seen.episode_id AS maxSeenAtEpisodeId
|
||||||
|
@ -47,4 +47,4 @@ JOIN (
|
||||||
) AS max_last_seen
|
) AS max_last_seen
|
||||||
ON episodes.anime_id = max_last_seen.anime_id;
|
ON episodes.anime_id = max_last_seen.anime_id;
|
||||||
|
|
||||||
CREATE INDEX animehistory_history_chapter_id_index ON animehistory(episode_id);
|
CREATE INDEX animehistory_history_episode_id_index ON animehistory(episode_id);
|
|
@ -9,8 +9,8 @@ UPDATE animes_categories SET last_modified_at = strftime('%s', 'now');
|
||||||
UPDATE episodes SET last_modified_at = strftime('%s', 'now');
|
UPDATE episodes SET last_modified_at = strftime('%s', 'now');
|
||||||
|
|
||||||
-- Create triggers
|
-- Create triggers
|
||||||
DROP TRIGGER IF EXISTS update_last_modified_at_mangas;
|
DROP TRIGGER IF EXISTS update_last_modified_at_animes;
|
||||||
CREATE TRIGGER update_last_modified_at_mangas
|
CREATE TRIGGER update_last_modified_at_animes
|
||||||
AFTER UPDATE ON animes
|
AFTER UPDATE ON animes
|
||||||
FOR EACH ROW
|
FOR EACH ROW
|
||||||
BEGIN
|
BEGIN
|
||||||
|
@ -19,8 +19,8 @@ BEGIN
|
||||||
WHERE _id = new._id;
|
WHERE _id = new._id;
|
||||||
END;
|
END;
|
||||||
|
|
||||||
DROP TRIGGER IF EXISTS update_favorite_modified_at_mangas;
|
DROP TRIGGER IF EXISTS update_favorite_modified_at_animes;
|
||||||
CREATE TRIGGER update_last_favorited_at_mangas
|
CREATE TRIGGER update_last_favorited_at_animes
|
||||||
AFTER UPDATE OF favorite ON animes
|
AFTER UPDATE OF favorite ON animes
|
||||||
BEGIN
|
BEGIN
|
||||||
UPDATE animes
|
UPDATE animes
|
||||||
|
@ -28,8 +28,8 @@ BEGIN
|
||||||
WHERE _id = new._id;
|
WHERE _id = new._id;
|
||||||
END;
|
END;
|
||||||
|
|
||||||
DROP TRIGGER IF EXISTS update_last_modified_at_chapters;
|
DROP TRIGGER IF EXISTS update_last_modified_at_episodes;
|
||||||
CREATE TRIGGER update_last_modified_at_chapters
|
CREATE TRIGGER update_last_modified_at_episodes
|
||||||
AFTER UPDATE ON episodes
|
AFTER UPDATE ON episodes
|
||||||
FOR EACH ROW
|
FOR EACH ROW
|
||||||
BEGIN
|
BEGIN
|
||||||
|
@ -38,8 +38,8 @@ BEGIN
|
||||||
WHERE _id = new._id;
|
WHERE _id = new._id;
|
||||||
END;
|
END;
|
||||||
|
|
||||||
DROP TRIGGER IF EXISTS update_last_modified_at_mangas_categories;
|
DROP TRIGGER IF EXISTS update_last_modified_at_animes_categories;
|
||||||
CREATE TRIGGER update_last_modified_at_mangas_categories
|
CREATE TRIGGER update_last_modified_at_animes_categories
|
||||||
AFTER UPDATE ON animes_categories
|
AFTER UPDATE ON animes_categories
|
||||||
FOR EACH ROW
|
FOR EACH ROW
|
||||||
BEGIN
|
BEGIN
|
||||||
|
|
|
@ -7,7 +7,7 @@ class GetDuplicateLibraryAnime(
|
||||||
private val animeRepository: AnimeRepository,
|
private val animeRepository: AnimeRepository,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun await(title: String): Anime? {
|
suspend fun await(anime: Anime): List<Anime> {
|
||||||
return animeRepository.getDuplicateLibraryAnime(title.lowercase())
|
return animeRepository.getDuplicateLibraryAnime(anime.id, anime.title.lowercase())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,32 +13,32 @@ import kotlin.math.absoluteValue
|
||||||
|
|
||||||
const val MAX_GRACE_PERIOD = 28
|
const val MAX_GRACE_PERIOD = 28
|
||||||
|
|
||||||
class SetAnimeUpdateInterval(
|
class SetAnimeFetchInterval(
|
||||||
private val libraryPreferences: LibraryPreferences = Injekt.get(),
|
private val libraryPreferences: LibraryPreferences = Injekt.get(),
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun updateInterval(
|
fun update(
|
||||||
anime: Anime,
|
anime: Anime,
|
||||||
episodes: List<Episode>,
|
episodes: List<Episode>,
|
||||||
zonedDateTime: ZonedDateTime,
|
zonedDateTime: ZonedDateTime,
|
||||||
fetchRange: Pair<Long, Long>,
|
fetchRange: Pair<Long, Long>,
|
||||||
): AnimeUpdate? {
|
): AnimeUpdate? {
|
||||||
val currentFetchRange = if (fetchRange.first == 0L && fetchRange.second == 0L) {
|
val currentInterval = if (fetchRange.first == 0L && fetchRange.second == 0L) {
|
||||||
getCurrentFetchRange(ZonedDateTime.now())
|
getCurrent(ZonedDateTime.now())
|
||||||
} else {
|
} else {
|
||||||
fetchRange
|
fetchRange
|
||||||
}
|
}
|
||||||
val interval = anime.calculateInterval.takeIf { it < 0 } ?: calculateInterval(episodes, zonedDateTime)
|
val interval = anime.fetchInterval.takeIf { it < 0 } ?: calculateInterval(episodes, zonedDateTime)
|
||||||
val nextUpdate = calculateNextUpdate(anime, interval, zonedDateTime, currentFetchRange)
|
val nextUpdate = calculateNextUpdate(anime, interval, zonedDateTime, currentInterval)
|
||||||
|
|
||||||
return if (anime.nextUpdate == nextUpdate && anime.calculateInterval == interval) {
|
return if (anime.nextUpdate == nextUpdate && anime.fetchInterval == interval) {
|
||||||
null
|
null
|
||||||
} else {
|
} else {
|
||||||
AnimeUpdate(id = anime.id, nextUpdate = nextUpdate, calculateInterval = interval)
|
AnimeUpdate(id = anime.id, nextUpdate = nextUpdate, fetchInterval = interval)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getCurrentFetchRange(timeToCal: ZonedDateTime): Pair<Long, Long> {
|
fun getCurrent(timeToCal: ZonedDateTime): Pair<Long, Long> {
|
||||||
// lead range and the following range depend on if updateOnlyExpectedPeriod set.
|
// lead range and the following range depend on if updateOnlyExpectedPeriod set.
|
||||||
var followRange = 0
|
var followRange = 0
|
||||||
var leadRange = 0
|
var leadRange = 0
|
||||||
|
@ -103,7 +103,7 @@ class SetAnimeUpdateInterval(
|
||||||
): Long {
|
): Long {
|
||||||
return if (
|
return if (
|
||||||
anime.nextUpdate !in fetchRange.first.rangeTo(fetchRange.second + 1) ||
|
anime.nextUpdate !in fetchRange.first.rangeTo(fetchRange.second + 1) ||
|
||||||
anime.calculateInterval == 0
|
anime.fetchInterval == 0
|
||||||
) {
|
) {
|
||||||
val latestDate = ZonedDateTime.ofInstant(Instant.ofEpochMilli(anime.lastUpdate), zonedDateTime.zone).toLocalDate().atStartOfDay()
|
val latestDate = ZonedDateTime.ofInstant(Instant.ofEpochMilli(anime.lastUpdate), zonedDateTime.zone).toLocalDate().atStartOfDay()
|
||||||
val timeSinceLatest = ChronoUnit.DAYS.between(latestDate, zonedDateTime).toInt()
|
val timeSinceLatest = ChronoUnit.DAYS.between(latestDate, zonedDateTime).toInt()
|
|
@ -11,7 +11,7 @@ data class Anime(
|
||||||
val favorite: Boolean,
|
val favorite: Boolean,
|
||||||
val lastUpdate: Long,
|
val lastUpdate: Long,
|
||||||
val nextUpdate: Long,
|
val nextUpdate: Long,
|
||||||
val calculateInterval: Int,
|
val fetchInterval: Int,
|
||||||
val dateAdded: Long,
|
val dateAdded: Long,
|
||||||
val viewerFlags: Long,
|
val viewerFlags: Long,
|
||||||
val episodeFlags: Long,
|
val episodeFlags: Long,
|
||||||
|
@ -118,7 +118,7 @@ data class Anime(
|
||||||
favorite = false,
|
favorite = false,
|
||||||
lastUpdate = 0L,
|
lastUpdate = 0L,
|
||||||
nextUpdate = 0L,
|
nextUpdate = 0L,
|
||||||
calculateInterval = 0,
|
fetchInterval = 0,
|
||||||
dateAdded = 0L,
|
dateAdded = 0L,
|
||||||
viewerFlags = 0L,
|
viewerFlags = 0L,
|
||||||
episodeFlags = 0L,
|
episodeFlags = 0L,
|
||||||
|
|
|
@ -8,7 +8,7 @@ data class AnimeUpdate(
|
||||||
val favorite: Boolean? = null,
|
val favorite: Boolean? = null,
|
||||||
val lastUpdate: Long? = null,
|
val lastUpdate: Long? = null,
|
||||||
val nextUpdate: Long? = null,
|
val nextUpdate: Long? = null,
|
||||||
val calculateInterval: Int? = null,
|
val fetchInterval: Int? = null,
|
||||||
val dateAdded: Long? = null,
|
val dateAdded: Long? = null,
|
||||||
val viewerFlags: Long? = null,
|
val viewerFlags: Long? = null,
|
||||||
val episodeFlags: Long? = null,
|
val episodeFlags: Long? = null,
|
||||||
|
@ -32,7 +32,7 @@ fun Anime.toAnimeUpdate(): AnimeUpdate {
|
||||||
favorite = favorite,
|
favorite = favorite,
|
||||||
lastUpdate = lastUpdate,
|
lastUpdate = lastUpdate,
|
||||||
nextUpdate = nextUpdate,
|
nextUpdate = nextUpdate,
|
||||||
calculateInterval = calculateInterval,
|
fetchInterval = fetchInterval,
|
||||||
dateAdded = dateAdded,
|
dateAdded = dateAdded,
|
||||||
viewerFlags = viewerFlags,
|
viewerFlags = viewerFlags,
|
||||||
episodeFlags = episodeFlags,
|
episodeFlags = episodeFlags,
|
||||||
|
|
|
@ -23,7 +23,7 @@ interface AnimeRepository {
|
||||||
|
|
||||||
fun getAnimeFavoritesBySourceId(sourceId: Long): Flow<List<Anime>>
|
fun getAnimeFavoritesBySourceId(sourceId: Long): Flow<List<Anime>>
|
||||||
|
|
||||||
suspend fun getDuplicateLibraryAnime(title: String): Anime?
|
suspend fun getDuplicateLibraryAnime(id: Long, title: String): List<Anime>
|
||||||
|
|
||||||
suspend fun resetAnimeViewerFlags(): Boolean
|
suspend fun resetAnimeViewerFlags(): Boolean
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ class GetDuplicateLibraryManga(
|
||||||
private val mangaRepository: MangaRepository,
|
private val mangaRepository: MangaRepository,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun await(title: String): Manga? {
|
suspend fun await(manga: Manga): List<Manga> {
|
||||||
return mangaRepository.getDuplicateLibraryManga(title.lowercase())
|
return mangaRepository.getDuplicateLibraryManga(manga.id, manga.title.lowercase())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,32 +13,32 @@ import kotlin.math.absoluteValue
|
||||||
|
|
||||||
const val MAX_GRACE_PERIOD = 28
|
const val MAX_GRACE_PERIOD = 28
|
||||||
|
|
||||||
class SetMangaUpdateInterval(
|
class SetMangaFetchInterval(
|
||||||
private val libraryPreferences: LibraryPreferences = Injekt.get(),
|
private val libraryPreferences: LibraryPreferences = Injekt.get(),
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun updateInterval(
|
fun update(
|
||||||
manga: Manga,
|
manga: Manga,
|
||||||
chapters: List<Chapter>,
|
chapters: List<Chapter>,
|
||||||
zonedDateTime: ZonedDateTime,
|
zonedDateTime: ZonedDateTime,
|
||||||
fetchRange: Pair<Long, Long>,
|
fetchRange: Pair<Long, Long>,
|
||||||
): MangaUpdate? {
|
): MangaUpdate? {
|
||||||
val currentFetchRange = if (fetchRange.first == 0L && fetchRange.second == 0L) {
|
val currentInterval = if (fetchRange.first == 0L && fetchRange.second == 0L) {
|
||||||
getCurrentFetchRange(ZonedDateTime.now())
|
getCurrent(ZonedDateTime.now())
|
||||||
} else {
|
} else {
|
||||||
fetchRange
|
fetchRange
|
||||||
}
|
}
|
||||||
val interval = manga.calculateInterval.takeIf { it < 0 } ?: calculateInterval(chapters, zonedDateTime)
|
val interval = manga.fetchInterval.takeIf { it < 0 } ?: calculateInterval(chapters, zonedDateTime)
|
||||||
val nextUpdate = calculateNextUpdate(manga, interval, zonedDateTime, currentFetchRange)
|
val nextUpdate = calculateNextUpdate(manga, interval, zonedDateTime, currentInterval)
|
||||||
|
|
||||||
return if (manga.nextUpdate == nextUpdate && manga.calculateInterval == interval) {
|
return if (manga.nextUpdate == nextUpdate && manga.fetchInterval == interval) {
|
||||||
null
|
null
|
||||||
} else {
|
} else {
|
||||||
MangaUpdate(id = manga.id, nextUpdate = nextUpdate, calculateInterval = interval)
|
MangaUpdate(id = manga.id, nextUpdate = nextUpdate, fetchInterval = interval)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getCurrentFetchRange(timeToCal: ZonedDateTime): Pair<Long, Long> {
|
fun getCurrent(timeToCal: ZonedDateTime): Pair<Long, Long> {
|
||||||
// lead range and the following range depend on if updateOnlyExpectedPeriod set.
|
// lead range and the following range depend on if updateOnlyExpectedPeriod set.
|
||||||
var followRange = 0
|
var followRange = 0
|
||||||
var leadRange = 0
|
var leadRange = 0
|
||||||
|
@ -103,7 +103,7 @@ class SetMangaUpdateInterval(
|
||||||
): Long {
|
): Long {
|
||||||
return if (
|
return if (
|
||||||
manga.nextUpdate !in fetchRange.first.rangeTo(fetchRange.second + 1) ||
|
manga.nextUpdate !in fetchRange.first.rangeTo(fetchRange.second + 1) ||
|
||||||
manga.calculateInterval == 0
|
manga.fetchInterval == 0
|
||||||
) {
|
) {
|
||||||
val latestDate = ZonedDateTime.ofInstant(Instant.ofEpochMilli(manga.lastUpdate), zonedDateTime.zone).toLocalDate().atStartOfDay()
|
val latestDate = ZonedDateTime.ofInstant(Instant.ofEpochMilli(manga.lastUpdate), zonedDateTime.zone).toLocalDate().atStartOfDay()
|
||||||
val timeSinceLatest = ChronoUnit.DAYS.between(latestDate, zonedDateTime).toInt()
|
val timeSinceLatest = ChronoUnit.DAYS.between(latestDate, zonedDateTime).toInt()
|
|
@ -10,7 +10,7 @@ data class Manga(
|
||||||
val favorite: Boolean,
|
val favorite: Boolean,
|
||||||
val lastUpdate: Long,
|
val lastUpdate: Long,
|
||||||
val nextUpdate: Long,
|
val nextUpdate: Long,
|
||||||
val calculateInterval: Int,
|
val fetchInterval: Int,
|
||||||
val dateAdded: Long,
|
val dateAdded: Long,
|
||||||
val viewerFlags: Long,
|
val viewerFlags: Long,
|
||||||
val chapterFlags: Long,
|
val chapterFlags: Long,
|
||||||
|
@ -99,7 +99,7 @@ data class Manga(
|
||||||
favorite = false,
|
favorite = false,
|
||||||
lastUpdate = 0L,
|
lastUpdate = 0L,
|
||||||
nextUpdate = 0L,
|
nextUpdate = 0L,
|
||||||
calculateInterval = 0,
|
fetchInterval = 0,
|
||||||
dateAdded = 0L,
|
dateAdded = 0L,
|
||||||
viewerFlags = 0L,
|
viewerFlags = 0L,
|
||||||
chapterFlags = 0L,
|
chapterFlags = 0L,
|
||||||
|
|
|
@ -8,7 +8,7 @@ data class MangaUpdate(
|
||||||
val favorite: Boolean? = null,
|
val favorite: Boolean? = null,
|
||||||
val lastUpdate: Long? = null,
|
val lastUpdate: Long? = null,
|
||||||
val nextUpdate: Long? = null,
|
val nextUpdate: Long? = null,
|
||||||
val calculateInterval: Int? = null,
|
val fetchInterval: Int? = null,
|
||||||
val dateAdded: Long? = null,
|
val dateAdded: Long? = null,
|
||||||
val viewerFlags: Long? = null,
|
val viewerFlags: Long? = null,
|
||||||
val chapterFlags: Long? = null,
|
val chapterFlags: Long? = null,
|
||||||
|
@ -32,7 +32,7 @@ fun Manga.toMangaUpdate(): MangaUpdate {
|
||||||
favorite = favorite,
|
favorite = favorite,
|
||||||
lastUpdate = lastUpdate,
|
lastUpdate = lastUpdate,
|
||||||
nextUpdate = nextUpdate,
|
nextUpdate = nextUpdate,
|
||||||
calculateInterval = calculateInterval,
|
fetchInterval = fetchInterval,
|
||||||
dateAdded = dateAdded,
|
dateAdded = dateAdded,
|
||||||
viewerFlags = viewerFlags,
|
viewerFlags = viewerFlags,
|
||||||
chapterFlags = chapterFlags,
|
chapterFlags = chapterFlags,
|
||||||
|
|
|
@ -23,7 +23,7 @@ interface MangaRepository {
|
||||||
|
|
||||||
fun getMangaFavoritesBySourceId(sourceId: Long): Flow<List<Manga>>
|
fun getMangaFavoritesBySourceId(sourceId: Long): Flow<List<Manga>>
|
||||||
|
|
||||||
suspend fun getDuplicateLibraryManga(title: String): Manga?
|
suspend fun getDuplicateLibraryManga(id: Long, title: String): List<Manga>
|
||||||
|
|
||||||
suspend fun resetMangaViewerFlags(): Boolean
|
suspend fun resetMangaViewerFlags(): Boolean
|
||||||
|
|
||||||
|
|
|
@ -10,14 +10,14 @@ import java.time.Duration
|
||||||
import java.time.ZonedDateTime
|
import java.time.ZonedDateTime
|
||||||
|
|
||||||
@Execution(ExecutionMode.CONCURRENT)
|
@Execution(ExecutionMode.CONCURRENT)
|
||||||
class SetAnimeUpdateIntervalTest {
|
class SetAnimeFetchIntervalTest {
|
||||||
private val testTime = ZonedDateTime.parse("2020-01-01T00:00:00Z")
|
private val testTime = ZonedDateTime.parse("2020-01-01T00:00:00Z")
|
||||||
private var episode = Episode.create().copy(
|
private var episode = Episode.create().copy(
|
||||||
dateFetch = testTime.toEpochSecond() * 1000,
|
dateFetch = testTime.toEpochSecond() * 1000,
|
||||||
dateUpload = testTime.toEpochSecond() * 1000,
|
dateUpload = testTime.toEpochSecond() * 1000,
|
||||||
)
|
)
|
||||||
|
|
||||||
private val setAnimeUpdateInterval = SetAnimeUpdateInterval(mockk())
|
private val setAnimeFetchInterval = SetAnimeFetchInterval(mockk())
|
||||||
|
|
||||||
private fun episodeAddTime(episode: Episode, duration: Duration): Episode {
|
private fun episodeAddTime(episode: Episode, duration: Duration): Episode {
|
||||||
val newTime = testTime.plus(duration).toEpochSecond() * 1000
|
val newTime = testTime.plus(duration).toEpochSecond() * 1000
|
||||||
|
@ -33,7 +33,7 @@ class SetAnimeUpdateIntervalTest {
|
||||||
val newEpisode = episodeAddTime(episode, duration)
|
val newEpisode = episodeAddTime(episode, duration)
|
||||||
episodes.add(newEpisode)
|
episodes.add(newEpisode)
|
||||||
}
|
}
|
||||||
setAnimeUpdateInterval.calculateInterval(episodes, testTime) shouldBe 7
|
setAnimeFetchInterval.calculateInterval(episodes, testTime) shouldBe 7
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -44,7 +44,7 @@ class SetAnimeUpdateIntervalTest {
|
||||||
val newEpisode = episodeAddTime(episode, duration)
|
val newEpisode = episodeAddTime(episode, duration)
|
||||||
episodes.add(newEpisode)
|
episodes.add(newEpisode)
|
||||||
}
|
}
|
||||||
setAnimeUpdateInterval.calculateInterval(episodes, testTime) shouldBe 7
|
setAnimeFetchInterval.calculateInterval(episodes, testTime) shouldBe 7
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -60,7 +60,7 @@ class SetAnimeUpdateIntervalTest {
|
||||||
val newEpisode = episodeAddTime(episode, duration)
|
val newEpisode = episodeAddTime(episode, duration)
|
||||||
episodes.add(newEpisode)
|
episodes.add(newEpisode)
|
||||||
}
|
}
|
||||||
setAnimeUpdateInterval.calculateInterval(episodes, testTime) shouldBe 7
|
setAnimeFetchInterval.calculateInterval(episodes, testTime) shouldBe 7
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default 1 if interval less than 1
|
// Default 1 if interval less than 1
|
||||||
|
@ -72,7 +72,7 @@ class SetAnimeUpdateIntervalTest {
|
||||||
val newEpisode = episodeAddTime(episode, duration)
|
val newEpisode = episodeAddTime(episode, duration)
|
||||||
episodes.add(newEpisode)
|
episodes.add(newEpisode)
|
||||||
}
|
}
|
||||||
setAnimeUpdateInterval.calculateInterval(episodes, testTime) shouldBe 1
|
setAnimeFetchInterval.calculateInterval(episodes, testTime) shouldBe 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normal interval calculation
|
// Normal interval calculation
|
||||||
|
@ -84,7 +84,7 @@ class SetAnimeUpdateIntervalTest {
|
||||||
val newEpisode = episodeAddTime(episode, duration)
|
val newEpisode = episodeAddTime(episode, duration)
|
||||||
episodes.add(newEpisode)
|
episodes.add(newEpisode)
|
||||||
}
|
}
|
||||||
setAnimeUpdateInterval.calculateInterval(episodes, testTime) shouldBe 1
|
setAnimeFetchInterval.calculateInterval(episodes, testTime) shouldBe 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -95,7 +95,7 @@ class SetAnimeUpdateIntervalTest {
|
||||||
val newEpisode = episodeAddTime(episode, duration)
|
val newEpisode = episodeAddTime(episode, duration)
|
||||||
episodes.add(newEpisode)
|
episodes.add(newEpisode)
|
||||||
}
|
}
|
||||||
setAnimeUpdateInterval.calculateInterval(episodes, testTime) shouldBe 2
|
setAnimeFetchInterval.calculateInterval(episodes, testTime) shouldBe 2
|
||||||
}
|
}
|
||||||
|
|
||||||
// If interval is decimal, floor to closest integer
|
// If interval is decimal, floor to closest integer
|
||||||
|
@ -107,7 +107,7 @@ class SetAnimeUpdateIntervalTest {
|
||||||
val newEpisode = episodeAddTime(episode, duration)
|
val newEpisode = episodeAddTime(episode, duration)
|
||||||
episodes.add(newEpisode)
|
episodes.add(newEpisode)
|
||||||
}
|
}
|
||||||
setAnimeUpdateInterval.calculateInterval(episodes, testTime) shouldBe 1
|
setAnimeFetchInterval.calculateInterval(episodes, testTime) shouldBe 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -118,7 +118,7 @@ class SetAnimeUpdateIntervalTest {
|
||||||
val newEpisode = episodeAddTime(episode, duration)
|
val newEpisode = episodeAddTime(episode, duration)
|
||||||
episodes.add(newEpisode)
|
episodes.add(newEpisode)
|
||||||
}
|
}
|
||||||
setAnimeUpdateInterval.calculateInterval(episodes, testTime) shouldBe 1
|
setAnimeFetchInterval.calculateInterval(episodes, testTime) shouldBe 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use fetch time if upload time not available
|
// Use fetch time if upload time not available
|
||||||
|
@ -130,6 +130,6 @@ class SetAnimeUpdateIntervalTest {
|
||||||
val newEpisode = episodeAddTime(episode, duration).copy(dateUpload = 0L)
|
val newEpisode = episodeAddTime(episode, duration).copy(dateUpload = 0L)
|
||||||
episodes.add(newEpisode)
|
episodes.add(newEpisode)
|
||||||
}
|
}
|
||||||
setAnimeUpdateInterval.calculateInterval(episodes, testTime) shouldBe 1
|
setAnimeFetchInterval.calculateInterval(episodes, testTime) shouldBe 1
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -10,14 +10,14 @@ import java.time.Duration
|
||||||
import java.time.ZonedDateTime
|
import java.time.ZonedDateTime
|
||||||
|
|
||||||
@Execution(ExecutionMode.CONCURRENT)
|
@Execution(ExecutionMode.CONCURRENT)
|
||||||
class SetMangaUpdateIntervalTest {
|
class SetMangaFetchIntervalTest {
|
||||||
private val testTime = ZonedDateTime.parse("2020-01-01T00:00:00Z")
|
private val testTime = ZonedDateTime.parse("2020-01-01T00:00:00Z")
|
||||||
private var chapter = Chapter.create().copy(
|
private var chapter = Chapter.create().copy(
|
||||||
dateFetch = testTime.toEpochSecond() * 1000,
|
dateFetch = testTime.toEpochSecond() * 1000,
|
||||||
dateUpload = testTime.toEpochSecond() * 1000,
|
dateUpload = testTime.toEpochSecond() * 1000,
|
||||||
)
|
)
|
||||||
|
|
||||||
private val setMangaUpdateInterval = SetMangaUpdateInterval(mockk())
|
private val setMangaFetchInterval = SetMangaFetchInterval(mockk())
|
||||||
|
|
||||||
private fun chapterAddTime(chapter: Chapter, duration: Duration): Chapter {
|
private fun chapterAddTime(chapter: Chapter, duration: Duration): Chapter {
|
||||||
val newTime = testTime.plus(duration).toEpochSecond() * 1000
|
val newTime = testTime.plus(duration).toEpochSecond() * 1000
|
||||||
|
@ -33,7 +33,7 @@ class SetMangaUpdateIntervalTest {
|
||||||
val newChapter = chapterAddTime(chapter, duration)
|
val newChapter = chapterAddTime(chapter, duration)
|
||||||
chapters.add(newChapter)
|
chapters.add(newChapter)
|
||||||
}
|
}
|
||||||
setMangaUpdateInterval.calculateInterval(chapters, testTime) shouldBe 7
|
setMangaFetchInterval.calculateInterval(chapters, testTime) shouldBe 7
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -44,7 +44,7 @@ class SetMangaUpdateIntervalTest {
|
||||||
val newChapter = chapterAddTime(chapter, duration)
|
val newChapter = chapterAddTime(chapter, duration)
|
||||||
chapters.add(newChapter)
|
chapters.add(newChapter)
|
||||||
}
|
}
|
||||||
setMangaUpdateInterval.calculateInterval(chapters, testTime) shouldBe 7
|
setMangaFetchInterval.calculateInterval(chapters, testTime) shouldBe 7
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -60,7 +60,7 @@ class SetMangaUpdateIntervalTest {
|
||||||
val newChapter = chapterAddTime(chapter, duration)
|
val newChapter = chapterAddTime(chapter, duration)
|
||||||
chapters.add(newChapter)
|
chapters.add(newChapter)
|
||||||
}
|
}
|
||||||
setMangaUpdateInterval.calculateInterval(chapters, testTime) shouldBe 7
|
setMangaFetchInterval.calculateInterval(chapters, testTime) shouldBe 7
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default 1 if interval less than 1
|
// Default 1 if interval less than 1
|
||||||
|
@ -72,7 +72,7 @@ class SetMangaUpdateIntervalTest {
|
||||||
val newChapter = chapterAddTime(chapter, duration)
|
val newChapter = chapterAddTime(chapter, duration)
|
||||||
chapters.add(newChapter)
|
chapters.add(newChapter)
|
||||||
}
|
}
|
||||||
setMangaUpdateInterval.calculateInterval(chapters, testTime) shouldBe 1
|
setMangaFetchInterval.calculateInterval(chapters, testTime) shouldBe 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normal interval calculation
|
// Normal interval calculation
|
||||||
|
@ -84,7 +84,7 @@ class SetMangaUpdateIntervalTest {
|
||||||
val newChapter = chapterAddTime(chapter, duration)
|
val newChapter = chapterAddTime(chapter, duration)
|
||||||
chapters.add(newChapter)
|
chapters.add(newChapter)
|
||||||
}
|
}
|
||||||
setMangaUpdateInterval.calculateInterval(chapters, testTime) shouldBe 1
|
setMangaFetchInterval.calculateInterval(chapters, testTime) shouldBe 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -95,7 +95,7 @@ class SetMangaUpdateIntervalTest {
|
||||||
val newChapter = chapterAddTime(chapter, duration)
|
val newChapter = chapterAddTime(chapter, duration)
|
||||||
chapters.add(newChapter)
|
chapters.add(newChapter)
|
||||||
}
|
}
|
||||||
setMangaUpdateInterval.calculateInterval(chapters, testTime) shouldBe 2
|
setMangaFetchInterval.calculateInterval(chapters, testTime) shouldBe 2
|
||||||
}
|
}
|
||||||
|
|
||||||
// If interval is decimal, floor to closest integer
|
// If interval is decimal, floor to closest integer
|
||||||
|
@ -107,7 +107,7 @@ class SetMangaUpdateIntervalTest {
|
||||||
val newChapter = chapterAddTime(chapter, duration)
|
val newChapter = chapterAddTime(chapter, duration)
|
||||||
chapters.add(newChapter)
|
chapters.add(newChapter)
|
||||||
}
|
}
|
||||||
setMangaUpdateInterval.calculateInterval(chapters, testTime) shouldBe 1
|
setMangaFetchInterval.calculateInterval(chapters, testTime) shouldBe 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -118,7 +118,7 @@ class SetMangaUpdateIntervalTest {
|
||||||
val newChapter = chapterAddTime(chapter, duration)
|
val newChapter = chapterAddTime(chapter, duration)
|
||||||
chapters.add(newChapter)
|
chapters.add(newChapter)
|
||||||
}
|
}
|
||||||
setMangaUpdateInterval.calculateInterval(chapters, testTime) shouldBe 1
|
setMangaFetchInterval.calculateInterval(chapters, testTime) shouldBe 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use fetch time if upload time not available
|
// Use fetch time if upload time not available
|
||||||
|
@ -130,6 +130,6 @@ class SetMangaUpdateIntervalTest {
|
||||||
val newChapter = chapterAddTime(chapter, duration).copy(dateUpload = 0L)
|
val newChapter = chapterAddTime(chapter, duration).copy(dateUpload = 0L)
|
||||||
chapters.add(newChapter)
|
chapters.add(newChapter)
|
||||||
}
|
}
|
||||||
setMangaUpdateInterval.calculateInterval(chapters, testTime) shouldBe 1
|
setMangaFetchInterval.calculateInterval(chapters, testTime) shouldBe 1
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,7 +3,7 @@ aboutlib_version = "10.8.3"
|
||||||
okhttp_version = "5.0.0-alpha.11"
|
okhttp_version = "5.0.0-alpha.11"
|
||||||
shizuku_version = "12.2.0"
|
shizuku_version = "12.2.0"
|
||||||
sqlite = "2.3.1"
|
sqlite = "2.3.1"
|
||||||
sqldelight = "1.5.5"
|
sqldelight = "2.0.0"
|
||||||
leakcanary = "2.12"
|
leakcanary = "2.12"
|
||||||
voyager = "1.0.0-rc06"
|
voyager = "1.0.0-rc06"
|
||||||
richtext = "0.17.0"
|
richtext = "0.17.0"
|
||||||
|
@ -76,10 +76,12 @@ shizuku-provider = { module = "dev.rikka.shizuku:provider", version.ref = "shizu
|
||||||
leakcanary-android = { module = "com.squareup.leakcanary:leakcanary-android", version.ref = "leakcanary" }
|
leakcanary-android = { module = "com.squareup.leakcanary:leakcanary-android", version.ref = "leakcanary" }
|
||||||
leakcanary-plumber = { module = "com.squareup.leakcanary:plumber-android", version.ref = "leakcanary" }
|
leakcanary-plumber = { module = "com.squareup.leakcanary:plumber-android", version.ref = "leakcanary" }
|
||||||
|
|
||||||
sqldelight-android-driver = { module = "com.squareup.sqldelight:android-driver", version.ref = "sqldelight" }
|
sqldelight-android-driver = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" }
|
||||||
sqldelight-coroutines = { module = "com.squareup.sqldelight:coroutines-extensions-jvm", version.ref = "sqldelight" }
|
sqldelight-coroutines = { module = "app.cash.sqldelight:coroutines-extensions-jvm", version.ref = "sqldelight" }
|
||||||
sqldelight-android-paging = { module = "com.squareup.sqldelight:android-paging3-extensions", version.ref = "sqldelight" }
|
sqldelight-android-paging = { module = "app.cash.sqldelight:androidx-paging3-extensions", version.ref = "sqldelight" }
|
||||||
sqldelight-gradle = { module = "com.squareup.sqldelight:gradle-plugin", version.ref = "sqldelight" }
|
sqldelight-primitive-adapters = { module = "app.cash.sqldelight:primitive-adapters", version.ref = "sqldelight" }
|
||||||
|
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.0"
|
||||||
kotest-assertions = "io.kotest:kotest-assertions-core:5.6.2"
|
kotest-assertions = "io.kotest:kotest-assertions-core:5.6.2"
|
||||||
|
@ -106,6 +108,7 @@ js-engine = ["quickjs-android"]
|
||||||
sqlite = ["sqlite-framework", "sqlite-ktx", "sqlite-android"]
|
sqlite = ["sqlite-framework", "sqlite-ktx", "sqlite-android"]
|
||||||
coil = ["coil-core", "coil-gif", "coil-compose"]
|
coil = ["coil-core", "coil-gif", "coil-compose"]
|
||||||
shizuku = ["shizuku-api", "shizuku-provider"]
|
shizuku = ["shizuku-api", "shizuku-provider"]
|
||||||
|
sqldelight = ["sqldelight-android-driver", "sqldelight-coroutines", "sqldelight-android-paging", "sqldelight-primitive-adapters"]
|
||||||
voyager = ["voyager-navigator", "voyager-tab-navigator", "voyager-transitions"]
|
voyager = ["voyager-navigator", "voyager-tab-navigator", "voyager-transitions"]
|
||||||
richtext = ["richtext-commonmark", "richtext-m3"]
|
richtext = ["richtext-commonmark", "richtext-m3"]
|
||||||
test = ["junit", "kotest-assertions", "mockk"]
|
test = ["junit", "kotest-assertions", "mockk"]
|
||||||
|
|
|
@ -226,7 +226,6 @@
|
||||||
<string name="pref_show_nsfw_source">Show in sources and extensions lists</string>
|
<string name="pref_show_nsfw_source">Show in sources and extensions lists</string>
|
||||||
<string name="parental_controls_info">This does not prevent unofficial or potentially incorrectly flagged extensions from surfacing NSFW (18+) content within the app.</string>
|
<string name="parental_controls_info">This does not prevent unofficial or potentially incorrectly flagged extensions from surfacing NSFW (18+) content within the app.</string>
|
||||||
|
|
||||||
<string name="recently">Recently</string>
|
|
||||||
<string name="relative_time_today">Today</string>
|
<string name="relative_time_today">Today</string>
|
||||||
<plurals name="relative_time">
|
<plurals name="relative_time">
|
||||||
<item quantity="one">Yesterday</item>
|
<item quantity="one">Yesterday</item>
|
||||||
|
@ -524,6 +523,7 @@
|
||||||
<string name="cache_deleted">Cache cleared. %1$d files have been deleted</string>
|
<string name="cache_deleted">Cache cleared. %1$d files have been deleted</string>
|
||||||
<string name="cache_delete_error">Error occurred while clearing</string>
|
<string name="cache_delete_error">Error occurred while clearing</string>
|
||||||
<string name="pref_invalidate_download_cache">Invalidate downloads index</string>
|
<string name="pref_invalidate_download_cache">Invalidate downloads index</string>
|
||||||
|
<string name="download_cache_invalidated">Downloads index invalidated</string>
|
||||||
<string name="pref_clear_database">Clear database</string>
|
<string name="pref_clear_database">Clear database</string>
|
||||||
<string name="pref_clear_database_summary">Delete history for entries that are not saved in your library</string>
|
<string name="pref_clear_database_summary">Delete history for entries that are not saved in your library</string>
|
||||||
<string name="clear_database_source_item_count">%1$d non-library entries in database</string>
|
<string name="clear_database_source_item_count">%1$d non-library entries in database</string>
|
||||||
|
|
|
@ -5,7 +5,7 @@ plugins {
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
android()
|
androidTarget()
|
||||||
sourceSets {
|
sourceSets {
|
||||||
val commonMain by getting {
|
val commonMain by getting {
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
|
@ -4,7 +4,7 @@ plugins {
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
android()
|
androidTarget()
|
||||||
sourceSets {
|
sourceSets {
|
||||||
val commonMain by getting {
|
val commonMain by getting {
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
Loading…
Reference in a new issue