Last commit merged: 9a817e49be
This commit is contained in:
LuftVerbot 2023-11-11 15:42:22 +01:00
parent 338b54a39c
commit 83dcd5ea31
50 changed files with 360 additions and 220 deletions

View file

@ -2,6 +2,7 @@ package eu.kanade.presentation.browse.anime
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -19,7 +20,6 @@ import eu.kanade.tachiyomi.ui.browse.anime.source.globalsearch.AnimeSearchItemRe
import eu.kanade.tachiyomi.ui.browse.anime.source.globalsearch.GlobalAnimeSearchState import eu.kanade.tachiyomi.ui.browse.anime.source.globalsearch.GlobalAnimeSearchState
import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.LocaleHelper
import tachiyomi.domain.entries.anime.model.Anime import tachiyomi.domain.entries.anime.model.Anime
import tachiyomi.presentation.core.components.LazyColumn
import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.components.material.padding

View file

@ -1,6 +1,7 @@
package eu.kanade.presentation.browse.anime package eu.kanade.presentation.browse.anime
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State import androidx.compose.runtime.State
import eu.kanade.presentation.browse.GlobalSearchEmptyResultItem import eu.kanade.presentation.browse.GlobalSearchEmptyResultItem
@ -14,7 +15,6 @@ import eu.kanade.tachiyomi.ui.browse.anime.migration.search.MigrateAnimeSearchSt
import eu.kanade.tachiyomi.ui.browse.anime.source.globalsearch.AnimeSearchItemResult import eu.kanade.tachiyomi.ui.browse.anime.source.globalsearch.AnimeSearchItemResult
import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.LocaleHelper
import tachiyomi.domain.entries.anime.model.Anime import tachiyomi.domain.entries.anime.model.Anime
import tachiyomi.presentation.core.components.LazyColumn
import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
@Composable @Composable

View file

@ -1,6 +1,7 @@
package eu.kanade.presentation.browse.anime.components package eu.kanade.presentation.browse.anime.components
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@ -14,7 +15,6 @@ import eu.kanade.presentation.library.EntryListItem
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import tachiyomi.domain.entries.anime.model.Anime import tachiyomi.domain.entries.anime.model.Anime
import tachiyomi.domain.entries.anime.model.AnimeCover import tachiyomi.domain.entries.anime.model.AnimeCover
import tachiyomi.presentation.core.components.LazyColumn
import tachiyomi.presentation.core.util.plus import tachiyomi.presentation.core.util.plus
@Composable @Composable

View file

@ -2,6 +2,7 @@ package eu.kanade.presentation.browse.manga
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -19,7 +20,6 @@ import eu.kanade.tachiyomi.ui.browse.manga.source.globalsearch.GlobalMangaSearch
import eu.kanade.tachiyomi.ui.browse.manga.source.globalsearch.MangaSearchItemResult import eu.kanade.tachiyomi.ui.browse.manga.source.globalsearch.MangaSearchItemResult
import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.LocaleHelper
import tachiyomi.domain.entries.manga.model.Manga import tachiyomi.domain.entries.manga.model.Manga
import tachiyomi.presentation.core.components.LazyColumn
import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.components.material.padding

View file

@ -1,6 +1,7 @@
package eu.kanade.presentation.browse.manga package eu.kanade.presentation.browse.manga
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State import androidx.compose.runtime.State
import eu.kanade.presentation.browse.GlobalSearchEmptyResultItem import eu.kanade.presentation.browse.GlobalSearchEmptyResultItem
@ -14,7 +15,6 @@ import eu.kanade.tachiyomi.ui.browse.manga.migration.search.MigrateMangaSearchSt
import eu.kanade.tachiyomi.ui.browse.manga.source.globalsearch.MangaSearchItemResult import eu.kanade.tachiyomi.ui.browse.manga.source.globalsearch.MangaSearchItemResult
import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.LocaleHelper
import tachiyomi.domain.entries.manga.model.Manga import tachiyomi.domain.entries.manga.model.Manga
import tachiyomi.presentation.core.components.LazyColumn
import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
@Composable @Composable

View file

@ -1,6 +1,7 @@
package eu.kanade.presentation.browse.manga.components package eu.kanade.presentation.browse.manga.components
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@ -13,7 +14,6 @@ import eu.kanade.presentation.library.EntryListItem
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import tachiyomi.domain.entries.manga.model.Manga import tachiyomi.domain.entries.manga.model.Manga
import tachiyomi.domain.entries.manga.model.MangaCover import tachiyomi.domain.entries.manga.model.MangaCover
import tachiyomi.presentation.core.components.LazyColumn
import tachiyomi.presentation.core.util.plus import tachiyomi.presentation.core.util.plus
@Composable @Composable

View file

@ -2,13 +2,13 @@ package eu.kanade.presentation.category.components
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import tachiyomi.domain.category.model.Category import tachiyomi.domain.category.model.Category
import tachiyomi.presentation.core.components.LazyColumn
import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.components.material.padding
@Composable @Composable

View file

@ -18,6 +18,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBars import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
@ -81,7 +82,6 @@ import tachiyomi.domain.items.episode.model.Episode
import tachiyomi.domain.items.service.missingItemsCount import tachiyomi.domain.items.service.missingItemsCount
import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.domain.source.anime.model.StubAnimeSource import tachiyomi.domain.source.anime.model.StubAnimeSource
import tachiyomi.presentation.core.components.LazyColumn
import tachiyomi.presentation.core.components.TwoPanelBox import tachiyomi.presentation.core.components.TwoPanelBox
import tachiyomi.presentation.core.components.VerticalFastScroller import tachiyomi.presentation.core.components.VerticalFastScroller
import tachiyomi.presentation.core.components.material.ExtendedFloatingActionButton import tachiyomi.presentation.core.components.material.ExtendedFloatingActionButton

View file

@ -18,6 +18,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBars import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
@ -75,7 +76,6 @@ import tachiyomi.domain.items.chapter.model.Chapter
import tachiyomi.domain.items.service.missingItemsCount import tachiyomi.domain.items.service.missingItemsCount
import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.domain.source.manga.model.StubMangaSource import tachiyomi.domain.source.manga.model.StubMangaSource
import tachiyomi.presentation.core.components.LazyColumn
import tachiyomi.presentation.core.components.TwoPanelBox import tachiyomi.presentation.core.components.TwoPanelBox
import tachiyomi.presentation.core.components.VerticalFastScroller import tachiyomi.presentation.core.components.VerticalFastScroller
import tachiyomi.presentation.core.components.material.ExtendedFloatingActionButton import tachiyomi.presentation.core.components.material.ExtendedFloatingActionButton

View file

@ -4,6 +4,7 @@ import androidx.annotation.StringRes
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
@ -50,7 +51,6 @@ import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget
import eu.kanade.presentation.util.LocalBackPress import eu.kanade.presentation.util.LocalBackPress
import eu.kanade.presentation.util.Screen import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import tachiyomi.presentation.core.components.LazyColumn
import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
import cafe.adriel.voyager.core.screen.Screen as VoyagerScreen import cafe.adriel.voyager.core.screen.Screen as VoyagerScreen

View file

@ -4,6 +4,7 @@ import android.content.Context
import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.ArrowBack
@ -39,7 +40,6 @@ import eu.kanade.tachiyomi.util.system.workManager
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import tachiyomi.presentation.core.components.LazyColumn
import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.util.plus import tachiyomi.presentation.core.util.plus

View file

@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
@ -28,7 +29,6 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import tachiyomi.presentation.core.components.LazyColumn
import tachiyomi.presentation.core.components.material.Divider import tachiyomi.presentation.core.components.material.Divider
import tachiyomi.presentation.core.util.isScrolledToEnd import tachiyomi.presentation.core.util.isScrolledToEnd
import tachiyomi.presentation.core.util.isScrolledToStart import tachiyomi.presentation.core.util.isScrolledToStart

View file

@ -3,6 +3,7 @@ package eu.kanade.presentation.more.stats
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.CollectionsBookmark import androidx.compose.material.icons.outlined.CollectionsBookmark
@ -19,7 +20,6 @@ import eu.kanade.presentation.more.stats.components.StatsSection
import eu.kanade.presentation.more.stats.data.StatsData import eu.kanade.presentation.more.stats.data.StatsData
import eu.kanade.presentation.util.toDurationString import eu.kanade.presentation.util.toDurationString
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import tachiyomi.presentation.core.components.LazyColumn
import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.components.material.padding
import java.util.Locale import java.util.Locale
import kotlin.time.DurationUnit import kotlin.time.DurationUnit

