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.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
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.util.system.LocaleHelper
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.padding

View file

@ -1,6 +1,7 @@
package eu.kanade.presentation.browse.anime
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
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.util.system.LocaleHelper
import tachiyomi.domain.entries.anime.model.Anime
import tachiyomi.presentation.core.components.LazyColumn
import tachiyomi.presentation.core.components.material.Scaffold
@Composable

View file

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

View file

@ -2,6 +2,7 @@ package eu.kanade.presentation.browse.manga
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
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.util.system.LocaleHelper
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.padding

View file

@ -1,6 +1,7 @@
package eu.kanade.presentation.browse.manga
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
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.util.system.LocaleHelper
import tachiyomi.domain.entries.manga.model.Manga
import tachiyomi.presentation.core.components.LazyColumn
import tachiyomi.presentation.core.components.material.Scaffold
@Composable

View file

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

View file

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

View file

@ -18,6 +18,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.items
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.library.service.LibraryPreferences
import tachiyomi.domain.source.anime.model.StubAnimeSource
import tachiyomi.presentation.core.components.LazyColumn
import tachiyomi.presentation.core.components.TwoPanelBox
import tachiyomi.presentation.core.components.VerticalFastScroller
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.padding
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.items
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.library.service.LibraryPreferences
import tachiyomi.domain.source.manga.model.StubMangaSource
import tachiyomi.presentation.core.components.LazyColumn
import tachiyomi.presentation.core.components.TwoPanelBox
import tachiyomi.presentation.core.components.VerticalFastScroller
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.isSystemInDarkTheme
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
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.Screen
import eu.kanade.tachiyomi.R
import tachiyomi.presentation.core.components.LazyColumn
import tachiyomi.presentation.core.components.material.Scaffold
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.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.rememberScrollState
import androidx.compose.material.icons.Icons
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.map
import kotlinx.coroutines.flow.stateIn
import tachiyomi.presentation.core.components.LazyColumn
import tachiyomi.presentation.core.components.material.Scaffold
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.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
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.unit.dp
import eu.kanade.tachiyomi.R
import tachiyomi.presentation.core.components.LazyColumn
import tachiyomi.presentation.core.components.material.Divider
import tachiyomi.presentation.core.util.isScrolledToEnd
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.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons
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.util.toDurationString
import eu.kanade.tachiyomi.R
import tachiyomi.presentation.core.components.LazyColumn
import tachiyomi.presentation.core.components.material.padding
import java.util.Locale
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.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons
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.util.toDurationString
import eu.kanade.tachiyomi.R
import tachiyomi.presentation.core.components.LazyColumn
import tachiyomi.presentation.core.components.material.padding
import java.util.Locale
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.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
@ -19,7 +20,6 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import eu.kanade.presentation.util.isTabletUi
import tachiyomi.domain.category.model.Category
import tachiyomi.presentation.core.components.LazyColumn
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.screens.LoadingScreen
import kotlin.random.Random

View file

