mirror of
https://github.com/aniyomiorg/aniyomi.git
synced 2024-11-21 20:27:06 +03:00
parent
338b54a39c
commit
83dcd5ea31
50 changed files with 360 additions and 220 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
||||
viewModelScope.launchIO {
|
||||
logcat { "Loading ${chapter.chapter.url}" }
|
||||
|
||||
withIOContext {
|
||||
flushReadTimer()
|
||||
restartReadTimer()
|
||||
|
||||
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.
|
||||
*/
|
||||
private fun saveReadingProgress() {
|
||||
getCurrentChapter()?.let {
|
||||
viewModelScope.launchNonCancellable {
|
||||
saveChapterProgress(it)
|
||||
saveChapterHistory(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves this [readerChapter] progress (last read page and whether it's read).
|
||||
* Saves the chapter progress (last read page and whether it's read)
|
||||
* if incognito mode isn't on.
|
||||
*/
|
||||
private suspend fun saveChapterProgress(readerChapter: ReaderChapter) {
|
||||
if (!incognitoMode) return
|
||||
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)
|
||||
}
|
||||
|
||||
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(),
|
||||
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] last read history if incognito mode isn't on.
|
||||
* Saves the chapter 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.
|
||||
*/
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
49
data/src/main/sqldelight/migrations/26.sqm
Normal file
49
data/src/main/sqldelight/migrations/26.sqm
Normal 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;
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
49
data/src/main/sqldelightanime/migrations/126.sqm
Normal file
49
data/src/main/sqldelightanime/migrations/126.sqm
Normal 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;
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue