diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/ReaderMenu.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/ReaderMenu.kt index 9a102b4e..b3a10222 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/ReaderMenu.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/ReaderMenu.kt @@ -244,13 +244,13 @@ fun WideReaderMenu( fitSize: Boolean, maxSize: Int, navigationViewer: ViewerNavigation, - currentPage: Int, + currentPage: ReaderItem?, currentPageOffset: Int, navigate: (Int) -> Unit, navigateTap: (Navigation) -> Unit, pageEmitterHolder: StableHolder>, retry: (ReaderPage) -> Unit, - progress: (Int) -> Unit, + progress: (ReaderItem) -> Unit, updateLastPageReadOffset: (Int) -> Unit, sideMenuOpen: Boolean, setSideMenuOpen: (Boolean) -> Unit, @@ -273,6 +273,7 @@ fun WideReaderMenu( ) { ReaderSideMenu( chapter = chapter, + pages = pages, currentPage = currentPage, readerModes = readerModes, selectedMode = readerMode, @@ -325,13 +326,13 @@ fun ThinReaderMenu( fitSize: Boolean, maxSize: Int, navigationViewer: ViewerNavigation, - currentPage: Int, + currentPage: ReaderItem?, currentPageOffset: Int, navigate: (Int) -> Unit, navigateTap: (Navigation) -> Unit, pageEmitterHolder: StableHolder>, retry: (ReaderPage) -> Unit, - progress: (Int) -> Unit, + progress: (ReaderItem) -> Unit, updateLastPageReadOffset: (Int) -> Unit, readerMenuOpen: Boolean, setMangaReaderMode: (String) -> Unit, @@ -407,6 +408,7 @@ fun ThinReaderMenu( chapter = chapter, nextChapter = nextChapter, direction = direction, + pages = pages, currentPage = currentPage, navigate = navigate, readerMenuOpen = readerMenuOpen, @@ -427,12 +429,12 @@ fun ReaderLayout( fitSize: Boolean, maxSize: Int, navigationViewer: ViewerNavigation, - currentPage: Int, + currentPage: ReaderItem?, currentPageOffset: Int, navigateTap: (Navigation) -> Unit, pageEmitterHolder: StableHolder>, retry: (ReaderPage) -> Unit, - progress: (Int) -> Unit, + progress: (ReaderItem) -> Unit, updateLastPageReadOffset: (Int) -> Unit ) { val loadingModifier = Modifier.widthIn(max = 700.dp) diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/ReaderMenuViewModel.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/ReaderMenuViewModel.kt index b1731336..cca10944 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/ReaderMenuViewModel.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/ReaderMenuViewModel.kt @@ -95,7 +95,7 @@ class ReaderMenuViewModel @Inject constructor( private val _pages = MutableStateFlow>(persistentListOf()) val pages = _pages.asStateFlow() - private val _currentPage = MutableStateFlow(1) + private val _currentPage = MutableStateFlow(null) val currentPage = _currentPage.asStateFlow() private val _currentPageOffset = MutableStateFlow(1) @@ -163,7 +163,7 @@ class ReaderMenuViewModel @Inject constructor( } } if (moveTo != null) { - _pageEmitter.emit(PageMove.Direction(moveTo, currentPage.value)) + _pageEmitter.emit(PageMove.Direction(moveTo)) } } } @@ -171,24 +171,24 @@ class ReaderMenuViewModel @Inject constructor( fun navigate(page: Int) { log.info { "Navigate to $page" } scope.launch { - _pageEmitter.emit(PageMove.Page(page)) + _pageEmitter.emit(PageMove.Page(pages.value.getOrNull(page) ?: return@launch)) } } - fun progress(index: Int) { - log.info { "Progressed to $index" } - _currentPage.value = index + fun progress(page: ReaderItem) { + log.info { "Progressed to $page" } + _currentPage.value = page } fun retry(page: ReaderPage) { - log.info { "Retrying $page" } + log.info { "Retrying ${page.index}" } chapter.value?.pageLoader?.retryPage(page) } private fun resetValues() { viewerChapters.recycle() _pages.value = persistentListOf() - _currentPage.value = 1 + _currentPage.value = null } fun setMangaReaderMode(mode: String) { @@ -279,11 +279,6 @@ class ReaderMenuViewModel @Inject constructor( ) } - val lastPageRead = chapter.chapter.lastPageRead - if (lastPageRead != 0) { - _currentPage.value = lastPageRead.coerceAtMost(chapter.chapter.pageCount!!) - } - val lastPageReadOffset = chapter.chapter.meta.juiPageOffset if (lastPageReadOffset != 0) { _currentPageOffset.value = lastPageReadOffset @@ -300,14 +295,20 @@ class ReaderMenuViewModel @Inject constructor( val prevSeparator = ReaderPageSeparator(viewerChapters.prevChapter.value, chapter) val nextSeparator = ReaderPageSeparator(chapter, viewerChapters.nextChapter.value) _pages.value = (_pages.value.ifEmpty { listOf(prevSeparator) } + pageList + nextSeparator).toImmutableList() - pageList.getOrNull(_currentPage.value - 1)?.let { chapter.pageLoader?.loadPage(it) } + val lastPageRead = chapter.chapter.lastPageRead + _currentPage.value = if (lastPageRead != 0) { + pageList[lastPageRead.coerceAtMost(pageList.lastIndex)] + } else { + pageList.first() + }.also { chapter.pageLoader?.loadPage(it) } } .launchIn(chapter.scope) _currentPage - .onEach { index -> - (_pages.value.getOrNull(_currentPage.value - 1) as? ReaderPage)?.let { chapter.pageLoader?.loadPage(it) } - if (index == _pages.value.lastIndex) { + .filterIsInstance() + .onEach { page -> + chapter.pageLoader?.loadPage(page) + if ((page.index + 1) >= chapter.chapter.pageCount!!) { markChapterRead(chapter) } } @@ -315,14 +316,25 @@ class ReaderMenuViewModel @Inject constructor( } private fun markChapterRead(chapter: ReaderChapter) { - scope.launch { updateChapterRead.await(chapter.chapter, read = true, onError = { toast(it.message.orEmpty()) }) } + scope.launch { + updateChapterRead.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) { + fun sendProgress( + chapter: Chapter? = this.chapter.value?.chapter, + lastPageRead: Int = (currentPage.value as? ReaderPage)?.index ?: 0 + ) { chapter ?: return if (chapter.read) return - GlobalScope.launch { updateChapterLastPageRead.await(chapter, lastPageRead = lastPageRead, onError = { toast(it.message.orEmpty()) }) } + GlobalScope.launch { + updateChapterLastPageRead.await( + chapter, + lastPageRead = lastPageRead, + onError = { toast(it.message.orEmpty()) } + ) + } } fun updateLastPageReadOffset(offset: Int) { @@ -331,7 +343,9 @@ class ReaderMenuViewModel @Inject constructor( @OptIn(DelicateCoroutinesApi::class) private fun updateLastPageReadOffset(chapter: Chapter, offset: Int) { - GlobalScope.launch { updateChapterMeta.await(chapter, offset, onError = { toast(it.message.orEmpty()) }) } + GlobalScope.launch { + updateChapterMeta.await(chapter, offset, onError = { toast(it.message.orEmpty()) }) + } } override fun onDispose() { diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/ReaderSideMenu.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/ReaderSideMenu.kt index 8b71401b..d79fb352 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/ReaderSideMenu.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/ReaderSideMenu.kt @@ -56,6 +56,7 @@ 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.model.ReaderChapter +import ca.gosyer.jui.ui.reader.model.ReaderItem import ca.gosyer.jui.uicore.components.AroundLayout import ca.gosyer.jui.uicore.components.Spinner import ca.gosyer.jui.uicore.resources.stringResource @@ -67,7 +68,8 @@ import kotlin.math.roundToInt @Composable fun ReaderSideMenu( chapter: ReaderChapter, - currentPage: Int, + pages: ImmutableList, + currentPage: ReaderItem?, readerModes: ImmutableList, selectedMode: String, onNewPageClicked: (Int) -> Unit, @@ -86,6 +88,7 @@ fun ReaderSideMenu( onSetReaderMode = onSetReaderMode ) ReaderProgressSlider( + pages = pages, currentPage = currentPage, pageCount = pageCount, onNewPageClicked = onNewPageClicked, @@ -106,7 +109,8 @@ fun ReaderExpandBottomMenu( chapter: ReaderChapter, nextChapter: ReaderChapter?, direction: Direction, - currentPage: Int, + pages: ImmutableList, + currentPage: ReaderItem?, navigate: (Int) -> Unit, readerMenuOpen: Boolean, movePrevChapter: () -> Unit, @@ -170,7 +174,7 @@ fun ReaderExpandBottomMenu( startLayout = { Box(Modifier.fillMaxHeight().width(32.dp), contentAlignment = Alignment.Center) { val text = if (!isRtL) { - currentPage + pages.indexOf(currentPage) } else { chapter.chapter.pageCount!! }.toString() @@ -180,7 +184,7 @@ fun ReaderExpandBottomMenu( endLayout = { Box(Modifier.fillMaxHeight().width(32.dp), contentAlignment = Alignment.Center) { val text = if (isRtL) { - currentPage + pages.indexOf(currentPage) } else { chapter.chapter.pageCount!! }.toString() @@ -192,6 +196,7 @@ fun ReaderExpandBottomMenu( modifier = Modifier.fillMaxWidth() .padding(paddingValues) .padding(horizontal = 4.dp), + pages = pages, currentPage = currentPage, pageCount = chapter.chapter.pageCount!!, onNewPageClicked = navigate, @@ -253,13 +258,14 @@ private fun ReaderMenuToolbar(onCloseSideMenuClicked: () -> Unit) { @Composable private fun ReaderProgressSlider( modifier: Modifier = Modifier, - currentPage: Int, + pages: ImmutableList, + currentPage: ReaderItem?, pageCount: Int, onNewPageClicked: (Int) -> Unit, isRtL: Boolean ) { val animatedProgress by animateFloatAsState( - targetValue = currentPage.toFloat(), + targetValue = pages.indexOf(currentPage).toFloat(), animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec ) var isValueChanging by remember { mutableStateOf(false) } diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/model/PageMove.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/model/PageMove.kt index 666e6f84..07329c15 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/model/PageMove.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/model/PageMove.kt @@ -7,6 +7,6 @@ package ca.gosyer.jui.ui.reader.model sealed class PageMove { - data class Direction(val moveTo: MoveTo, val currentPage: Int) : PageMove() - data class Page(val pageNumber: Int) : PageMove() + data class Direction(val moveTo: MoveTo) : PageMove() + data class Page(val page: ReaderItem) : PageMove() } diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/viewer/Continuous.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/viewer/Continuous.kt index c2facba1..725b7a53 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/viewer/Continuous.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/viewer/Continuous.kt @@ -49,6 +49,7 @@ import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapLatest +import kotlinx.coroutines.flow.mapNotNull @Composable fun ContinuousReader( @@ -57,17 +58,17 @@ fun ContinuousReader( direction: Direction, maxSize: Int, padding: Int, - currentPage: Int, + currentPage: ReaderItem?, currentPageOffset: Int, loadingModifier: Modifier, pageContentScale: ContentScale, pageEmitterHolder: StableHolder>, retry: (ReaderPage) -> Unit, - progress: (Int) -> Unit, + progress: (ReaderItem) -> Unit, updateLastPageReadOffset: (Int) -> Unit ) { BoxWithConstraints(modifier then Modifier.fillMaxSize()) { - val state = rememberLazyListState(currentPage, currentPageOffset) + val state = rememberLazyListState(pages.indexOf(currentPage).coerceAtLeast(1), currentPageOffset) val density = LocalDensity.current LaunchedEffect(Unit) { pageEmitterHolder.item @@ -87,8 +88,8 @@ fun ContinuousReader( Unit } is PageMove.Page -> { - val (pageNumber) = pageMove - if (pageNumber in 0..pages.size) { + val pageNumber = pages.indexOf(pageMove.page) + if (pageNumber > -1) { state.animateScrollToItem(pageNumber) } } @@ -104,6 +105,7 @@ fun ContinuousReader( LaunchedEffect(state) { snapshotFlow { state.layoutInfo.visibleItemsInfo.lastOrNull()?.index } .filterNotNull() + .mapNotNull { pages.getOrNull(it) } .collect(progress) } diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/viewer/Pager.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/viewer/Pager.kt index da121411..703a5ed6 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/viewer/Pager.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/viewer/Pager.kt @@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Modifier import androidx.compose.ui.layout.ContentScale import ca.gosyer.jui.domain.reader.model.Direction @@ -33,34 +34,36 @@ import kotlinx.coroutines.flow.mapLatest fun PagerReader( parentModifier: Modifier, direction: Direction, - currentPage: Int, + currentPage: ReaderItem?, pages: ImmutableList, loadingModifier: Modifier, pageContentScale: ContentScale, pageEmitterHolder: StableHolder>, retry: (ReaderPage) -> Unit, - progress: (Int) -> Unit + progress: (ReaderItem) -> Unit ) { - val state = rememberPagerState(initialPage = currentPage) + val state = rememberPagerState(initialPage = pages.indexOf(currentPage).coerceAtLeast(1)) + val currentPageState = rememberUpdatedState(currentPage) - LaunchedEffect(pages.size) { + LaunchedEffect(pages.size, state, currentPageState) { val pageRange = 0..(pages.size + 1) pageEmitterHolder.item .mapLatest { pageMove -> when (pageMove) { is PageMove.Direction -> { - val (moveTo, currentPage) = pageMove + val currentPage = currentPageState.value ?: return@mapLatest + val (moveTo) = pageMove val page = when (moveTo) { - MoveTo.Previous -> currentPage - 1 - MoveTo.Next -> currentPage + 1 + MoveTo.Previous -> pages.indexOf(currentPage) - 1 + MoveTo.Next -> pages.indexOf(currentPage) + 1 } if (page in pageRange) { state.animateScrollToPage(page) } } is PageMove.Page -> { - val (pageNumber) = pageMove - if (pageNumber in pageRange) { + val pageNumber = pages.indexOf(pageMove.page) + if (pageNumber > -1) { state.animateScrollToPage(pageNumber) } } @@ -70,8 +73,9 @@ fun PagerReader( } LaunchedEffect(state.currentPage) { - if (state.currentPage != currentPage) { - progress(state.currentPage) + val page = pages.getOrNull(state.currentPage) + if (page != null) { + progress(page) } } val modifier = parentModifier then Modifier.fillMaxSize()