@ -37,6 +37,8 @@ data class BackupAnime(
@ProtoNumber(103) var viewer_flags: Int = 0,
@ProtoNumber(104) var history: List<BackupAnimeHistory> = emptyList(),
@ProtoNumber(105) var updateStrategy: UpdateStrategy = UpdateStrategy.ALWAYS_UPDATE,
@ProtoNumber(106) var lastModifiedAt: Long = 0,
@ProtoNumber(107) var favoriteModifiedAt: Long? = null,
) {
fun getAnimeImpl(): Anime {
return Anime.create().copy(
@ -54,6 +56,8 @@ data class BackupAnime(
viewerFlags = this@BackupAnime.viewer_flags.toLong(),
episodeFlags = this@BackupAnime.episodeFlags.toLong(),
updateStrategy = this@BackupAnime.updateStrategy,
lastModifiedAt = this@BackupAnime.lastModifiedAt,
favoriteModifiedAt = this@BackupAnime.favoriteModifiedAt,
)
}
@ -86,6 +90,8 @@ data class BackupAnime(
viewer_flags = anime.skipIntroLength,
episodeFlags = anime.episodeFlags.toInt(),
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
@ProtoNumber(9) var chapterNumber: Float = 0F,
@ProtoNumber(10) var sourceOrder: Long = 0,
@ProtoNumber(11) var lastModifiedAt: Long = 0,
) {
fun toChapterImpl(): Chapter {
return Chapter.create().copy(
@ -33,11 +34,13 @@ data class BackupChapter(
dateFetch = this@BackupChapter.dateFetch,
dateUpload = this@BackupChapter.dateUpload,
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(
url = url,
name = name,
@ -49,5 +52,6 @@ val backupChapterMapper = { _: Long, _: Long, url: String, name: String, scanlat
dateFetch = dateFetch,
dateUpload = dateUpload,
sourceOrder = source_order,
lastModifiedAt = lastModifiedAt,
)
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -26,6 +26,8 @@ class ChapterImpl : Chapter {
override var source_order: Int = 0
override var last_modified: Long = 0
override fun equals(other: Any?): Boolean {
if (this === other) return true
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.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Button
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.CollapsibleBox
import tachiyomi.presentation.core.components.HeadingItem
import tachiyomi.presentation.core.components.LazyColumn
import tachiyomi.presentation.core.components.SortItem
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.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.LocalTextStyle
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.CollapsibleBox
import tachiyomi.presentation.core.components.HeadingItem
import tachiyomi.presentation.core.components.LazyColumn
import tachiyomi.presentation.core.components.SortItem
import tachiyomi.presentation.core.components.TextItem
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.sizeIn
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
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.util.lang.toRelativeString
import tachiyomi.domain.entries.anime.model.Anime
import tachiyomi.presentation.core.components.LazyColumn
import tachiyomi.presentation.core.components.VerticalFastScroller
import tachiyomi.presentation.core.components.material.ReadItemAlpha
import tachiyomi.presentation.core.components.material.padding

View file

@ -235,13 +235,18 @@ class ReaderActivity : BaseActivity() {
readingModeToast?.cancel()
}
override fun onPause() {
viewModel.flushReadTimer()
super.onPause()
}
/**
* Set menu visibility again on activity resume to apply immersive mode again if needed.
* Helps with rotations.
*/
override fun onResume() {
super.onResume()
viewModel.setReadStartTime()
viewModel.restartReadTimer()
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
* [animate] the views.
*/
fun setMenuVisibility(visible: Boolean, animate: Boolean = true) {
private fun setMenuVisibility(visible: Boolean, animate: Boolean = true) {
viewModel.showMenus(visible)
if (visible) {
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
* bottom menu and delegates the change to the presenter.
*/
@SuppressLint("SetTextI18n")
fun onPageSelected(page: ReaderPage) {
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.
*/
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.
*/
fun updateViewerInset(fullscreen: Boolean) {
private fun updateViewerInset(fullscreen: Boolean) {
viewModel.state.value.viewer?.getView()?.applyInsetter {
if (!fullscreen) {
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.receiveAsFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import logcat.LogPriority
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.
* 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
logcat { "Loading ${chapter.chapter.url}" }
viewModelScope.launchIO {
logcat { "Loading ${chapter.chapter.url}" }
flushReadTimer()
restartReadTimer()
withIOContext {
try {
loadChapter(loader, chapter)
} 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
* 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) {
return
}
@ -397,9 +399,7 @@ class ReaderViewModel(
fun onViewerLoaded(viewer: Viewer?) {
mutableState.update {
it.copy(
viewer = viewer,
)
it.copy(viewer = viewer)
}
}
@ -414,31 +414,19 @@ class ReaderViewModel(
return
}
val currentChapters = state.value.viewerChapters ?: return
val pages = page.chapter.pages ?: return
val selectedChapter = page.chapter
val pages = selectedChapter.pages ?: return
// Save last page read and mark as read if needed
saveReadingProgress()
mutableState.update {
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)
}
viewModelScope.launchNonCancellable {
updateChapterProgress(page.index)
}
if (selectedChapter != currentChapters.currChapter) {
if (selectedChapter != getCurrentChapter()) {
logcat { "Setting ${selectedChapter.chapter.url} as active" }
setReadStartTime()
viewModelScope.launch { loadNewChapter(selectedChapter) }
loadNewChapter(selectedChapter)
}
val inDownloadRange = page.number.toDouble() / pages.size > 0.25
if (inDownloadRange) {
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() {
getCurrentChapter()?.let {
viewModelScope.launchNonCancellable {
saveChapterProgress(it)
saveChapterHistory(it)
private suspend fun updateChapterProgress(pageIndex: Int) {
val readerChapter = getCurrentChapter() ?: return
mutableState.update {
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).
* if incognito mode isn't on.
* Saves the chapter last read history if incognito mode isn't on.
*/
private suspend fun saveChapterProgress(readerChapter: ReaderChapter) {
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) {
private suspend fun updateHistory() {
if (incognitoMode) return
val readerChapter = getCurrentChapter() ?: return
val chapterId = readerChapter.chapter.id!!
val readAt = Date()
val sessionReadDuration = chapterReadStartTime?.let { readAt.time - it } ?: 0
@ -552,17 +552,6 @@ class ReaderViewModel(
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.
*/

View file

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

View file

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

View file

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

View file

@ -11,6 +11,7 @@ CREATE TABLE chapters(
source_order INTEGER NOT NULL,
date_fetch 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)
ON DELETE CASCADE
);
@ -18,6 +19,15 @@ CREATE TABLE chapters(
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 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:
SELECT *
FROM chapters
@ -50,8 +60,8 @@ DELETE FROM chapters
WHERE _id IN :chapterIds;
insert:
INSERT INTO chapters(manga_id,url,name,scanlator,read,bookmark,last_page_read,chapter_number,source_order,date_fetch,date_upload)
VALUES (:mangaId,:url,:name,:scanlator,:read,:bookmark,:lastPageRead,:chapterNumber,:sourceOrder,:dateFetch,:dateUpload);
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, strftime('%s', 'now'));
update:
UPDATE chapters

View file

@ -22,12 +22,31 @@ CREATE TABLE mangas(
cover_last_modified INTEGER AS Long NOT NULL,
date_added INTEGER AS Long NOT NULL,
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 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:
SELECT *
FROM mangas
@ -45,6 +64,15 @@ SELECT *
FROM mangas
WHERE favorite = 1;
getAllManga:
SELECT *
FROM mangas;
getMangasWithFavoriteTimestamp:
SELECT *
FROM mangas
WHERE favorite_modified_at IS NOT NULL;
getSourceIdWithFavoriteCount:
SELECT
source,
@ -81,8 +109,8 @@ DELETE FROM mangas
WHERE favorite = 0 AND source IN :sourceIds;
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)
VALUES (:source,:url,:artist,:author,:description,:genre,:title,:status,:thumbnailUrl,:favorite,:lastUpdate,:nextUpdate,:initialized,:viewerFlags,:chapterFlags,:coverLastModified,:dateAdded,:updateStrategy,:calculateInterval);
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, strftime('%s', 'now'));
update:
UPDATE mangas SET

View file

@ -2,15 +2,25 @@ CREATE TABLE mangas_categories(
_id INTEGER NOT NULL PRIMARY KEY,
manga_id INTEGER NOT NULL,
category_id INTEGER NOT NULL,
last_modified_at INTEGER AS Long NOT NULL,
FOREIGN KEY(category_id) REFERENCES categories (_id)
ON DELETE CASCADE,
FOREIGN KEY(manga_id) REFERENCES mangas (_id)
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 INTO mangas_categories(manga_id, category_id)
VALUES (:mangaId, :categoryId);
INSERT INTO mangas_categories(manga_id, category_id, last_modified_at)
VALUES (:mangaId, :categoryId, strftime('%s', 'now'));
deleteMangaCategoryByMangaId:
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,
date_added INTEGER AS Long NOT NULL,
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 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:
SELECT *
FROM animes
@ -45,6 +64,15 @@ SELECT *
FROM animes
WHERE favorite = 1;
getAllAnime:
SELECT *
FROM animes;
getAnimesWithFavoriteTimestamp:
SELECT *
FROM animes
WHERE favorite_modified_at IS NOT NULL;
getAnimeSourceIdWithFavoriteCount:
SELECT
source,
@ -81,8 +109,8 @@ DELETE FROM animes
WHERE favorite = 0 AND source IN :sourceIds;
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)
VALUES (:source,:url,:artist,:author,:description,:genre,:title,:status,:thumbnailUrl,:favorite,:lastUpdate,:nextUpdate,:initialized,:viewerFlags,:episodeFlags,:coverLastModified,:dateAdded,:updateStrategy,:calculateInterval);
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, strftime('%s', 'now'));
update:
UPDATE animes SET

View file

@ -2,15 +2,25 @@ CREATE TABLE animes_categories(
_id INTEGER NOT NULL PRIMARY KEY,
anime_id INTEGER NOT NULL,
category_id INTEGER NOT NULL,
last_modified_at INTEGER AS Long NOT NULL,
FOREIGN KEY(category_id) REFERENCES categories (_id)
ON DELETE CASCADE,
FOREIGN KEY(anime_id) REFERENCES animes (_id)
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 INTO animes_categories(anime_id, category_id)
VALUES (:animeId, :categoryId);
INSERT INTO animes_categories(anime_id, category_id, last_modified_at)
VALUES (:animeId, :categoryId, strftime('%s', 'now'));
deleteAnimeCategoryByAnimeId:
DELETE FROM animes_categories

View file

@ -12,6 +12,7 @@ CREATE TABLE episodes(
source_order INTEGER NOT NULL,
date_fetch 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)
ON DELETE CASCADE
);
@ -19,6 +20,15 @@ CREATE TABLE episodes(
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 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:
SELECT *
FROM episodes
@ -52,8 +62,8 @@ DELETE FROM episodes
WHERE _id IN :episodeIds;
insert:
INSERT INTO episodes(anime_id,url,name,scanlator,seen,bookmark,last_second_seen,total_seconds,episode_number,source_order,date_fetch,date_upload)
VALUES (:animeId,:url,:name,:scanlator,:seen,:bookmark,:lastSecondSeen,:totalSeconds,:episodeNumber,:sourceOrder,:dateFetch,:dateUpload);
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, strftime('%s', 'now'));
update:
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 updateStrategy: UpdateStrategy,
val initialized: Boolean,
val lastModifiedAt: Long,
val favoriteModifiedAt: Long?,
) : Serializable {
val sorting: Long
@ -129,6 +131,8 @@ data class Anime(
thumbnailUrl = null,
updateStrategy = UpdateStrategy.ALWAYS_UPDATE,
initialized = false,
lastModifiedAt = 0L,
favoriteModifiedAt = null,
)
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -1,5 +1,5 @@
[versions]
aboutlib_version = "10.8.0"
aboutlib_version = "10.8.2"
okhttp_version = "5.0.0-alpha.11"
shizuku_version = "12.2.0"
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"
directionalviewpager = "com.github.tachiyomiorg:DirectionalViewPager:1.0.0"
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"
swipe = "me.saket.swipe:swipe:1.2.0"

View file

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

View file

@ -1,9 +1,9 @@
package tachiyomi.presentation.core.components
import androidx.compose.foundation.gestures.FlingBehavior
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.LazyListState
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.unit.dp
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.
@ -60,7 +28,6 @@ fun ScrollbarLazyColumn(
verticalArrangement: Arrangement.Vertical =
if (!reverseLayout) Arrangement.Top else Arrangement.Bottom,
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
flingBehavior: FlingBehavior = flingBehaviorIgnoringMotionScale(),
userScrollEnabled: Boolean = true,
content: LazyListScope.() -> Unit,
) {
@ -81,7 +48,6 @@ fun ScrollbarLazyColumn(
reverseLayout = reverseLayout,
verticalArrangement = verticalArrangement,
horizontalAlignment = horizontalAlignment,
flingBehavior = flingBehavior,
userScrollEnabled = userScrollEnabled,
content = content,
)
@ -99,7 +65,6 @@ fun FastScrollLazyColumn(
verticalArrangement: Arrangement.Vertical =
if (!reverseLayout) Arrangement.Top else Arrangement.Bottom,
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
flingBehavior: FlingBehavior = flingBehaviorIgnoringMotionScale(),
userScrollEnabled: Boolean = true,
content: LazyListScope.() -> Unit,
) {
@ -115,7 +80,6 @@ fun FastScrollLazyColumn(
reverseLayout = reverseLayout,
verticalArrangement = verticalArrangement,
horizontalAlignment = horizontalAlignment,
flingBehavior = flingBehavior,
userScrollEnabled = userScrollEnabled,
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.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyItemScope
import androidx.compose.foundation.lazy.LazyListItemInfo
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
}
}
}