View file

@ -3,6 +3,7 @@ package eu.kanade.presentation.more.stats
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.CollectionsBookmark import androidx.compose.material.icons.outlined.CollectionsBookmark
@ -19,7 +20,6 @@ import eu.kanade.presentation.more.stats.components.StatsSection
import eu.kanade.presentation.more.stats.data.StatsData import eu.kanade.presentation.more.stats.data.StatsData
import eu.kanade.presentation.util.toDurationString import eu.kanade.presentation.util.toDurationString
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import tachiyomi.presentation.core.components.LazyColumn
import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.components.material.padding
import java.util.Locale import java.util.Locale
import kotlin.time.DurationUnit import kotlin.time.DurationUnit

View file

@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@ -19,7 +20,6 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import eu.kanade.presentation.util.isTabletUi import eu.kanade.presentation.util.isTabletUi
import tachiyomi.domain.category.model.Category import tachiyomi.domain.category.model.Category
import tachiyomi.presentation.core.components.LazyColumn
import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.screens.LoadingScreen import tachiyomi.presentation.core.screens.LoadingScreen
import kotlin.random.Random import kotlin.random.Random

View file

@ -37,6 +37,8 @@ data class BackupAnime(
@ProtoNumber(103) var viewer_flags: Int = 0, @ProtoNumber(103) var viewer_flags: Int = 0,
@ProtoNumber(104) var history: List<BackupAnimeHistory> = emptyList(), @ProtoNumber(104) var history: List<BackupAnimeHistory> = emptyList(),
@ProtoNumber(105) var updateStrategy: UpdateStrategy = UpdateStrategy.ALWAYS_UPDATE, @ProtoNumber(105) var updateStrategy: UpdateStrategy = UpdateStrategy.ALWAYS_UPDATE,
@ProtoNumber(106) var lastModifiedAt: Long = 0,
@ProtoNumber(107) var favoriteModifiedAt: Long? = null,
) { ) {
fun getAnimeImpl(): Anime { fun getAnimeImpl(): Anime {
return Anime.create().copy( return Anime.create().copy(
@ -54,6 +56,8 @@ data class BackupAnime(
viewerFlags = this@BackupAnime.viewer_flags.toLong(), viewerFlags = this@BackupAnime.viewer_flags.toLong(),
episodeFlags = this@BackupAnime.episodeFlags.toLong(), episodeFlags = this@BackupAnime.episodeFlags.toLong(),
updateStrategy = this@BackupAnime.updateStrategy, updateStrategy = this@BackupAnime.updateStrategy,
lastModifiedAt = this@BackupAnime.lastModifiedAt,
favoriteModifiedAt = this@BackupAnime.favoriteModifiedAt,
) )
} }
@ -86,6 +90,8 @@ data class BackupAnime(
viewer_flags = anime.skipIntroLength, viewer_flags = anime.skipIntroLength,
episodeFlags = anime.episodeFlags.toInt(), episodeFlags = anime.episodeFlags.toInt(),
updateStrategy = anime.updateStrategy, updateStrategy = anime.updateStrategy,
lastModifiedAt = anime.lastModifiedAt,
favoriteModifiedAt = anime.favoriteModifiedAt,
) )
} }
} }

View file

@ -20,6 +20,7 @@ data class BackupChapter(
// chapterNumber is called number is 1.x // chapterNumber is called number is 1.x
@ProtoNumber(9) var chapterNumber: Float = 0F, @ProtoNumber(9) var chapterNumber: Float = 0F,
@ProtoNumber(10) var sourceOrder: Long = 0, @ProtoNumber(10) var sourceOrder: Long = 0,
@ProtoNumber(11) var lastModifiedAt: Long = 0,
) { ) {
fun toChapterImpl(): Chapter { fun toChapterImpl(): Chapter {
return Chapter.create().copy( return Chapter.create().copy(
@ -33,11 +34,13 @@ data class BackupChapter(
dateFetch = this@BackupChapter.dateFetch, dateFetch = this@BackupChapter.dateFetch,
dateUpload = this@BackupChapter.dateUpload, dateUpload = this@BackupChapter.dateUpload,
sourceOrder = this@BackupChapter.sourceOrder, sourceOrder = this@BackupChapter.sourceOrder,
lastModifiedAt = this@BackupChapter.lastModifiedAt,
) )
} }
} }
val backupChapterMapper = { _: Long, _: Long, url: String, name: String, scanlator: String?, read: Boolean, bookmark: Boolean, lastPageRead: Long, chapterNumber: Float, source_order: Long, dateFetch: Long, dateUpload: Long -> val backupChapterMapper = { _: Long, _: Long, url: String, name: String, scanlator: String?, read: Boolean, bookmark:
Boolean, lastPageRead: Long, chapterNumber: Float, source_order: Long, dateFetch: Long, dateUpload: Long, lastModifiedAt: Long ->
BackupChapter( BackupChapter(
url = url, url = url,
name = name, name = name,
@ -49,5 +52,6 @@ val backupChapterMapper = { _: Long, _: Long, url: String, name: String, scanlat
dateFetch = dateFetch, dateFetch = dateFetch,
dateUpload = dateUpload, dateUpload = dateUpload,
sourceOrder = source_order, sourceOrder = source_order,
lastModifiedAt = lastModifiedAt,
) )
} }

View file

@ -21,6 +21,7 @@ data class BackupEpisode(
// episodeNumber is called number is 1.x // episodeNumber is called number is 1.x
@ProtoNumber(9) var episodeNumber: Float = 0F, @ProtoNumber(9) var episodeNumber: Float = 0F,
@ProtoNumber(10) var sourceOrder: Long = 0, @ProtoNumber(10) var sourceOrder: Long = 0,
@ProtoNumber(11) var lastModifiedAt: Long = 0,
) { ) {
fun toEpisodeImpl(): Episode { fun toEpisodeImpl(): Episode {
return Episode.create().copy( return Episode.create().copy(
@ -35,11 +36,13 @@ data class BackupEpisode(
dateFetch = this@BackupEpisode.dateFetch, dateFetch = this@BackupEpisode.dateFetch,
dateUpload = this@BackupEpisode.dateUpload, dateUpload = this@BackupEpisode.dateUpload,
sourceOrder = this@BackupEpisode.sourceOrder, sourceOrder = this@BackupEpisode.sourceOrder,
lastModifiedAt = this@BackupEpisode.lastModifiedAt,
) )
} }
} }
val backupEpisodeMapper = { _: Long, _: Long, url: String, name: String, scanlator: String?, seen: Boolean, bookmark: Boolean, lastSecondSeen: Long, totalSeconds: Long, episodeNumber: Float, source_order: Long, dateFetch: Long, dateUpload: Long -> val backupEpisodeMapper = { _: Long, _: Long, url: String, name: String, scanlator: String?, seen: Boolean, bookmark:
Boolean, lastSecondSeen: Long, totalSeconds: Long, episodeNumber: Float, source_order: Long, dateFetch: Long, dateUpload: Long, lastModifiedAt: Long ->
BackupEpisode( BackupEpisode(
url = url, url = url,
name = name, name = name,
@ -52,5 +55,6 @@ val backupEpisodeMapper = { _: Long, _: Long, url: String, name: String, scanlat
dateFetch = dateFetch, dateFetch = dateFetch,
dateUpload = dateUpload, dateUpload = dateUpload,
sourceOrder = source_order, sourceOrder = source_order,
lastModifiedAt = lastModifiedAt,
) )
} }

View file

@ -39,6 +39,8 @@ data class BackupManga(
@ProtoNumber(103) var viewer_flags: Int? = null, @ProtoNumber(103) var viewer_flags: Int? = null,
@ProtoNumber(104) var history: List<BackupHistory> = emptyList(), @ProtoNumber(104) var history: List<BackupHistory> = emptyList(),
@ProtoNumber(105) var updateStrategy: UpdateStrategy = UpdateStrategy.ALWAYS_UPDATE, @ProtoNumber(105) var updateStrategy: UpdateStrategy = UpdateStrategy.ALWAYS_UPDATE,
@ProtoNumber(106) var lastModifiedAt: Long = 0,
@ProtoNumber(107) var favoriteModifiedAt: Long? = null,
) { ) {
fun getMangaImpl(): Manga { fun getMangaImpl(): Manga {
return Manga.create().copy( return Manga.create().copy(
@ -56,6 +58,8 @@ data class BackupManga(
viewerFlags = (this@BackupManga.viewer_flags ?: this@BackupManga.viewer).toLong(), viewerFlags = (this@BackupManga.viewer_flags ?: this@BackupManga.viewer).toLong(),
chapterFlags = this@BackupManga.chapterFlags.toLong(), chapterFlags = this@BackupManga.chapterFlags.toLong(),
updateStrategy = this@BackupManga.updateStrategy, updateStrategy = this@BackupManga.updateStrategy,
lastModifiedAt = this@BackupManga.lastModifiedAt,
favoriteModifiedAt = this@BackupManga.favoriteModifiedAt,
) )
} }
@ -89,6 +93,8 @@ data class BackupManga(
viewer_flags = manga.viewerFlags.toInt(), viewer_flags = manga.viewerFlags.toInt(),
chapterFlags = manga.chapterFlags.toInt(), chapterFlags = manga.chapterFlags.toInt(),
updateStrategy = manga.updateStrategy, updateStrategy = manga.updateStrategy,
lastModifiedAt = manga.lastModifiedAt,
favoriteModifiedAt = manga.favoriteModifiedAt,
) )
} }
} }

