mirror of
https://github.com/Suwayomi/TachideskJUI.git
synced 2025-12-10 06:42:05 +01:00
Chapters graphql, reader is broken
This commit is contained in:
@@ -16,12 +16,12 @@ actual class ReaderLauncher(
|
||||
private val context: Context,
|
||||
) {
|
||||
actual fun launch(
|
||||
chapterIndex: Int,
|
||||
chapterId: Long,
|
||||
mangaId: Long,
|
||||
) {
|
||||
Intent(context, Class.forName("ca.gosyer.jui.android.ReaderActivity")).apply {
|
||||
putExtra("manga", mangaId)
|
||||
putExtra("chapter", chapterIndex)
|
||||
putExtra("chapter", chapterId)
|
||||
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}.let(context::startActivity)
|
||||
}
|
||||
|
||||
@@ -12,11 +12,10 @@ import ca.gosyer.jui.domain.category.interactor.GetCategories
|
||||
import ca.gosyer.jui.domain.category.interactor.GetMangaCategories
|
||||
import ca.gosyer.jui.domain.category.interactor.RemoveMangaFromCategory
|
||||
import ca.gosyer.jui.domain.category.model.Category
|
||||
import ca.gosyer.jui.domain.chapter.interactor.BatchUpdateChapter
|
||||
import ca.gosyer.jui.domain.chapter.interactor.DeleteChapterDownload
|
||||
import ca.gosyer.jui.domain.chapter.interactor.GetChapters
|
||||
import ca.gosyer.jui.domain.chapter.interactor.RefreshChapters
|
||||
import ca.gosyer.jui.domain.chapter.interactor.UpdateChapterMarkPreviousRead
|
||||
import ca.gosyer.jui.domain.chapter.interactor.UpdateChapter
|
||||
import ca.gosyer.jui.domain.chapter.model.Chapter
|
||||
import ca.gosyer.jui.domain.download.interactor.BatchChapterDownload
|
||||
import ca.gosyer.jui.domain.download.interactor.QueueChapterDownload
|
||||
@@ -63,8 +62,7 @@ class MangaScreenViewModel
|
||||
private val refreshManga: RefreshManga,
|
||||
private val getChapters: GetChapters,
|
||||
private val refreshChapters: RefreshChapters,
|
||||
private val batchUpdateChapter: BatchUpdateChapter,
|
||||
private val updateChapterMarkPreviousRead: UpdateChapterMarkPreviousRead,
|
||||
private val updateChapter: UpdateChapter,
|
||||
private val queueChapterDownload: QueueChapterDownload,
|
||||
private val stopChapterDownload: StopChapterDownload,
|
||||
private val deleteChapterDownload: DeleteChapterDownload,
|
||||
@@ -253,8 +251,8 @@ class MangaScreenViewModel
|
||||
read: Boolean,
|
||||
) {
|
||||
scope.launch {
|
||||
manga.value?.let { manga ->
|
||||
batchUpdateChapter.await(manga, chapterIds, isRead = read, onError = { toast(it.message.orEmpty()) })
|
||||
manga.value?.let {
|
||||
updateChapter.await(chapterIds, read = read, onError = { toast(it.message.orEmpty()) })
|
||||
_selectedIds.value = persistentListOf()
|
||||
}
|
||||
}
|
||||
@@ -269,8 +267,8 @@ class MangaScreenViewModel
|
||||
bookmark: Boolean,
|
||||
) {
|
||||
scope.launch {
|
||||
manga.value?.let { manga ->
|
||||
batchUpdateChapter.await(manga, chapterIds, isBookmarked = bookmark, onError = { toast(it.message.orEmpty()) })
|
||||
manga.value?.let {
|
||||
updateChapter.await(chapterIds, bookmarked = bookmark, onError = { toast(it.message.orEmpty()) })
|
||||
_selectedIds.value = persistentListOf()
|
||||
}
|
||||
}
|
||||
@@ -282,8 +280,11 @@ class MangaScreenViewModel
|
||||
|
||||
fun markPreviousRead(index: Int) {
|
||||
scope.launch {
|
||||
manga.value?.let { manga ->
|
||||
updateChapterMarkPreviousRead.await(manga, index, onError = { toast(it.message.orEmpty()) })
|
||||
manga.value?.let {
|
||||
val chapters = chapters.value
|
||||
.sortedBy { it.chapter.index }
|
||||
.subList(0, index).map{it.chapter.id} // todo test
|
||||
updateChapter.await(chapters, read = true, onError = { toast(it.message.orEmpty()) })
|
||||
_selectedIds.value = persistentListOf()
|
||||
}
|
||||
}
|
||||
@@ -298,9 +299,8 @@ class MangaScreenViewModel
|
||||
fun deleteDownload(id: Long?) {
|
||||
scope.launch {
|
||||
if (id == null) {
|
||||
val manga = _manga.value ?: return@launch
|
||||
val chapterIds = _selectedIds.value
|
||||
batchUpdateChapter.await(manga, chapterIds, delete = true, onError = { toast(it.message.orEmpty()) })
|
||||
deleteChapterDownload.await(chapterIds, onError = { toast(it.message.orEmpty()) })
|
||||
selectedItems.value.forEach {
|
||||
it.setNotDownloaded()
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ expect fun Modifier.chapterItemModifier(
|
||||
fun ChapterItem(
|
||||
chapterDownload: ChapterDownloadItem,
|
||||
format: (Instant) -> String,
|
||||
onClick: (Int) -> Unit,
|
||||
onClick: (Long) -> Unit,
|
||||
markRead: (Long) -> Unit,
|
||||
markUnread: (Long) -> Unit,
|
||||
bookmarkChapter: (Long) -> Unit,
|
||||
@@ -78,7 +78,7 @@ fun ChapterItem(
|
||||
.height(70.dp)
|
||||
.selectedBackground(isSelected)
|
||||
.chapterItemModifier(
|
||||
onClick = { onClick(chapter.index) },
|
||||
onClick = { onClick(chapter.id) },
|
||||
markRead = { markRead(chapter.id) }.takeUnless { chapter.read },
|
||||
markUnread = { markUnread(chapter.id) }.takeIf { chapter.read },
|
||||
bookmarkChapter = { bookmarkChapter(chapter.id) }.takeUnless { chapter.bookmarked },
|
||||
|
||||
@@ -228,11 +228,9 @@ fun MangaScreenContent(
|
||||
onClick = if (inActionMode) {
|
||||
{
|
||||
if (chapter.isSelected.value) {
|
||||
onUnselectChapter(
|
||||
chapter.chapter.id,
|
||||
)
|
||||
onUnselectChapter(it)
|
||||
} else {
|
||||
onSelectChapter(chapter.chapter.id)
|
||||
onSelectChapter(it)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
package ca.gosyer.jui.ui.reader
|
||||
|
||||
import ca.gosyer.jui.domain.chapter.interactor.GetChapterPages
|
||||
import ca.gosyer.jui.domain.reader.service.ReaderPreferences
|
||||
import ca.gosyer.jui.domain.server.Http
|
||||
import ca.gosyer.jui.ui.base.image.BitmapDecoderFactory
|
||||
@@ -26,6 +27,7 @@ class ChapterLoader(
|
||||
private val http: Http,
|
||||
private val chapterCache: DiskCache,
|
||||
private val bitmapDecoderFactory: BitmapDecoderFactory,
|
||||
private val getChapterPages: GetChapterPages,
|
||||
) {
|
||||
fun loadChapter(chapter: ReaderChapter): StateFlow<PagesState> {
|
||||
if (chapterIsReady(chapter)) {
|
||||
@@ -34,7 +36,7 @@ class ChapterLoader(
|
||||
chapter.state = ReaderChapter.State.Loading
|
||||
log.debug { "Loading pages for ${chapter.chapter.name}" }
|
||||
|
||||
val loader = TachideskPageLoader(chapter, readerPreferences, http, chapterCache, bitmapDecoderFactory)
|
||||
val loader = TachideskPageLoader(chapter, readerPreferences, http, chapterCache, bitmapDecoderFactory, getChapterPages)
|
||||
|
||||
val pages = loader.getPages()
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ import kotlinx.coroutines.launch
|
||||
|
||||
expect class ReaderLauncher {
|
||||
fun launch(
|
||||
chapterIndex: Int,
|
||||
chapterId: Long,
|
||||
mangaId: Long,
|
||||
)
|
||||
|
||||
@@ -114,12 +114,12 @@ expect fun rememberReaderLauncher(): ReaderLauncher
|
||||
|
||||
@Composable
|
||||
fun ReaderMenu(
|
||||
chapterIndex: Int,
|
||||
chapterId: Long,
|
||||
mangaId: Long,
|
||||
onCloseRequest: () -> Unit,
|
||||
) {
|
||||
val viewModels = LocalViewModels.current
|
||||
val vm = remember { viewModels.readerViewModel(ReaderMenuViewModel.Params(chapterIndex, mangaId)) }
|
||||
val vm = remember { viewModels.readerViewModel(ReaderMenuViewModel.Params(chapterId, mangaId)) }
|
||||
DisposableEffect(vm) {
|
||||
onDispose(vm::onDispose)
|
||||
}
|
||||
|
||||
@@ -9,11 +9,10 @@ package ca.gosyer.jui.ui.reader
|
||||
import ca.gosyer.jui.core.lang.launchDefault
|
||||
import ca.gosyer.jui.core.prefs.getAsFlow
|
||||
import ca.gosyer.jui.domain.chapter.interactor.GetChapter
|
||||
import ca.gosyer.jui.domain.chapter.interactor.GetChapterPage
|
||||
import ca.gosyer.jui.domain.chapter.interactor.GetChapterPages
|
||||
import ca.gosyer.jui.domain.chapter.interactor.GetChapters
|
||||
import ca.gosyer.jui.domain.chapter.interactor.UpdateChapterLastPageRead
|
||||
import ca.gosyer.jui.domain.chapter.interactor.UpdateChapter
|
||||
import ca.gosyer.jui.domain.chapter.interactor.UpdateChapterMeta
|
||||
import ca.gosyer.jui.domain.chapter.interactor.UpdateChapterRead
|
||||
import ca.gosyer.jui.domain.chapter.model.Chapter
|
||||
import ca.gosyer.jui.domain.manga.interactor.GetManga
|
||||
import ca.gosyer.jui.domain.manga.interactor.UpdateMangaMeta
|
||||
@@ -77,9 +76,8 @@ class ReaderMenuViewModel
|
||||
private val getManga: GetManga,
|
||||
private val getChapters: GetChapters,
|
||||
private val getChapter: GetChapter,
|
||||
private val getChapterPage: GetChapterPage,
|
||||
private val updateChapterRead: UpdateChapterRead,
|
||||
private val updateChapterLastPageRead: UpdateChapterLastPageRead,
|
||||
private val getChapterPages: GetChapterPages,
|
||||
private val updateChapter: UpdateChapter,
|
||||
private val updateMangaMeta: UpdateMangaMeta,
|
||||
private val updateChapterMeta: UpdateChapterMeta,
|
||||
private val chapterCache: ChapterCache,
|
||||
@@ -157,6 +155,7 @@ class ReaderMenuViewModel
|
||||
http = http,
|
||||
chapterCache = chapterCache,
|
||||
bitmapDecoderFactory = BitmapDecoderFactory(contextWrapper),
|
||||
getChapterPages = getChapterPages,
|
||||
)
|
||||
|
||||
init {
|
||||
@@ -167,7 +166,7 @@ class ReaderMenuViewModel
|
||||
scope.launchDefault {
|
||||
runCatching {
|
||||
initManga(params.mangaId)
|
||||
initChapters(params.mangaId, params.chapterIndex)
|
||||
initChapters(params.mangaId, params.chapterId)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -179,11 +178,13 @@ class ReaderMenuViewModel
|
||||
.collectLatest { page ->
|
||||
page.chapter.pageLoader?.loadPage(page)
|
||||
if (page.chapter == chapter.value) {
|
||||
if ((page.index + 1) >= page.chapter.chapter.pageCount!!) {
|
||||
val pages = page.chapter.pages.value as? PagesState.Success
|
||||
?: return@collectLatest
|
||||
if ((page.index2 + 1) >= pages.pages.size) {
|
||||
markChapterRead(page.chapter)
|
||||
}
|
||||
val nextChapter = nextChapter.value
|
||||
if (nextChapter != null && (page.index + 1) >= (page.chapter.chapter.pageCount!! - 5)) {
|
||||
if (nextChapter != null && (page.index2 + 1) >= ((pages.pages.size - 5).coerceAtLeast(1))) {
|
||||
requestPreloadChapter(nextChapter)
|
||||
}
|
||||
} else {
|
||||
@@ -191,10 +192,10 @@ class ReaderMenuViewModel
|
||||
val nextChapter = nextChapter.value
|
||||
if (page.chapter == previousChapter) {
|
||||
viewerChapters.value = viewerChapters.value.movePrev()
|
||||
initChapters(params.mangaId, page.chapter.chapter.index, fromMenuButton = false)
|
||||
initChapters(params.mangaId, page.chapter.chapter.id, fromMenuButton = false)
|
||||
} else if (page.chapter == nextChapter) {
|
||||
viewerChapters.value = viewerChapters.value.moveNext()
|
||||
initChapters(params.mangaId, page.chapter.chapter.index, fromMenuButton = false)
|
||||
initChapters(params.mangaId, page.chapter.chapter.id, fromMenuButton = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -253,7 +254,7 @@ class ReaderMenuViewModel
|
||||
}
|
||||
|
||||
fun retry(page: ReaderPage) {
|
||||
log.info { "Retrying ${page.index}" }
|
||||
log.info { "Retrying ${page.index2}" }
|
||||
chapter.value?.pageLoader?.retryPage(page)
|
||||
}
|
||||
|
||||
@@ -277,7 +278,7 @@ class ReaderMenuViewModel
|
||||
_state.value = ReaderChapter.State.Wait
|
||||
sendProgress()
|
||||
viewerChapters.value = viewerChapters.value.movePrev()
|
||||
initChapters(params.mangaId, prevChapter.chapter.index, fromMenuButton = true)
|
||||
initChapters(params.mangaId, prevChapter.chapter.id, fromMenuButton = true)
|
||||
} catch (e: Exception) {
|
||||
log.warn(e) { "Error loading prev chapter" }
|
||||
}
|
||||
@@ -291,7 +292,7 @@ class ReaderMenuViewModel
|
||||
_state.value = ReaderChapter.State.Wait
|
||||
sendProgress()
|
||||
viewerChapters.value = viewerChapters.value.moveNext()
|
||||
initChapters(params.mangaId, nextChapter.chapter.index, fromMenuButton = true)
|
||||
initChapters(params.mangaId, nextChapter.chapter.id, fromMenuButton = true)
|
||||
} catch (e: Exception) {
|
||||
log.warn(e) { "Error loading next chapter" }
|
||||
}
|
||||
@@ -313,52 +314,45 @@ class ReaderMenuViewModel
|
||||
|
||||
private suspend fun initChapters(
|
||||
mangaId: Long,
|
||||
chapterIndex: Int,
|
||||
chapterId: Long,
|
||||
fromMenuButton: Boolean = true,
|
||||
) {
|
||||
log.debug { "Loading chapter index $chapterIndex" }
|
||||
log.debug { "Loading chapter index $chapterId" }
|
||||
val (chapter, pages) = coroutineScope {
|
||||
val getCurrentChapter = async {
|
||||
val chapter = getReaderChapter(chapterIndex) ?: return@async null
|
||||
val pages = loader.loadChapter(chapter)
|
||||
viewerChapters.update { it.copy(currChapter = chapter) }
|
||||
chapter to pages
|
||||
val chapters = getChapters.asFlow(mangaId)
|
||||
.take(1)
|
||||
.catch {
|
||||
_state.value = ReaderChapter.State.Error(it)
|
||||
log.warn(it) { "Error getting chapters for $mangaId" }
|
||||
}
|
||||
.singleOrNull()
|
||||
?: return@coroutineScope null
|
||||
val chapter = chapters.find { it.id == chapterId }
|
||||
?.let { ReaderChapter(it) }
|
||||
?: return@coroutineScope null
|
||||
val pages = loader.loadChapter(chapter)
|
||||
viewerChapters.update { it.copy(currChapter = chapter) }
|
||||
|
||||
if (viewerChapters.value.nextChapter == null) {
|
||||
val nextChapter = chapters.find { it.index == chapter.chapter.index + 1 }
|
||||
if (nextChapter != null) {
|
||||
val nextReaderChapter = ReaderChapter(nextChapter)
|
||||
viewerChapters.update { it.copy(nextChapter = nextReaderChapter) }
|
||||
} else {
|
||||
viewerChapters.update { it.copy(nextChapter = null) }
|
||||
}
|
||||
}
|
||||
|
||||
val getAdjacentChapters = async {
|
||||
val chapters = getChapters.await(
|
||||
mangaId,
|
||||
onError = { /* TODO: 2022-07-01 Error toast */ },
|
||||
).orEmpty()
|
||||
|
||||
val nextChapter = async {
|
||||
if (viewerChapters.value.nextChapter == null) {
|
||||
val nextChapter = chapters.find { it.index == chapterIndex + 1 }
|
||||
if (nextChapter != null) {
|
||||
val nextReaderChapter = getReaderChapter(nextChapter.index)
|
||||
viewerChapters.update { it.copy(nextChapter = nextReaderChapter) }
|
||||
} else {
|
||||
viewerChapters.update { it.copy(nextChapter = null) }
|
||||
}
|
||||
}
|
||||
if (viewerChapters.value.prevChapter == null) {
|
||||
val prevChapter = chapters.find { it.index == chapter.chapter.index - 1 }
|
||||
if (prevChapter != null) {
|
||||
val prevReaderChapter = ReaderChapter(prevChapter)
|
||||
viewerChapters.update { it.copy(prevChapter = prevReaderChapter) }
|
||||
} else {
|
||||
viewerChapters.update { it.copy(prevChapter = null) }
|
||||
}
|
||||
val prevChapter = async {
|
||||
if (viewerChapters.value.prevChapter == null) {
|
||||
val prevChapter = chapters.find { it.index == chapterIndex - 1 }
|
||||
if (prevChapter != null) {
|
||||
val prevReaderChapter = getReaderChapter(prevChapter.index)
|
||||
viewerChapters.update { it.copy(prevChapter = prevReaderChapter) }
|
||||
} else {
|
||||
viewerChapters.update { it.copy(prevChapter = null) }
|
||||
}
|
||||
}
|
||||
}
|
||||
nextChapter.await()
|
||||
prevChapter.await()
|
||||
}
|
||||
|
||||
getAdjacentChapters.await()
|
||||
getCurrentChapter.await()
|
||||
chapter to pages
|
||||
} ?: return
|
||||
|
||||
if (fromMenuButton) {
|
||||
@@ -386,18 +380,6 @@ class ReaderMenuViewModel
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getReaderChapter(chapterIndex: Int): ReaderChapter? {
|
||||
return ReaderChapter(
|
||||
getChapter.asFlow(params.mangaId, chapterIndex)
|
||||
.take(1)
|
||||
.catch {
|
||||
_state.value = ReaderChapter.State.Error(it)
|
||||
log.warn(it) { "Error getting chapter $chapterIndex" }
|
||||
}
|
||||
.singleOrNull() ?: return null,
|
||||
)
|
||||
}
|
||||
|
||||
fun requestPreloadChapter(chapter: ReaderChapter) {
|
||||
if (chapter.state != ReaderChapter.State.Wait && chapter.state !is ReaderChapter.State.Error) {
|
||||
return
|
||||
@@ -408,19 +390,19 @@ class ReaderMenuViewModel
|
||||
|
||||
private fun markChapterRead(chapter: ReaderChapter) {
|
||||
scope.launch {
|
||||
updateChapterRead.await(chapter.chapter, read = true, onError = { toast(it.message.orEmpty()) })
|
||||
updateChapter.await(chapter.chapter, read = true, onError = { toast(it.message.orEmpty()) })
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
fun sendProgress(
|
||||
chapter: Chapter? = this.chapter.value?.chapter,
|
||||
lastPageRead: Int = (currentPage.value as? ReaderPage)?.index ?: 0,
|
||||
lastPageRead: Int = (currentPage.value as? ReaderPage)?.index2 ?: 0,
|
||||
) {
|
||||
chapter ?: return
|
||||
if (chapter.read) return
|
||||
GlobalScope.launch {
|
||||
updateChapterLastPageRead.await(
|
||||
updateChapter.await(
|
||||
chapter,
|
||||
lastPageRead = lastPageRead,
|
||||
onError = { toast(it.message.orEmpty()) },
|
||||
@@ -448,7 +430,7 @@ class ReaderMenuViewModel
|
||||
}
|
||||
|
||||
data class Params(
|
||||
val chapterIndex: Int,
|
||||
val chapterId: Long,
|
||||
val mangaId: Long,
|
||||
)
|
||||
|
||||
|
||||
@@ -42,7 +42,9 @@ import androidx.compose.material.icons.rounded.ChevronLeft
|
||||
import androidx.compose.material.icons.rounded.SkipNext
|
||||
import androidx.compose.material.icons.rounded.SkipPrevious
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
@@ -56,6 +58,7 @@ import ca.gosyer.jui.core.util.replace
|
||||
import ca.gosyer.jui.domain.manga.model.MangaMeta
|
||||
import ca.gosyer.jui.domain.reader.model.Direction
|
||||
import ca.gosyer.jui.i18n.MR
|
||||
import ca.gosyer.jui.ui.reader.loader.PagesState
|
||||
import ca.gosyer.jui.ui.reader.model.ReaderChapter
|
||||
import ca.gosyer.jui.ui.reader.model.ReaderItem
|
||||
import ca.gosyer.jui.uicore.components.AroundLayout
|
||||
@@ -82,7 +85,6 @@ fun ReaderSideMenu(
|
||||
) {
|
||||
Surface(Modifier.fillMaxHeight().width(260.dp)) {
|
||||
Column(Modifier.fillMaxSize()) {
|
||||
val pageCount = chapter.chapter.pageCount!!
|
||||
ReaderMenuToolbar(onCloseSideMenuClicked = onCloseSideMenuClicked)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
ReaderModeSetting(
|
||||
@@ -91,6 +93,12 @@ fun ReaderSideMenu(
|
||||
onSetReaderMode = onSetReaderMode,
|
||||
)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
val chapterPages by key(chapter.chapter.id) { chapter.pages.collectAsState() }
|
||||
val pageCount = when (chapterPages) {
|
||||
PagesState.Empty -> null
|
||||
PagesState.Loading -> null
|
||||
is PagesState.Success -> (chapterPages as? PagesState.Success)?.pages?.size
|
||||
}
|
||||
ReaderProgressSlider(
|
||||
pages = pages,
|
||||
currentPage = currentPage,
|
||||
@@ -183,6 +191,12 @@ fun ReaderExpandBottomMenu(
|
||||
shape = CircleShape,
|
||||
backgroundColor = MaterialTheme.colors.surface.copy(alpha = 0.5F),
|
||||
) {
|
||||
val chapterPages by key(chapter.chapter.id) { chapter.pages.collectAsState() }
|
||||
val pageCount = when (chapterPages) {
|
||||
PagesState.Empty -> null
|
||||
PagesState.Loading -> null
|
||||
is PagesState.Success -> (chapterPages as? PagesState.Success)?.pages?.size
|
||||
}
|
||||
AroundLayout(
|
||||
Modifier.padding(horizontal = 8.dp),
|
||||
startLayout = {
|
||||
@@ -190,7 +204,7 @@ fun ReaderExpandBottomMenu(
|
||||
val text = if (!isRtL) {
|
||||
pages.indexOf(currentPage)
|
||||
} else {
|
||||
chapter.chapter.pageCount!!
|
||||
pageCount ?: "1"
|
||||
}.toString()
|
||||
Text(text, fontSize = 15.sp)
|
||||
}
|
||||
@@ -200,7 +214,7 @@ fun ReaderExpandBottomMenu(
|
||||
val text = if (isRtL) {
|
||||
pages.indexOf(currentPage)
|
||||
} else {
|
||||
chapter.chapter.pageCount!!
|
||||
pageCount ?: "1"
|
||||
}.toString()
|
||||
Text(text, fontSize = 15.sp)
|
||||
}
|
||||
@@ -212,7 +226,7 @@ fun ReaderExpandBottomMenu(
|
||||
.padding(horizontal = 4.dp),
|
||||
pages = pages,
|
||||
currentPage = currentPage,
|
||||
pageCount = chapter.chapter.pageCount!!,
|
||||
pageCount = pageCount,
|
||||
onNewPageClicked = navigate,
|
||||
isRtL = isRtL,
|
||||
)
|
||||
@@ -278,7 +292,7 @@ private fun ReaderProgressSlider(
|
||||
modifier: Modifier = Modifier,
|
||||
pages: ImmutableList<ReaderItem>,
|
||||
currentPage: ReaderItem?,
|
||||
pageCount: Int,
|
||||
pageCount: Int?,
|
||||
onNewPageClicked: (Int) -> Unit,
|
||||
isRtL: Boolean,
|
||||
) {
|
||||
@@ -295,9 +309,10 @@ private fun ReaderProgressSlider(
|
||||
onNewPageClicked(it.roundToInt())
|
||||
}
|
||||
},
|
||||
valueRange = 0F..pageCount.toFloat(),
|
||||
steps = pageCount,
|
||||
valueRange = 0F..(pageCount ?: 1).toFloat(),
|
||||
steps = (pageCount ?: 1),
|
||||
onValueChangeFinished = { isValueChanging = false },
|
||||
enabled = pageCount != null,
|
||||
modifier = modifier.let {
|
||||
if (isRtL) {
|
||||
it then Modifier.rotate(180F)
|
||||
|
||||
@@ -8,15 +8,16 @@ package ca.gosyer.jui.ui.reader.loader
|
||||
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import ca.gosyer.jui.core.io.SYSTEM
|
||||
import ca.gosyer.jui.core.io.source
|
||||
import ca.gosyer.jui.core.lang.PriorityChannel
|
||||
import ca.gosyer.jui.core.lang.throwIfCancellation
|
||||
import ca.gosyer.jui.domain.chapter.interactor.GetChapterPages
|
||||
import ca.gosyer.jui.domain.reader.service.ReaderPreferences
|
||||
import ca.gosyer.jui.domain.server.Http
|
||||
import ca.gosyer.jui.ui.base.image.BitmapDecoderFactory
|
||||
import ca.gosyer.jui.ui.base.model.StableHolder
|
||||
import ca.gosyer.jui.ui.reader.model.ReaderChapter
|
||||
import ca.gosyer.jui.ui.reader.model.ReaderPage
|
||||
import ca.gosyer.jui.ui.util.lang.toSource
|
||||
import cafe.adriel.voyager.core.concurrent.AtomicInt32
|
||||
import com.seiko.imageloader.asImageBitmap
|
||||
import com.seiko.imageloader.cache.disk.DiskCache
|
||||
@@ -25,9 +26,6 @@ import com.seiko.imageloader.model.DataSource
|
||||
import com.seiko.imageloader.model.ImageResult
|
||||
import com.seiko.imageloader.option.Options
|
||||
import io.ktor.client.plugins.onDownload
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.statement.HttpResponse
|
||||
import io.ktor.client.statement.bodyAsChannel
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.IO
|
||||
@@ -39,6 +37,7 @@ import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.emitAll
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
@@ -57,6 +56,7 @@ class TachideskPageLoader(
|
||||
private val http: Http,
|
||||
private val chapterCache: DiskCache,
|
||||
private val bitmapDecoderFactory: BitmapDecoderFactory,
|
||||
private val getChapterPages: GetChapterPages,
|
||||
) : PageLoader() {
|
||||
val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
|
||||
|
||||
@@ -109,15 +109,15 @@ class TachideskPageLoader(
|
||||
}
|
||||
|
||||
private suspend fun fetchImage(page: ReaderPage) {
|
||||
log.debug { "Loading page ${page.index}" }
|
||||
log.debug { "Loading page ${page.index2}" }
|
||||
flow {
|
||||
val response = http.get("api/v1/manga/${chapter.chapter.mangaId}/chapter/${chapter.chapter.index}/page/${page.index}") {
|
||||
val response = getChapterPages.asFlow(page.url) {
|
||||
onDownload { bytesSentTotal, contentLength ->
|
||||
page.progress.value = (bytesSentTotal.toFloat() / contentLength).coerceAtMost(1.0F)
|
||||
}
|
||||
}
|
||||
|
||||
emit(response)
|
||||
emitAll(response)
|
||||
}
|
||||
.onEach {
|
||||
putImageInCache(it, page)
|
||||
@@ -129,21 +129,21 @@ class TachideskPageLoader(
|
||||
page.bitmap.value = StableHolder(null)
|
||||
page.status.value = ReaderPage.Status.ERROR
|
||||
page.error.value = it.message
|
||||
log.warn(it) { "Failed to get page ${page.index} for chapter ${chapter.chapter.index} for ${chapter.chapter.mangaId}" }
|
||||
log.warn(it) { "Failed to get page ${page.index2} for chapter ${chapter.chapter.index} for ${chapter.chapter.mangaId}" }
|
||||
}
|
||||
.flowOn(Dispatchers.IO)
|
||||
.collect()
|
||||
}
|
||||
|
||||
private suspend fun putImageInCache(
|
||||
response: HttpResponse,
|
||||
response: ByteArray,
|
||||
page: ReaderPage,
|
||||
) {
|
||||
val editor = chapterCache.openEditor(page.cacheKey)
|
||||
?: throw Exception("Couldn't open cache")
|
||||
try {
|
||||
FileSystem.SYSTEM.write(editor.data) {
|
||||
response.bodyAsChannel().toSource().use {
|
||||
response.source().use {
|
||||
writeAll(it)
|
||||
}
|
||||
}
|
||||
@@ -192,7 +192,7 @@ class TachideskPageLoader(
|
||||
currentPage: ReaderPage,
|
||||
amount: Int,
|
||||
): List<PriorityPage> {
|
||||
val pageIndex = currentPage.index
|
||||
val pageIndex = currentPage.index2
|
||||
val pages = (currentPage.chapter.pages.value as? PagesState.Success)?.pages ?: return emptyList()
|
||||
if (pageIndex >= pages.lastIndex) return emptyList()
|
||||
|
||||
@@ -214,14 +214,15 @@ class TachideskPageLoader(
|
||||
override fun getPages(): StateFlow<PagesState> {
|
||||
scope.launch {
|
||||
if (pagesFlow.value != PagesState.Loading) return@launch
|
||||
val pageRange = chapter.chapter.pageCount?.let { 0..it.minus(1) }
|
||||
pagesFlow.value = if (pageRange == null || pageRange.isEmpty()) {
|
||||
val pages = getChapterPages.await(chapter.chapter.id)
|
||||
pagesFlow.value = if (pages.isNullOrEmpty()) {
|
||||
PagesState.Empty
|
||||
} else {
|
||||
PagesState.Success(
|
||||
pageRange.map {
|
||||
pages.mapIndexed { index, url ->
|
||||
ReaderPage(
|
||||
index = it,
|
||||
url = url,
|
||||
index2 = index,
|
||||
bitmap = MutableStateFlow(StableHolder(null)),
|
||||
bitmapInfo = MutableStateFlow(null),
|
||||
progress = MutableStateFlow(0.0F),
|
||||
@@ -295,7 +296,7 @@ class TachideskPageLoader(
|
||||
}
|
||||
|
||||
private val ReaderPage.cacheKey
|
||||
get() = "${chapter.chapter.mangaId}-${chapter.chapter.index}-$index"
|
||||
get() = "${chapter.chapter.id}-$url"
|
||||
|
||||
private fun DiskCache.Snapshot.source(): BufferedSource = FileSystem.SYSTEM.source(data).buffer()
|
||||
|
||||
|
||||
@@ -14,7 +14,8 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
@Immutable
|
||||
data class ReaderPage(
|
||||
val index: Int,
|
||||
val url: String,
|
||||
val index2: Int,
|
||||
val bitmap: MutableStateFlow<StableHolder<(suspend () -> ImageDecodeState)?>>,
|
||||
val bitmapInfo: MutableStateFlow<BitmapInfo?>,
|
||||
val progress: MutableStateFlow<Float>,
|
||||
|
||||
@@ -126,7 +126,7 @@ fun ContinuousReader(
|
||||
}
|
||||
|
||||
fun retry(index: Int) {
|
||||
pages.find { it is ReaderPage && it.index == index }?.let { retry(it as ReaderPage) }
|
||||
pages.find { it is ReaderPage && it.index2 == index }?.let { retry(it as ReaderPage) }
|
||||
}
|
||||
|
||||
when (direction.isVertical) {
|
||||
@@ -199,7 +199,7 @@ private fun LazyListScope.items(
|
||||
pages,
|
||||
key = {
|
||||
when (it) {
|
||||
is ReaderPage -> it.chapter.chapter.index to it.index
|
||||
is ReaderPage -> it.chapter.chapter.index to it.index2
|
||||
is ReaderPageSeparator -> it.previousChapter?.chapter?.index to it.nextChapter?.chapter?.index
|
||||
}
|
||||
},
|
||||
@@ -207,7 +207,7 @@ private fun LazyListScope.items(
|
||||
when (image) {
|
||||
is ReaderPage -> Box(modifier, contentAlignment = Alignment.Center) {
|
||||
ReaderImage(
|
||||
imageIndex = image.index,
|
||||
imageIndex = image.index2,
|
||||
drawableHolder = image.bitmap.collectAsState().value,
|
||||
bitmapInfo = image.bitmapInfo.collectAsState().value,
|
||||
progress = image.progress.collectAsState().value,
|
||||
|
||||
@@ -84,7 +84,7 @@ fun PagerReader(
|
||||
val modifier = parentModifier then Modifier.fillMaxSize()
|
||||
|
||||
fun retry(index: Int) {
|
||||
pages.find { it is ReaderPage && it.index == index }?.let { retry(it as ReaderPage) }
|
||||
pages.find { it is ReaderPage && it.index2 == index }?.let { retry(it as ReaderPage) }
|
||||
}
|
||||
|
||||
if (direction.isVertical) {
|
||||
@@ -95,7 +95,7 @@ fun PagerReader(
|
||||
modifier = modifier,
|
||||
key = {
|
||||
when (val page = pages.getOrNull(it)) {
|
||||
is ReaderPage -> page.chapter.chapter.index to page.index
|
||||
is ReaderPage -> page.chapter.chapter.index to page.index2
|
||||
is ReaderPageSeparator -> page.previousChapter?.chapter?.index to page.nextChapter?.chapter?.index
|
||||
else -> it
|
||||
}
|
||||
@@ -118,7 +118,7 @@ fun PagerReader(
|
||||
modifier = modifier,
|
||||
key = {
|
||||
when (val page = pages.getOrNull(it)) {
|
||||
is ReaderPage -> page.chapter.chapter.index to page.index
|
||||
is ReaderPage -> page.chapter.chapter.index to page.index2
|
||||
is ReaderPageSeparator -> page.previousChapter?.chapter?.index to page.nextChapter?.chapter?.index
|
||||
else -> it
|
||||
}
|
||||
@@ -148,7 +148,7 @@ fun HandlePager(
|
||||
when (val image = pages[page]) {
|
||||
is ReaderPage -> {
|
||||
ReaderImage(
|
||||
imageIndex = image.index,
|
||||
imageIndex = image.index2,
|
||||
drawableHolder = image.bitmap.collectAsState().value,
|
||||
bitmapInfo = image.bitmapInfo.collectAsState().value,
|
||||
progress = image.progress.collectAsState().value,
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
package ca.gosyer.jui.ui.updates
|
||||
|
||||
import ca.gosyer.jui.core.lang.launchDefault
|
||||
import ca.gosyer.jui.domain.chapter.interactor.BatchUpdateChapter
|
||||
import ca.gosyer.jui.domain.chapter.interactor.DeleteChapterDownload
|
||||
import ca.gosyer.jui.domain.chapter.interactor.UpdateChapter
|
||||
import ca.gosyer.jui.domain.chapter.model.Chapter
|
||||
import ca.gosyer.jui.domain.download.interactor.BatchChapterDownload
|
||||
import ca.gosyer.jui.domain.download.interactor.QueueChapterDownload
|
||||
@@ -47,7 +47,7 @@ class UpdatesScreenViewModel
|
||||
private val stopChapterDownload: StopChapterDownload,
|
||||
private val deleteChapterDownload: DeleteChapterDownload,
|
||||
private val getRecentUpdates: GetRecentUpdates,
|
||||
private val batchUpdateChapter: BatchUpdateChapter,
|
||||
private val updateChapter: UpdateChapter,
|
||||
private val batchChapterDownload: BatchChapterDownload,
|
||||
private val updateLibrary: UpdateLibrary,
|
||||
private val updatesPager: UpdatesPager,
|
||||
@@ -119,7 +119,7 @@ class UpdatesScreenViewModel
|
||||
read: Boolean,
|
||||
) {
|
||||
scope.launch {
|
||||
batchUpdateChapter.await(chapterIds, isRead = read, onError = { toast(it.message.orEmpty()) })
|
||||
updateChapter.await(chapterIds, read = read, onError = { toast(it.message.orEmpty()) })
|
||||
_selectedIds.value = persistentListOf()
|
||||
}
|
||||
}
|
||||
@@ -133,7 +133,7 @@ class UpdatesScreenViewModel
|
||||
bookmark: Boolean,
|
||||
) {
|
||||
scope.launch {
|
||||
batchUpdateChapter.await(chapterIds, isBookmarked = bookmark, onError = { toast(it.message.orEmpty()) })
|
||||
updateChapter.await(chapterIds, bookmarked = bookmark, onError = { toast(it.message.orEmpty()) })
|
||||
_selectedIds.value = persistentListOf()
|
||||
}
|
||||
}
|
||||
@@ -158,7 +158,7 @@ class UpdatesScreenViewModel
|
||||
scope.launchDefault {
|
||||
if (chapter == null) {
|
||||
val selectedIds = _selectedIds.value
|
||||
batchUpdateChapter.await(selectedIds, delete = true, onError = { toast(it.message.orEmpty()) })
|
||||
deleteChapterDownload.await(selectedIds, onError = { toast(it.message.orEmpty()) })
|
||||
selectedItems.value.forEach {
|
||||
it.setNotDownloaded()
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ fun UpdatesScreenContent(
|
||||
inActionMode: Boolean,
|
||||
selectedItems: ImmutableList<ChapterDownloadItem>,
|
||||
loadNextPage: () -> Unit,
|
||||
openChapter: (index: Int, mangaId: Long) -> Unit,
|
||||
openChapter: (id: Long, mangaId: Long) -> Unit,
|
||||
openManga: (Long) -> Unit,
|
||||
markRead: (Long?) -> Unit,
|
||||
markUnread: (Long?) -> Unit,
|
||||
@@ -201,7 +201,7 @@ fun UpdatesScreenContent(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
{ openChapter(chapter.index, manga.id) }
|
||||
{ openChapter(chapter.id, manga.id) }
|
||||
},
|
||||
markRead = markRead,
|
||||
markUnread = markUnread,
|
||||
|
||||
@@ -24,13 +24,13 @@ import ca.gosyer.jui.ui.util.lang.launchApplication
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
|
||||
actual class ReaderLauncher {
|
||||
private var isOpen by mutableStateOf<Pair<Int, Long>?>(null)
|
||||
private var isOpen by mutableStateOf<Pair<Long, Long>?>(null)
|
||||
|
||||
actual fun launch(
|
||||
chapterIndex: Int,
|
||||
chapterId: Long,
|
||||
mangaId: Long,
|
||||
) {
|
||||
isOpen = chapterIndex to mangaId
|
||||
isOpen = chapterId to mangaId
|
||||
}
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
@@ -38,7 +38,7 @@ actual class ReaderLauncher {
|
||||
actual fun Reader() {
|
||||
val localParams = currentCompositionLocalContext
|
||||
DisposableEffect(isOpen) {
|
||||
isOpen?.let { (chapterIndex, mangaId) ->
|
||||
isOpen?.let { (chapterId, mangaId) ->
|
||||
launchApplication {
|
||||
val windowState = rememberWindowState(
|
||||
position = WindowPosition.Aligned(Alignment.Center),
|
||||
@@ -52,7 +52,7 @@ actual class ReaderLauncher {
|
||||
state = windowState,
|
||||
) {
|
||||
ReaderMenu(
|
||||
chapterIndex = chapterIndex,
|
||||
chapterId = chapterId,
|
||||
mangaId = mangaId,
|
||||
onCloseRequest = ::exitApplication,
|
||||
)
|
||||
|
||||
@@ -14,14 +14,14 @@ import cafe.adriel.voyager.navigator.Navigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
|
||||
class ReaderScreen(
|
||||
val chapterIndex: Int,
|
||||
val chapterId: Long,
|
||||
val mangaId: Long,
|
||||
) : Screen {
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
ReaderMenu(
|
||||
chapterIndex,
|
||||
chapterId,
|
||||
mangaId,
|
||||
navigator::pop,
|
||||
)
|
||||
@@ -32,10 +32,10 @@ actual class ReaderLauncher(
|
||||
private val navigator: Navigator?,
|
||||
) {
|
||||
actual fun launch(
|
||||
chapterIndex: Int,
|
||||
chapterId: Long,
|
||||
mangaId: Long,
|
||||
) {
|
||||
navigator?.push(ReaderScreen(chapterIndex, mangaId))
|
||||
navigator?.push(ReaderScreen(chapterId, mangaId))
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
||||
Reference in New Issue
Block a user