View file

@ -21,6 +21,8 @@ interface Episode : SEpisode, Serializable {
var date_fetch: Long var date_fetch: Long
var source_order: Int var source_order: Int
var last_modified: Long
} }
fun Episode.toDomainEpisode(): DomainEpisode? { fun Episode.toDomainEpisode(): DomainEpisode? {
@ -39,5 +41,6 @@ fun Episode.toDomainEpisode(): DomainEpisode? {
dateUpload = date_upload, dateUpload = date_upload,
episodeNumber = episode_number, episodeNumber = episode_number,
scanlator = scanlator, scanlator = scanlator,
lastModifiedAt = last_modified,
) )
} }

View file

@ -28,6 +28,8 @@ class EpisodeImpl : Episode {
override var source_order: Int = 0 override var source_order: Int = 0
override var last_modified: Long = 0
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
if (other == null || javaClass != other.javaClass) return false if (other == null || javaClass != other.javaClass) return false

View file

@ -19,6 +19,8 @@ interface Chapter : SChapter, Serializable {
var date_fetch: Long var date_fetch: Long
var source_order: Int var source_order: Int
var last_modified: Long
} }
fun Chapter.toDomainChapter(): DomainChapter? { fun Chapter.toDomainChapter(): DomainChapter? {
@ -36,5 +38,6 @@ fun Chapter.toDomainChapter(): DomainChapter? {
dateUpload = date_upload, dateUpload = date_upload,
chapterNumber = chapter_number, chapterNumber = chapter_number,
scanlator = scanlator, scanlator = scanlator,
lastModifiedAt = last_modified,
) )
} }

View file

@ -26,6 +26,8 @@ class ChapterImpl : Chapter {
override var source_order: Int = 0 override var source_order: Int = 0
override var last_modified: Long = 0
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
if (other == null || javaClass != other.javaClass) return false if (other == null || javaClass != other.javaClass) return false

View file

@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.Divider import androidx.compose.material3.Divider
@ -26,7 +27,6 @@ import tachiyomi.domain.entries.TriStateFilter
import tachiyomi.presentation.core.components.CheckboxItem import tachiyomi.presentation.core.components.CheckboxItem
import tachiyomi.presentation.core.components.CollapsibleBox import tachiyomi.presentation.core.components.CollapsibleBox
import tachiyomi.presentation.core.components.HeadingItem import tachiyomi.presentation.core.components.HeadingItem
import tachiyomi.presentation.core.components.LazyColumn
import tachiyomi.presentation.core.components.SortItem import tachiyomi.presentation.core.components.SortItem
import tachiyomi.presentation.core.components.TextItem import tachiyomi.presentation.core.components.TextItem

View file

@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
@ -24,7 +25,6 @@ import tachiyomi.domain.entries.TriStateFilter
import tachiyomi.presentation.core.components.CheckboxItem import tachiyomi.presentation.core.components.CheckboxItem
import tachiyomi.presentation.core.components.CollapsibleBox import tachiyomi.presentation.core.components.CollapsibleBox
import tachiyomi.presentation.core.components.HeadingItem import tachiyomi.presentation.core.components.HeadingItem
import tachiyomi.presentation.core.components.LazyColumn
import tachiyomi.presentation.core.components.SortItem import tachiyomi.presentation.core.components.SortItem
import tachiyomi.presentation.core.components.TextItem import tachiyomi.presentation.core.components.TextItem
import tachiyomi.presentation.core.components.material.Button import tachiyomi.presentation.core.components.material.Button

View file

@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.sizeIn import androidx.compose.foundation.layout.sizeIn
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
@ -38,7 +39,6 @@ import eu.kanade.tachiyomi.data.database.models.anime.Episode
import eu.kanade.tachiyomi.ui.entries.anime.episodeDecimalFormat import eu.kanade.tachiyomi.ui.entries.anime.episodeDecimalFormat
import eu.kanade.tachiyomi.util.lang.toRelativeString import eu.kanade.tachiyomi.util.lang.toRelativeString
import tachiyomi.domain.entries.anime.model.Anime import tachiyomi.domain.entries.anime.model.Anime
import tachiyomi.presentation.core.components.LazyColumn
import tachiyomi.presentation.core.components.VerticalFastScroller import tachiyomi.presentation.core.components.VerticalFastScroller
import tachiyomi.presentation.core.components.material.ReadItemAlpha import tachiyomi.presentation.core.components.material.ReadItemAlpha
import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.components.material.padding

View file

@ -235,13 +235,18 @@ class ReaderActivity : BaseActivity() {
readingModeToast?.cancel() readingModeToast?.cancel()
} }
override fun onPause() {
viewModel.flushReadTimer()
super.onPause()
}
/** /**
* Set menu visibility again on activity resume to apply immersive mode again if needed. * Set menu visibility again on activity resume to apply immersive mode again if needed.
* Helps with rotations. * Helps with rotations.
*/ */
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
viewModel.setReadStartTime() viewModel.restartReadTimer()
setMenuVisibility(viewModel.state.value.menuVisible, animate = false) setMenuVisibility(viewModel.state.value.menuVisible, animate = false)
} }
@ -591,7 +596,7 @@ class ReaderActivity : BaseActivity() {
* Sets the visibility of the menu according to [visible] and with an optional parameter to * Sets the visibility of the menu according to [visible] and with an optional parameter to
* [animate] the views. * [animate] the views.
*/ */
fun setMenuVisibility(visible: Boolean, animate: Boolean = true) { private fun setMenuVisibility(visible: Boolean, animate: Boolean = true) {
viewModel.showMenus(visible) viewModel.showMenus(visible)
if (visible) { if (visible) {
windowInsetsController.show(WindowInsetsCompat.Type.systemBars()) windowInsetsController.show(WindowInsetsCompat.Type.systemBars())
@ -796,7 +801,6 @@ class ReaderActivity : BaseActivity() {
* Called from the viewer whenever a [page] is marked as active. It updates the values of the * Called from the viewer whenever a [page] is marked as active. It updates the values of the
* bottom menu and delegates the change to the presenter. * bottom menu and delegates the change to the presenter.
*/ */
@SuppressLint("SetTextI18n")
fun onPageSelected(page: ReaderPage) { fun onPageSelected(page: ReaderPage) {
viewModel.onPageSelected(page) viewModel.onPageSelected(page)
} }
@ -814,7 +818,7 @@ class ReaderActivity : BaseActivity() {
* the viewer is reaching the beginning or end of a chapter or the transition page is active. * the viewer is reaching the beginning or end of a chapter or the transition page is active.
*/ */
fun requestPreloadChapter(chapter: ReaderChapter) { fun requestPreloadChapter(chapter: ReaderChapter) {
lifecycleScope.launchIO { viewModel.preloadChapter(chapter) } lifecycleScope.launchIO { viewModel.preload(chapter) }
} }
/** /**
@ -901,7 +905,7 @@ class ReaderActivity : BaseActivity() {
/** /**
* Updates viewer inset depending on fullscreen reader preferences. * Updates viewer inset depending on fullscreen reader preferences.
*/ */
fun updateViewerInset(fullscreen: Boolean) { private fun updateViewerInset(fullscreen: Boolean) {
viewModel.state.value.viewer?.getView()?.applyInsetter { viewModel.state.value.viewer?.getView()?.applyInsetter {
if (!fullscreen) { if (!fullscreen) {
type(navigationBars = true, statusBars = true) { type(navigationBars = true, statusBars = true) {

View file

@ -57,7 +57,6 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import logcat.LogPriority import logcat.LogPriority
import tachiyomi.core.util.lang.launchIO import tachiyomi.core.util.lang.launchIO
@ -314,12 +313,15 @@ class ReaderViewModel(
* Called when the user changed to the given [chapter] when changing pages from the viewer. * Called when the user changed to the given [chapter] when changing pages from the viewer.
* It's used only to set this chapter as active. * It's used only to set this chapter as active.
*/ */
private suspend fun loadNewChapter(chapter: ReaderChapter) { private fun loadNewChapter(chapter: ReaderChapter) {
val loader = loader ?: return val loader = loader ?: return
logcat { "Loading ${chapter.chapter.url}" } viewModelScope.launchIO {
logcat { "Loading ${chapter.chapter.url}" }
flushReadTimer()
restartReadTimer()
withIOContext {
try { try {
loadChapter(loader, chapter) loadChapter(loader, chapter)
} catch (e: Throwable) { } catch (e: Throwable) {
@ -358,7 +360,7 @@ class ReaderViewModel(
* Called when the viewers decide it's a good time to preload a [chapter] and improve the UX so * Called when the viewers decide it's a good time to preload a [chapter] and improve the UX so
* that the user doesn't have to wait too long to continue reading. * that the user doesn't have to wait too long to continue reading.
*/ */
private suspend fun preload(chapter: ReaderChapter) { suspend fun preload(chapter: ReaderChapter) {
if (chapter.state is ReaderChapter.State.Loaded || chapter.state == ReaderChapter.State.Loading) { if (chapter.state is ReaderChapter.State.Loaded || chapter.state == ReaderChapter.State.Loading) {
return return
} }
@ -397,9 +399,7 @@ class ReaderViewModel(
fun onViewerLoaded(viewer: Viewer?) { fun onViewerLoaded(viewer: Viewer?) {
mutableState.update { mutableState.update {
it.copy( it.copy(viewer = viewer)
viewer = viewer,
)
} }
} }
@ -414,31 +414,19 @@ class ReaderViewModel(
return return
} }
val currentChapters = state.value.viewerChapters ?: return
val pages = page.chapter.pages ?: return
val selectedChapter = page.chapter val selectedChapter = page.chapter
val pages = selectedChapter.pages ?: return
// Save last page read and mark as read if needed // Save last page read and mark as read if needed
saveReadingProgress() viewModelScope.launchNonCancellable {
mutableState.update { updateChapterProgress(page.index)
it.copy(
currentPage = page.index + 1,
)
}
if (!incognitoMode) {
selectedChapter.chapter.last_page_read = page.index
if (selectedChapter.pages?.lastIndex == page.index) {
selectedChapter.chapter.read = true
updateTrackChapterRead(selectedChapter)
deleteChapterIfNeeded(selectedChapter)
}
} }
if (selectedChapter != currentChapters.currChapter) { if (selectedChapter != getCurrentChapter()) {
logcat { "Setting ${selectedChapter.chapter.url} as active" } logcat { "Setting ${selectedChapter.chapter.url} as active" }
setReadStartTime() loadNewChapter(selectedChapter)
viewModelScope.launch { loadNewChapter(selectedChapter) }
} }
val inDownloadRange = page.number.toDouble() / pages.size > 0.25 val inDownloadRange = page.number.toDouble() / pages.size > 0.25
if (inDownloadRange) { if (inDownloadRange) {
downloadNextChapters() downloadNextChapters()
@ -508,42 +496,54 @@ class ReaderViewModel(
} }
/** /**
* Called when reader chapter is changed in reader or when activity is paused. * Saves the chapter progress (last read page and whether it's read)
* if incognito mode isn't on.
*/ */
private fun saveReadingProgress() { private suspend fun updateChapterProgress(pageIndex: Int) {
getCurrentChapter()?.let { val readerChapter = getCurrentChapter() ?: return
viewModelScope.launchNonCancellable {
saveChapterProgress(it) mutableState.update {
saveChapterHistory(it) it.copy(currentPage = pageIndex + 1)
}
if (!incognitoMode) {
readerChapter.requestedPage = pageIndex
readerChapter.chapter.last_page_read = pageIndex
if (readerChapter.pages?.lastIndex == pageIndex) {
readerChapter.chapter.read = true
updateTrackChapterRead(readerChapter)
deleteChapterIfNeeded(readerChapter)
} }
updateChapter.await(
ChapterUpdate(
id = readerChapter.chapter.id!!,
read = readerChapter.chapter.read,
bookmark = readerChapter.chapter.bookmark,
lastPageRead = readerChapter.chapter.last_page_read.toLong(),
),
)
}
}
fun restartReadTimer() {
chapterReadStartTime = Date().time
}
fun flushReadTimer() {
viewModelScope.launchNonCancellable {
updateHistory()
} }
} }
/** /**
* Saves this [readerChapter] progress (last read page and whether it's read). * Saves the chapter last read history if incognito mode isn't on.
* if incognito mode isn't on.
*/ */
private suspend fun saveChapterProgress(readerChapter: ReaderChapter) { private suspend fun updateHistory() {
if (!incognitoMode) return
val chapter = readerChapter.chapter
readerChapter.requestedPage = chapter.last_page_read
updateChapter.await(
ChapterUpdate(
id = chapter.id!!,
read = chapter.read,
bookmark = chapter.bookmark,
lastPageRead = chapter.last_page_read.toLong(),
),
)
}
/**
* Saves this [readerChapter] last read history if incognito mode isn't on.
*/
private suspend fun saveChapterHistory(readerChapter: ReaderChapter) {
if (incognitoMode) return if (incognitoMode) return
val readerChapter = getCurrentChapter() ?: return
val chapterId = readerChapter.chapter.id!! val chapterId = readerChapter.chapter.id!!
val readAt = Date() val readAt = Date()
val sessionReadDuration = chapterReadStartTime?.let { readAt.time - it } ?: 0 val sessionReadDuration = chapterReadStartTime?.let { readAt.time - it } ?: 0
@ -552,17 +552,6 @@ class ReaderViewModel(
chapterReadStartTime = null chapterReadStartTime = null
} }
fun setReadStartTime() {
chapterReadStartTime = Date().time
}
/**
* Called from the activity to preload the given [chapter].
*/
suspend fun preloadChapter(chapter: ReaderChapter) {
preload(chapter)
}
/** /**
* Called from the activity to load and set the next chapter as active. * Called from the activity to load and set the next chapter as active.
*/ */

View file

@ -4,8 +4,8 @@ import eu.kanade.tachiyomi.source.model.UpdateStrategy
import tachiyomi.domain.entries.anime.model.Anime import tachiyomi.domain.entries.anime.model.Anime
import tachiyomi.domain.library.anime.LibraryAnime import tachiyomi.domain.library.anime.LibraryAnime
val animeMapper: (Long, Long, String, String?, String?, String?, List<String>?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, UpdateStrategy, Long) -> Anime = val animeMapper: (Long, Long, String, String?, String?, String?, List<String>?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, UpdateStrategy, Long, Long, Long?) -> Anime =
{ id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, episodeFlags, coverLastModified, dateAdded, updateStrategy, calculateInterval -> { id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, episodeFlags, coverLastModified, dateAdded, updateStrategy, calculateInterval, lastModifiedAt, favoriteModifiedAt ->
Anime( Anime(
id = id, id = id,
source = source, source = source,
@ -27,11 +27,13 @@ val animeMapper: (Long, Long, String, String?, String?, String?, List<String>?,
thumbnailUrl = thumbnailUrl, thumbnailUrl = thumbnailUrl,
updateStrategy = updateStrategy, updateStrategy = updateStrategy,
initialized = initialized, initialized = initialized,
lastModifiedAt = lastModifiedAt,
favoriteModifiedAt = favoriteModifiedAt,
) )
} }
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) -> 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, Long, Long, Long, Long, Long, Long) -> LibraryAnime =
{ id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, episodeFlags, coverLastModified, dateAdded, updateStrategy, calculateInterval, 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(
id, id,
@ -54,6 +56,8 @@ val libraryAnime: (Long, Long, String, String?, String?, String?, List<String>?,
dateAdded, dateAdded,
updateStrategy, updateStrategy,
calculateInterval, calculateInterval,
lastModifiedAt,
favoriteModifiedAt,
), ),
category = category, category = category,
totalEpisodes = totalCount, totalEpisodes = totalCount,

View file

@ -4,8 +4,8 @@ import eu.kanade.tachiyomi.source.model.UpdateStrategy
import tachiyomi.domain.entries.manga.model.Manga import tachiyomi.domain.entries.manga.model.Manga
import tachiyomi.domain.library.manga.LibraryManga import tachiyomi.domain.library.manga.LibraryManga
val mangaMapper: (Long, Long, String, String?, String?, String?, List<String>?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, UpdateStrategy, Long) -> Manga = val mangaMapper: (Long, Long, String, String?, String?, String?, List<String>?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, UpdateStrategy, Long, Long, Long?) -> Manga =
{ id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, chapterFlags, coverLastModified, dateAdded, updateStrategy, calculateInterval -> { id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, chapterFlags, coverLastModified, dateAdded, updateStrategy, calculateInterval, lastModifiedAt, favoriteModifiedAt ->
Manga( Manga(
id = id, id = id,
source = source, source = source,
@ -27,11 +27,13 @@ val mangaMapper: (Long, Long, String, String?, String?, String?, List<String>?,
thumbnailUrl = thumbnailUrl, thumbnailUrl = thumbnailUrl,
updateStrategy = updateStrategy, updateStrategy = updateStrategy,
initialized = initialized, initialized = initialized,
lastModifiedAt = lastModifiedAt,
favoriteModifiedAt = favoriteModifiedAt,
) )
} }
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) -> 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, Long, Long, Long, Long, Long, Long) -> LibraryManga =
{ id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, chapterFlags, coverLastModified, dateAdded, updateStrategy, calculateInterval, 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(
id, id,
@ -54,6 +56,8 @@ val libraryManga: (Long, Long, String, String?, String?, String?, List<String>?,
dateAdded, dateAdded,
updateStrategy, updateStrategy,
calculateInterval, calculateInterval,
lastModifiedAt,
favoriteModifiedAt,
), ),
category = category, category = category,
totalChapters = totalCount, totalChapters = totalCount,

View file

@ -2,8 +2,8 @@ package tachiyomi.data.items.chapter
import tachiyomi.domain.items.chapter.model.Chapter import tachiyomi.domain.items.chapter.model.Chapter
val chapterMapper: (Long, Long, String, String, String?, Boolean, Boolean, Long, Float, Long, Long, Long) -> Chapter = val chapterMapper: (Long, Long, String, String, String?, Boolean, Boolean, Long, Float, Long, Long, Long, Long) -> Chapter =
{ id, mangaId, url, name, scanlator, read, bookmark, lastPageRead, chapterNumber, sourceOrder, dateFetch, dateUpload -> { id, mangaId, url, name, scanlator, read, bookmark, lastPageRead, chapterNumber, sourceOrder, dateFetch, dateUpload, lastModifiedAt ->
Chapter( Chapter(
id = id, id = id,
mangaId = mangaId, mangaId = mangaId,
@ -17,5 +17,6 @@ val chapterMapper: (Long, Long, String, String, String?, Boolean, Boolean, Long,
dateUpload = dateUpload, dateUpload = dateUpload,
chapterNumber = chapterNumber, chapterNumber = chapterNumber,
scanlator = scanlator, scanlator = scanlator,
lastModifiedAt = lastModifiedAt,
) )
} }

View file

@ -2,8 +2,8 @@ package tachiyomi.data.items.episode
import tachiyomi.domain.items.episode.model.Episode import tachiyomi.domain.items.episode.model.Episode
val episodeMapper: (Long, Long, String, String, String?, Boolean, Boolean, Long, Long, Float, Long, Long, Long) -> Episode = val episodeMapper: (Long, Long, String, String, String?, Boolean, Boolean, Long, Long, Float, Long, Long, Long, Long) -> Episode =
{ id, animeId, url, name, scanlator, seen, bookmark, lastSecondSeen, totalSeconds, episodeNumber, sourceOrder, dateFetch, dateUpload -> { id, animeId, url, name, scanlator, seen, bookmark, lastSecondSeen, totalSeconds, episodeNumber, sourceOrder, dateFetch, dateUpload, lastModifiedAt ->
Episode( Episode(
id = id, id = id,
animeId = animeId, animeId = animeId,
@ -18,5 +18,6 @@ val episodeMapper: (Long, Long, String, String, String?, Boolean, Boolean, Long,
dateUpload = dateUpload, dateUpload = dateUpload,
episodeNumber = episodeNumber, episodeNumber = episodeNumber,
scanlator = scanlator, scanlator = scanlator,
lastModifiedAt = lastModifiedAt,
) )
} }

View file

@ -11,6 +11,7 @@ CREATE TABLE chapters(
source_order INTEGER NOT NULL, source_order INTEGER NOT NULL,
date_fetch INTEGER AS Long NOT NULL, date_fetch INTEGER AS Long NOT NULL,
date_upload INTEGER AS Long NOT NULL, date_upload INTEGER AS Long NOT NULL,
last_modified_at INTEGER AS Long NOT NULL,
FOREIGN KEY(manga_id) REFERENCES mangas (_id) FOREIGN KEY(manga_id) REFERENCES mangas (_id)
ON DELETE CASCADE ON DELETE CASCADE
); );
@ -18,6 +19,15 @@ CREATE TABLE chapters(
CREATE INDEX chapters_manga_id_index ON chapters(manga_id); CREATE INDEX chapters_manga_id_index ON chapters(manga_id);
CREATE INDEX chapters_unread_by_manga_index ON chapters(manga_id, read) WHERE read = 0; CREATE INDEX chapters_unread_by_manga_index ON chapters(manga_id, read) WHERE read = 0;
CREATE TRIGGER update_last_modified_at_chapters
AFTER UPDATE ON chapters
FOR EACH ROW
BEGIN
UPDATE chapters
SET last_modified_at = strftime('%s', 'now')
WHERE _id = new._id;
END;
getChapterById: getChapterById:
SELECT * SELECT *
FROM chapters FROM chapters
@ -50,8 +60,8 @@ DELETE FROM chapters
WHERE _id IN :chapterIds; WHERE _id IN :chapterIds;
insert: insert:
INSERT INTO chapters(manga_id,url,name,scanlator,read,bookmark,last_page_read,chapter_number,source_order,date_fetch,date_upload) INSERT INTO chapters(manga_id, url, name, scanlator, read, bookmark, last_page_read, chapter_number, source_order,date_fetch, date_upload, last_modified_at)
VALUES (:mangaId,:url,:name,:scanlator,:read,:bookmark,:lastPageRead,:chapterNumber,:sourceOrder,:dateFetch,:dateUpload); VALUES (:mangaId, :url, :name, :scanlator, :read, :bookmark, :lastPageRead, :chapterNumber, :sourceOrder, :dateFetch, :dateUpload, strftime('%s', 'now'));
update: update:
UPDATE chapters UPDATE chapters

View file

@ -22,12 +22,31 @@ CREATE TABLE mangas(
cover_last_modified INTEGER AS Long NOT NULL, cover_last_modified INTEGER AS Long NOT NULL,
date_added INTEGER AS Long NOT NULL, date_added INTEGER AS Long 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,
favorite_modified_at INTEGER AS Long
); );
CREATE INDEX library_favorite_index ON mangas(favorite) WHERE favorite = 1; CREATE INDEX library_favorite_index ON mangas(favorite) WHERE favorite = 1;
CREATE INDEX mangas_url_index ON mangas(url); CREATE INDEX mangas_url_index ON mangas(url);
CREATE TRIGGER update_favorite_modified_at_mangas
AFTER UPDATE OF favorite ON mangas
BEGIN
UPDATE mangas
SET favorite_modified_at = strftime('%s', 'now')
WHERE _id = new._id;
END;
CREATE TRIGGER update_last_modified_at_mangas
AFTER UPDATE ON mangas
FOR EACH ROW
BEGIN
UPDATE mangas
SET last_modified_at = strftime('%s', 'now')
WHERE _id = new._id;
END;
getMangaById: getMangaById:
SELECT * SELECT *
FROM mangas FROM mangas
@ -45,6 +64,15 @@ SELECT *
FROM mangas FROM mangas
WHERE favorite = 1; WHERE favorite = 1;
getAllManga:
SELECT *
FROM mangas;
getMangasWithFavoriteTimestamp:
SELECT *
FROM mangas
WHERE favorite_modified_at IS NOT NULL;
getSourceIdWithFavoriteCount: getSourceIdWithFavoriteCount:
SELECT SELECT
source, source,
@ -81,8 +109,8 @@ DELETE FROM mangas
WHERE favorite = 0 AND source IN :sourceIds; WHERE favorite = 0 AND source IN :sourceIds;
insert: insert:
INSERT INTO mangas(source,url,artist,author,description,genre,title,status,thumbnail_url,favorite,last_update,next_update,initialized,viewer,chapter_flags,cover_last_modified,date_added,update_strategy,calculate_interval) INSERT INTO mangas(source, url, artist, author, description, genre, title, status, thumbnail_url, favorite, last_update, next_update, initialized, viewer, chapter_flags, cover_last_modified, date_added, update_strategy, calculate_interval, last_modified_at)
VALUES (:source,:url,:artist,:author,:description,:genre,:title,:status,:thumbnailUrl,:favorite,:lastUpdate,:nextUpdate,:initialized,:viewerFlags,:chapterFlags,:coverLastModified,:dateAdded,:updateStrategy,:calculateInterval); VALUES (:source, :url, :artist, :author, :description, :genre, :title, :status, :thumbnailUrl, :favorite, :lastUpdate, :nextUpdate, :initialized, :viewerFlags, :chapterFlags, :coverLastModified, :dateAdded, :updateStrategy, :calculateInterval, strftime('%s', 'now'));
update: update:
UPDATE mangas SET UPDATE mangas SET

View file

@ -2,15 +2,25 @@ 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,
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)
ON DELETE CASCADE ON DELETE CASCADE
); );
CREATE TRIGGER update_last_modified_at_mangas_categories
AFTER UPDATE ON mangas_categories
FOR EACH ROW
BEGIN
UPDATE mangas_categories
SET last_modified_at = strftime('%s', 'now')
WHERE _id = new._id;
END;
insert: insert:
INSERT INTO mangas_categories(manga_id, category_id) INSERT INTO mangas_categories(manga_id, category_id, last_modified_at)
VALUES (:mangaId, :categoryId); VALUES (:mangaId, :categoryId, strftime('%s', 'now'));
deleteMangaCategoryByMangaId: deleteMangaCategoryByMangaId:
DELETE FROM mangas_categories DELETE FROM mangas_categories

View file

@ -0,0 +1,49 @@
ALTER TABLE mangas ADD COLUMN last_modified_at INTEGER AS Long NOT NULL DEFAULT 0;
ALTER TABLE mangas ADD COLUMN favorite_modified_at INTEGER AS Long;
ALTER TABLE mangas_categories ADD COLUMN last_modified_at INTEGER AS Long NOT NULL DEFAULT 0;
ALTER TABLE chapters ADD COLUMN last_modified_at INTEGER AS Long NOT NULL DEFAULT 0;
UPDATE mangas SET last_modified_at = strftime('%s', 'now');
UPDATE mangas SET favorite_modified_at = strftime('%s', 'now') WHERE favorite = 1;
UPDATE mangas_categories SET last_modified_at = strftime('%s', 'now');
UPDATE chapters SET last_modified_at = strftime('%s', 'now');
-- Create triggers
DROP TRIGGER IF EXISTS update_last_modified_at_mangas;
CREATE TRIGGER update_last_modified_at_mangas
AFTER UPDATE ON mangas
FOR EACH ROW
BEGIN
UPDATE mangas
SET last_modified_at = strftime('%s', 'now')
WHERE _id = new._id;
END;
DROP TRIGGER IF EXISTS update_favorite_modified_at_mangas;
CREATE TRIGGER update_last_favorited_at_mangas
AFTER UPDATE OF favorite ON mangas
BEGIN
UPDATE mangas
SET favorite_modified_at = strftime('%s', 'now')
WHERE _id = new._id;
END;
DROP TRIGGER IF EXISTS update_last_modified_at_chapters;
CREATE TRIGGER update_last_modified_at_chapters
AFTER UPDATE ON chapters
FOR EACH ROW
BEGIN
UPDATE chapters
SET last_modified_at = strftime('%s', 'now')
WHERE _id = new._id;
END;
DROP TRIGGER IF EXISTS update_last_modified_at_mangas_categories;
CREATE TRIGGER update_last_modified_at_mangas_categories
AFTER UPDATE ON mangas_categories
FOR EACH ROW
BEGIN
UPDATE mangas_categories
SET last_modified_at = strftime('%s', 'now')
WHERE _id = new._id;
END;

View file

@ -22,12 +22,31 @@ CREATE TABLE animes(
cover_last_modified INTEGER AS Long NOT NULL, cover_last_modified INTEGER AS Long NOT NULL,
date_added INTEGER AS Long NOT NULL, date_added INTEGER AS Long 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,
favorite_modified_at INTEGER AS Long
); );
CREATE INDEX animelib_favorite_index ON animes(favorite) WHERE favorite = 1; CREATE INDEX animelib_favorite_index ON animes(favorite) WHERE favorite = 1;
CREATE INDEX animes_url_index ON animes(url); CREATE INDEX animes_url_index ON animes(url);
CREATE TRIGGER update_favorite_modified_at_animes
AFTER UPDATE OF favorite ON animes
BEGIN
UPDATE animes
SET favorite_modified_at = strftime('%s', 'now')
WHERE _id = new._id;
END;
CREATE TRIGGER update_last_modified_at_animes
AFTER UPDATE ON animes
FOR EACH ROW
BEGIN
UPDATE animes
SET last_modified_at = strftime('%s', 'now')
WHERE _id = new._id;
END;
getAnimeById: getAnimeById:
SELECT * SELECT *
FROM animes FROM animes
@ -45,6 +64,15 @@ SELECT *
FROM animes FROM animes
WHERE favorite = 1; WHERE favorite = 1;
getAllAnime:
SELECT *
FROM animes;
getAnimesWithFavoriteTimestamp:
SELECT *
FROM animes
WHERE favorite_modified_at IS NOT NULL;
getAnimeSourceIdWithFavoriteCount: getAnimeSourceIdWithFavoriteCount:
SELECT SELECT
source, source,
@ -81,8 +109,8 @@ DELETE FROM animes
WHERE favorite = 0 AND source IN :sourceIds; WHERE favorite = 0 AND source IN :sourceIds;
insert: insert:
INSERT INTO animes(source,url,artist,author,description,genre,title,status,thumbnail_url,favorite,last_update,next_update,initialized,viewer,episode_flags,cover_last_modified,date_added,update_strategy,calculate_interval) INSERT INTO animes(source, url, artist, author, description, genre, title, status, thumbnail_url, favorite, last_update, next_update, initialized, viewer, episode_flags, cover_last_modified, date_added, update_strategy, calculate_interval, last_modified_at)
VALUES (:source,:url,:artist,:author,:description,:genre,:title,:status,:thumbnailUrl,:favorite,:lastUpdate,:nextUpdate,:initialized,:viewerFlags,:episodeFlags,:coverLastModified,:dateAdded,:updateStrategy,:calculateInterval); VALUES (:source, :url, :artist, :author, :description, :genre, :title, :status, :thumbnailUrl, :favorite, :lastUpdate, :nextUpdate, :initialized, :viewerFlags, :episodeFlags, :coverLastModified, :dateAdded, :updateStrategy, :calculateInterval, strftime('%s', 'now'));
update: update:
UPDATE animes SET UPDATE animes SET

View file

@ -2,15 +2,25 @@ 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,
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)
ON DELETE CASCADE ON DELETE CASCADE
); );
CREATE TRIGGER update_last_modified_at_animes_categories
AFTER UPDATE ON animes_categories
FOR EACH ROW
BEGIN
UPDATE animes_categories
SET last_modified_at = strftime('%s', 'now')
WHERE _id = new._id;
END;
insert: insert:
INSERT INTO animes_categories(anime_id, category_id) INSERT INTO animes_categories(anime_id, category_id, last_modified_at)
VALUES (:animeId, :categoryId); VALUES (:animeId, :categoryId, strftime('%s', 'now'));
deleteAnimeCategoryByAnimeId: deleteAnimeCategoryByAnimeId:
DELETE FROM animes_categories DELETE FROM animes_categories

View file

@ -12,6 +12,7 @@ CREATE TABLE episodes(
source_order INTEGER NOT NULL, source_order INTEGER NOT NULL,
date_fetch INTEGER AS Long NOT NULL, date_fetch INTEGER AS Long NOT NULL,
date_upload INTEGER AS Long NOT NULL, date_upload INTEGER AS Long NOT NULL,
last_modified_at INTEGER AS Long NOT NULL,
FOREIGN KEY(anime_id) REFERENCES animes (_id) FOREIGN KEY(anime_id) REFERENCES animes (_id)
ON DELETE CASCADE ON DELETE CASCADE
); );
@ -19,6 +20,15 @@ CREATE TABLE episodes(
CREATE INDEX episodes_anime_id_index ON episodes(anime_id); CREATE INDEX episodes_anime_id_index ON episodes(anime_id);
CREATE INDEX episodes_unseen_by_anime_index ON episodes(anime_id, seen) WHERE seen = 0; CREATE INDEX episodes_unseen_by_anime_index ON episodes(anime_id, seen) WHERE seen = 0;
CREATE TRIGGER update_last_modified_at_episodes
AFTER UPDATE ON episodes
FOR EACH ROW
BEGIN
UPDATE episodes
SET last_modified_at = strftime('%s', 'now')
WHERE _id = new._id;
END;
getEpisodeById: getEpisodeById:
SELECT * SELECT *
FROM episodes FROM episodes
@ -52,8 +62,8 @@ DELETE FROM episodes
WHERE _id IN :episodeIds; WHERE _id IN :episodeIds;
insert: insert:
INSERT INTO episodes(anime_id,url,name,scanlator,seen,bookmark,last_second_seen,total_seconds,episode_number,source_order,date_fetch,date_upload) INSERT INTO episodes(anime_id, url, name, scanlator, seen, bookmark, last_second_seen, total_seconds, episode_number, source_order, date_fetch, date_upload, last_modified_at)
VALUES (:animeId,:url,:name,:scanlator,:seen,:bookmark,:lastSecondSeen,:totalSeconds,:episodeNumber,:sourceOrder,:dateFetch,:dateUpload); VALUES (:animeId, :url, :name, :scanlator, :seen, :bookmark, :lastSecondSeen, :totalSeconds, :episodeNumber, :sourceOrder, :dateFetch, :dateUpload, strftime('%s', 'now'));
update: update:
UPDATE episodes UPDATE episodes

View file

@ -0,0 +1,49 @@
ALTER TABLE animes ADD COLUMN last_modified_at INTEGER AS Long NOT NULL DEFAULT 0;
ALTER TABLE animes ADD COLUMN favorite_modified_at INTEGER AS Long;
ALTER TABLE animes_categories ADD COLUMN last_modified_at INTEGER AS Long NOT NULL DEFAULT 0;
ALTER TABLE episodes ADD COLUMN last_modified_at INTEGER AS Long NOT NULL DEFAULT 0;
UPDATE animes SET last_modified_at = strftime('%s', 'now');
UPDATE animes SET favorite_modified_at = strftime('%s', 'now') WHERE favorite = 1;
UPDATE animes_categories SET last_modified_at = strftime('%s', 'now');
UPDATE episodes SET last_modified_at = strftime('%s', 'now');
-- Create triggers
DROP TRIGGER IF EXISTS update_last_modified_at_mangas;
CREATE TRIGGER update_last_modified_at_mangas
AFTER UPDATE ON animes
FOR EACH ROW
BEGIN
UPDATE animes
SET last_modified_at = strftime('%s', 'now')
WHERE _id = new._id;
END;
DROP TRIGGER IF EXISTS update_favorite_modified_at_mangas;
CREATE TRIGGER update_last_favorited_at_mangas
AFTER UPDATE OF favorite ON animes
BEGIN
UPDATE animes
SET favorite_modified_at = strftime('%s', 'now')
WHERE _id = new._id;
END;
DROP TRIGGER IF EXISTS update_last_modified_at_chapters;
CREATE TRIGGER update_last_modified_at_chapters
AFTER UPDATE ON episodes
FOR EACH ROW
BEGIN
UPDATE episodes
SET last_modified_at = strftime('%s', 'now')
WHERE _id = new._id;
END;
DROP TRIGGER IF EXISTS update_last_modified_at_mangas_categories;
CREATE TRIGGER update_last_modified_at_mangas_categories
AFTER UPDATE ON animes_categories
FOR EACH ROW
BEGIN
UPDATE animes_categories
SET last_modified_at = strftime('%s', 'now')
WHERE _id = new._id;
END;

View file

@ -26,6 +26,8 @@ data class Anime(
val thumbnailUrl: String?, val thumbnailUrl: String?,
val updateStrategy: UpdateStrategy, val updateStrategy: UpdateStrategy,
val initialized: Boolean, val initialized: Boolean,
val lastModifiedAt: Long,
val favoriteModifiedAt: Long?,
) : Serializable { ) : Serializable {
val sorting: Long val sorting: Long
@ -129,6 +131,8 @@ data class Anime(
thumbnailUrl = null, thumbnailUrl = null,
updateStrategy = UpdateStrategy.ALWAYS_UPDATE, updateStrategy = UpdateStrategy.ALWAYS_UPDATE,
initialized = false, initialized = false,
lastModifiedAt = 0L,
favoriteModifiedAt = null,
) )
} }
} }

View file

@ -25,6 +25,8 @@ data class Manga(
val thumbnailUrl: String?, val thumbnailUrl: String?,
val updateStrategy: UpdateStrategy, val updateStrategy: UpdateStrategy,
val initialized: Boolean, val initialized: Boolean,
val lastModifiedAt: Long,
val favoriteModifiedAt: Long?,
) : Serializable { ) : Serializable {
val sorting: Long val sorting: Long
@ -110,6 +112,8 @@ data class Manga(
thumbnailUrl = null, thumbnailUrl = null,
updateStrategy = UpdateStrategy.ALWAYS_UPDATE, updateStrategy = UpdateStrategy.ALWAYS_UPDATE,
initialized = false, initialized = false,
lastModifiedAt = 0L,
favoriteModifiedAt = null,
) )
} }
} }

View file

@ -13,6 +13,7 @@ data class Chapter(
val dateUpload: Long, val dateUpload: Long,
val chapterNumber: Float, val chapterNumber: Float,
val scanlator: String?, val scanlator: String?,
val lastModifiedAt: Long,
) { ) {
val isRecognizedNumber: Boolean val isRecognizedNumber: Boolean
get() = chapterNumber >= 0f get() = chapterNumber >= 0f
@ -31,6 +32,7 @@ data class Chapter(
dateUpload = -1, dateUpload = -1,
chapterNumber = -1f, chapterNumber = -1f,
scanlator = null, scanlator = null,
lastModifiedAt = 0,
) )
} }
} }

View file

@ -14,6 +14,7 @@ data class Episode(
val dateUpload: Long, val dateUpload: Long,
val episodeNumber: Float, val episodeNumber: Float,
val scanlator: String?, val scanlator: String?,
val lastModifiedAt: Long,
) { ) {
val isRecognizedNumber: Boolean val isRecognizedNumber: Boolean
get() = episodeNumber >= 0f get() = episodeNumber >= 0f
@ -33,6 +34,7 @@ data class Episode(
dateUpload = -1, dateUpload = -1,
episodeNumber = -1f, episodeNumber = -1f,
scanlator = null, scanlator = null,
lastModifiedAt = 0,
) )
} }
} }

View file

@ -1,7 +1,7 @@
[versions] [versions]
compiler = "1.4.8" compiler = "1.4.8"
compose-bom = "2023.06.00-alpha01" compose-bom = "2023.07.00-alpha01"
accompanist = "0.31.4-beta" accompanist = "0.31.5-beta"
[libraries] [libraries]
activity = "androidx.activity:activity-compose:1.7.2" activity = "androidx.activity:activity-compose:1.7.2"

View file

@ -1,5 +1,5 @@
[versions] [versions]
aboutlib_version = "10.8.0" aboutlib_version = "10.8.2"
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"
@ -57,7 +57,7 @@ flexible-adapter-core = "com.github.arkon.FlexibleAdapter:flexible-adapter:c8013
photoview = "com.github.chrisbanes:PhotoView:2.3.0" photoview = "com.github.chrisbanes:PhotoView:2.3.0"
directionalviewpager = "com.github.tachiyomiorg:DirectionalViewPager:1.0.0" directionalviewpager = "com.github.tachiyomiorg:DirectionalViewPager:1.0.0"
insetter = "dev.chrisbanes.insetter:insetter:0.6.1" insetter = "dev.chrisbanes.insetter:insetter:0.6.1"
compose-materialmotion = "io.github.fornewid:material-motion-compose-core:1.0.3" compose-materialmotion = "io.github.fornewid:material-motion-compose-core:1.0.4"
compose-simpleicons = "br.com.devsrsouza.compose.icons.android:simple-icons:1.0.0" compose-simpleicons = "br.com.devsrsouza.compose.icons.android:simple-icons:1.0.0"
swipe = "me.saket.swipe:swipe:1.2.0" swipe = "me.saket.swipe:swipe:1.2.0"

View file

@ -1,6 +1,5 @@
package tachiyomi.presentation.core.components package tachiyomi.presentation.core.components
import androidx.compose.foundation.gestures.FlingBehavior
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridCells
@ -14,7 +13,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import tachiyomi.presentation.core.util.flingBehaviorIgnoringMotionScale
@Composable @Composable
fun FastScrollLazyVerticalGrid( fun FastScrollLazyVerticalGrid(
@ -31,7 +29,6 @@ fun FastScrollLazyVerticalGrid(
verticalArrangement: Arrangement.Vertical = verticalArrangement: Arrangement.Vertical =
if (!reverseLayout) Arrangement.Top else Arrangement.Bottom, if (!reverseLayout) Arrangement.Top else Arrangement.Bottom,
horizontalArrangement: Arrangement.Horizontal = Arrangement.Start, horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,
flingBehavior: FlingBehavior = flingBehaviorIgnoringMotionScale(),
userScrollEnabled: Boolean = true, userScrollEnabled: Boolean = true,
content: LazyGridScope.() -> Unit, content: LazyGridScope.() -> Unit,
) { ) {
@ -54,7 +51,6 @@ fun FastScrollLazyVerticalGrid(
reverseLayout = reverseLayout, reverseLayout = reverseLayout,
verticalArrangement = verticalArrangement, verticalArrangement = verticalArrangement,
horizontalArrangement = horizontalArrangement, horizontalArrangement = horizontalArrangement,
flingBehavior = flingBehavior,
userScrollEnabled = userScrollEnabled, userScrollEnabled = userScrollEnabled,
content = content, content = content,
) )

View file

@ -1,9 +1,9 @@
package tachiyomi.presentation.core.components package tachiyomi.presentation.core.components
import androidx.compose.foundation.gestures.FlingBehavior
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.calculateEndPadding import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
@ -15,38 +15,6 @@ import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import tachiyomi.presentation.core.util.drawVerticalScrollbar import tachiyomi.presentation.core.util.drawVerticalScrollbar
import tachiyomi.presentation.core.util.flingBehaviorIgnoringMotionScale
/**
* LazyColumn with fling animation fix
*
* @see flingBehaviorIgnoringMotionScale
*/
@Composable
fun LazyColumn(
modifier: Modifier = Modifier,
state: LazyListState = rememberLazyListState(),
contentPadding: PaddingValues = PaddingValues(0.dp),
reverseLayout: Boolean = false,
verticalArrangement: Arrangement.Vertical =
if (!reverseLayout) Arrangement.Top else Arrangement.Bottom,
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
flingBehavior: FlingBehavior = flingBehaviorIgnoringMotionScale(),
userScrollEnabled: Boolean = true,
content: LazyListScope.() -> Unit,
) {
androidx.compose.foundation.lazy.LazyColumn(
modifier = modifier,
state = state,
contentPadding = contentPadding,
reverseLayout = reverseLayout,
verticalArrangement = verticalArrangement,
horizontalAlignment = horizontalAlignment,
flingBehavior = flingBehavior,
userScrollEnabled = userScrollEnabled,
content = content,
)
}
/** /**
* LazyColumn with scrollbar. * LazyColumn with scrollbar.
@ -60,7 +28,6 @@ fun ScrollbarLazyColumn(
verticalArrangement: Arrangement.Vertical = verticalArrangement: Arrangement.Vertical =
if (!reverseLayout) Arrangement.Top else Arrangement.Bottom, if (!reverseLayout) Arrangement.Top else Arrangement.Bottom,
horizontalAlignment: Alignment.Horizontal = Alignment.Start, horizontalAlignment: Alignment.Horizontal = Alignment.Start,
flingBehavior: FlingBehavior = flingBehaviorIgnoringMotionScale(),
userScrollEnabled: Boolean = true, userScrollEnabled: Boolean = true,
content: LazyListScope.() -> Unit, content: LazyListScope.() -> Unit,
) { ) {
@ -81,7 +48,6 @@ fun ScrollbarLazyColumn(
reverseLayout = reverseLayout, reverseLayout = reverseLayout,
verticalArrangement = verticalArrangement, verticalArrangement = verticalArrangement,
horizontalAlignment = horizontalAlignment, horizontalAlignment = horizontalAlignment,
flingBehavior = flingBehavior,
userScrollEnabled = userScrollEnabled, userScrollEnabled = userScrollEnabled,
content = content, content = content,
) )
@ -99,7 +65,6 @@ fun FastScrollLazyColumn(
verticalArrangement: Arrangement.Vertical = verticalArrangement: Arrangement.Vertical =
if (!reverseLayout) Arrangement.Top else Arrangement.Bottom, if (!reverseLayout) Arrangement.Top else Arrangement.Bottom,
horizontalAlignment: Alignment.Horizontal = Alignment.Start, horizontalAlignment: Alignment.Horizontal = Alignment.Start,
flingBehavior: FlingBehavior = flingBehaviorIgnoringMotionScale(),
userScrollEnabled: Boolean = true, userScrollEnabled: Boolean = true,
content: LazyListScope.() -> Unit, content: LazyListScope.() -> Unit,
) { ) {
@ -115,7 +80,6 @@ fun FastScrollLazyColumn(
reverseLayout = reverseLayout, reverseLayout = reverseLayout,
verticalArrangement = verticalArrangement, verticalArrangement = verticalArrangement,
horizontalAlignment = horizontalAlignment, horizontalAlignment = horizontalAlignment,
flingBehavior = flingBehavior,
userScrollEnabled = userScrollEnabled, userScrollEnabled = userScrollEnabled,
content = content, content = content,
) )

View file

@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyItemScope import androidx.compose.foundation.lazy.LazyItemScope
import androidx.compose.foundation.lazy.LazyListItemInfo import androidx.compose.foundation.lazy.LazyListItemInfo
import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.LazyListState

View file

@ -1,60 +0,0 @@
package tachiyomi.presentation.core.util
import androidx.compose.animation.core.AnimationState
import androidx.compose.animation.core.DecayAnimationSpec
import androidx.compose.animation.core.animateDecay
import androidx.compose.animation.rememberSplineBasedDecay
import androidx.compose.foundation.gestures.FlingBehavior
import androidx.compose.foundation.gestures.ScrollScope
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.MotionDurationScale
import kotlinx.coroutines.withContext
import kotlin.math.abs
/**
* FlingBehavior that always uses the default motion scale.
*
* This makes the scrolling animation works like View's lists
* when "Remove animation" settings is on.
*/
@Composable
fun flingBehaviorIgnoringMotionScale(): FlingBehavior {
val flingSpec = rememberSplineBasedDecay<Float>()
return remember(flingSpec) {
DefaultFlingBehavior(flingSpec)
}
}
private val DefaultMotionDurationScale = object : MotionDurationScale {
// Use default motion scale factor
override val scaleFactor: Float = 1f
}
private class DefaultFlingBehavior(
private val flingDecay: DecayAnimationSpec<Float>,
) : FlingBehavior {
override suspend fun ScrollScope.performFling(initialVelocity: Float): Float {
// come up with the better threshold, but we need it since spline curve gives us NaNs
return if (abs(initialVelocity) > 1f) {
var velocityLeft = initialVelocity
var lastValue = 0f
withContext(DefaultMotionDurationScale) {
AnimationState(
initialValue = 0f,
initialVelocity = initialVelocity,
).animateDecay(flingDecay) {
val delta = value - lastValue
val consumed = scrollBy(delta)
lastValue = value
velocityLeft = this.velocity
// avoid rounding errors and stop if anything is unconsumed
if (abs(delta - consumed) > 0.5f) this.cancelAnimation()
}
}
velocityLeft
} else {
initialVelocity
}
}
}