Fixed: Clicking on a table of content item wasn’t scrolling the WebView to the selected section because the Compose ScrollState caused recomposition back to the previous scroll position — now replaced with a custom approach to smoothly scroll to the target section. * Fixed: The whole screen was scrolling when scrolling the table of contents.

* Fixed: App was crashing and not opening the intro screen on fresh install.
* Fixed: Design and appearance of the table of contents in dark mode.
This commit is contained in:
MohitMaliFtechiz 2025-07-29 02:35:53 +05:30
parent f041ee9cf0
commit 8afaa8be52
8 changed files with 150 additions and 127 deletions

View File

@ -33,6 +33,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.rememberDrawerState import androidx.compose.material3.rememberDrawerState
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.core.content.pm.ShortcutInfoCompat import androidx.core.content.pm.ShortcutInfoCompat
import androidx.core.content.pm.ShortcutManagerCompat import androidx.core.content.pm.ShortcutManagerCompat
@ -40,7 +41,6 @@ import androidx.core.graphics.drawable.IconCompat
import androidx.core.os.ConfigurationCompat import androidx.core.os.ConfigurationCompat
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavController import androidx.navigation.NavController
import androidx.navigation.NavOptions
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import eu.mhutti1.utils.storage.StorageDevice import eu.mhutti1.utils.storage.StorageDevice
import eu.mhutti1.utils.storage.StorageDeviceUtils import eu.mhutti1.utils.storage.StorageDeviceUtils
@ -128,9 +128,17 @@ class KiwixMainActivity : CoreMainActivity() {
leftDrawerState = rememberDrawerState(DrawerValue.Closed) leftDrawerState = rememberDrawerState(DrawerValue.Closed)
uiCoroutineScope = rememberCoroutineScope() uiCoroutineScope = rememberCoroutineScope()
bottomAppBarScrollBehaviour = BottomAppBarDefaults.exitAlwaysScrollBehavior() bottomAppBarScrollBehaviour = BottomAppBarDefaults.exitAlwaysScrollBehavior()
val startDestination = remember {
if (sharedPreferenceUtil.showIntro() && !isIntroScreenNotVisible()) {
KiwixDestination.Intro.route
} else {
KiwixDestination.Reader.route
}
}
KiwixMainActivityScreen( KiwixMainActivityScreen(
navController = navController, navController = navController,
leftDrawerContent = leftDrawerMenu, leftDrawerContent = leftDrawerMenu,
startDestination = startDestination,
topLevelDestinationsRoute = topLevelDestinationsRoute, topLevelDestinationsRoute = topLevelDestinationsRoute,
leftDrawerState = leftDrawerState, leftDrawerState = leftDrawerState,
uiCoroutineScope = uiCoroutineScope, uiCoroutineScope = uiCoroutineScope,
@ -140,12 +148,6 @@ class KiwixMainActivity : CoreMainActivity() {
) )
LaunchedEffect(navController) { LaunchedEffect(navController) {
navController.addOnDestinationChangedListener(finishActionModeOnDestinationChange) navController.addOnDestinationChangedListener(finishActionModeOnDestinationChange)
if (sharedPreferenceUtil.showIntro() && !isIntroScreenNotVisible()) {
val navOptions = NavOptions.Builder()
.setPopUpTo(KiwixDestination.Reader.route, inclusive = true)
.build()
navigate(KiwixDestination.Intro.route, navOptions)
}
} }
DialogHost(alertDialogShower) DialogHost(alertDialogShower)
} }

View File

@ -56,6 +56,7 @@ import org.kiwix.kiwixmobile.ui.KiwixNavGraph
fun KiwixMainActivityScreen( fun KiwixMainActivityScreen(
navController: NavHostController, navController: NavHostController,
leftDrawerContent: List<DrawerMenuGroup>, leftDrawerContent: List<DrawerMenuGroup>,
startDestination: String,
topLevelDestinationsRoute: Set<String>, topLevelDestinationsRoute: Set<String>,
leftDrawerState: DrawerState, leftDrawerState: DrawerState,
uiCoroutineScope: CoroutineScope, uiCoroutineScope: CoroutineScope,
@ -100,6 +101,7 @@ fun KiwixMainActivityScreen(
Box(modifier = Modifier.padding(paddingValues)) { Box(modifier = Modifier.padding(paddingValues)) {
KiwixNavGraph( KiwixNavGraph(
navController = navController, navController = navController,
startDestination = startDestination,
modifier = Modifier.fillMaxSize() modifier = Modifier.fillMaxSize()
) )
} }

View File

@ -221,9 +221,6 @@ class KiwixReaderFragment : CoreReaderFragment() {
exitBook() exitBook()
} }
override fun getBottomNavigationView(): BottomNavigationView? = null
// requireActivity().findViewById(R.id.bottom_nav_view)
/** /**
* Restores the view state based on the provided webViewHistoryItemList data and restore origin. * Restores the view state based on the provided webViewHistoryItemList data and restore origin.
* *

View File

@ -77,11 +77,12 @@ import org.kiwix.kiwixmobile.webserver.ZimHostFragment
@Composable @Composable
fun KiwixNavGraph( fun KiwixNavGraph(
navController: NavHostController, navController: NavHostController,
startDestination: String,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
NavHost( NavHost(
navController = navController, navController = navController,
startDestination = KiwixDestination.Reader.route, startDestination = startDestination,
modifier = modifier modifier = modifier
) { ) {
composable( composable(

View File

@ -73,7 +73,6 @@ import androidx.drawerlayout.widget.DrawerLayout
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomnavigation.BottomNavigationView
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -303,7 +302,6 @@ abstract class CoreReaderFragment :
nextPageButtonItem = Triple({ goForward() }, { showForwardHistory() }, false), nextPageButtonItem = Triple({ goForward() }, { showForwardHistory() }, false),
tocButtonItem = false to { }, tocButtonItem = false to { },
onCloseAllTabs = { closeAllTabs() }, onCloseAllTabs = { closeAllTabs() },
bottomNavigationHeight = ZERO,
shouldShowBottomAppBar = true, shouldShowBottomAppBar = true,
selectedWebView = null, selectedWebView = null,
readerScreenTitle = "", readerScreenTitle = "",
@ -330,9 +328,7 @@ abstract class CoreReaderFragment :
appName = "", appName = "",
donateButtonClick = {}, donateButtonClick = {},
laterButtonClick = {}, laterButtonClick = {},
tableOfContentTitle = "", tableOfContentTitle = ""
tableContentHeaderClick = { tableOfContentHeaderClick() },
tableOfContentSectionClick = { tableOfContentSectionClick(it) },
) )
) )
private var readerLifeCycleScope: CoroutineScope? = null private var readerLifeCycleScope: CoroutineScope? = null
@ -443,7 +439,6 @@ abstract class CoreReaderFragment :
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
readerScreenState.update { readerScreenState.update {
copy( copy(
bottomNavigationHeight = getBottomNavigationHeight(),
readerScreenTitle = context.getString(string.reader), readerScreenTitle = context.getString(string.reader),
darkModeViewPainter = darkModeViewPainter, darkModeViewPainter = darkModeViewPainter,
fullScreenItem = fullScreenItem.first to getVideoView(), fullScreenItem = fullScreenItem.first to getVideoView(),
@ -482,7 +477,7 @@ abstract class CoreReaderFragment :
}, },
mainActivityBottomAppBarScrollBehaviour = (requireActivity() as CoreMainActivity).bottomAppBarScrollBehaviour, mainActivityBottomAppBarScrollBehaviour = (requireActivity() as CoreMainActivity).bottomAppBarScrollBehaviour,
documentSections = documentSections, documentSections = documentSections,
showTableOfContentDrawer = shouldTableOfContentDrawer, showTableOfContentDrawer = shouldTableOfContentDrawer
) )
DialogHost(alertDialogShower as AlertDialogShower) DialogHost(alertDialogShower as AlertDialogShower)
} }
@ -545,8 +540,6 @@ abstract class CoreReaderFragment :
} }
} }
private fun getBottomNavigationHeight(): Int = getBottomNavigationView()?.measuredHeight ?: ZERO
/** /**
* Provides the visibility state and click action for the TOC (Table of Contents) button * Provides the visibility state and click action for the TOC (Table of Contents) button
* shown in the reader's bottom app bar. * shown in the reader's bottom app bar.
@ -677,31 +670,6 @@ abstract class CoreReaderFragment :
documentSections?.clear() documentSections?.clear()
} }
private fun tableOfContentHeaderClick() {
getCurrentWebView()?.scrollY = 0
shouldTableOfContentDrawer.update { false }
}
private fun tableOfContentSectionClick(position: Int) {
if (hasItemForPositionInDocumentSectionsList(position)) { // Bug Fix #3796
loadUrlWithCurrentWebview(
"javascript:document.getElementById('" +
documentSections?.get(position)?.id?.replace("'", "\\'") +
"').scrollIntoView();"
)
}
shouldTableOfContentDrawer.update { false }
}
private fun hasItemForPositionInDocumentSectionsList(position: Int): Boolean {
val documentListSize = documentSections?.size ?: return false
return when {
position < 0 -> false
position >= documentListSize -> false
else -> true
}
}
private fun showTabSwitcher() { private fun showTabSwitcher() {
(requireActivity() as CoreMainActivity).apply { (requireActivity() as CoreMainActivity).apply {
disableDrawer() disableDrawer()
@ -2699,8 +2667,6 @@ abstract class CoreReaderFragment :
* when handling invalid JSON scenarios. * when handling invalid JSON scenarios.
*/ */
abstract fun restoreViewStateOnInvalidWebViewHistory() abstract fun restoreViewStateOnInvalidWebViewHistory()
abstract fun getBottomNavigationView(): BottomNavigationView?
} }
enum class RestoreOrigin { enum class RestoreOrigin {

View File

@ -33,10 +33,12 @@ import androidx.compose.animation.slideOutHorizontally
import androidx.compose.animation.slideOutVertically import androidx.compose.animation.slideOutVertically
import androidx.compose.animation.togetherWith import androidx.compose.animation.togetherWith
import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.BoxScope
@ -86,12 +88,14 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.key import androidx.compose.runtime.key
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
@ -111,8 +115,10 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.viewinterop.AndroidView
import androidx.compose.ui.zIndex import androidx.compose.ui.zIndex
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.kiwix.kiwixmobile.core.R import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.downloader.downloadManager.HUNDERED import org.kiwix.kiwixmobile.core.downloader.downloadManager.HUNDERED
import org.kiwix.kiwixmobile.core.downloader.downloadManager.ZERO
import org.kiwix.kiwixmobile.core.extensions.update import org.kiwix.kiwixmobile.core.extensions.update
import org.kiwix.kiwixmobile.core.main.DarkModeViewPainter import org.kiwix.kiwixmobile.core.main.DarkModeViewPainter
import org.kiwix.kiwixmobile.core.main.KiwixWebView import org.kiwix.kiwixmobile.core.main.KiwixWebView
@ -130,6 +136,7 @@ import org.kiwix.kiwixmobile.core.ui.models.toPainter
import org.kiwix.kiwixmobile.core.ui.theme.Black import org.kiwix.kiwixmobile.core.ui.theme.Black
import org.kiwix.kiwixmobile.core.ui.theme.DenimBlue800 import org.kiwix.kiwixmobile.core.ui.theme.DenimBlue800
import org.kiwix.kiwixmobile.core.ui.theme.KiwixDialogTheme import org.kiwix.kiwixmobile.core.ui.theme.KiwixDialogTheme
import org.kiwix.kiwixmobile.core.ui.theme.MineShaftGray700
import org.kiwix.kiwixmobile.core.ui.theme.White import org.kiwix.kiwixmobile.core.ui.theme.White
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.BACK_TO_TOP_BUTTON_BOTTOM_MARGIN import org.kiwix.kiwixmobile.core.utils.ComposeDimens.BACK_TO_TOP_BUTTON_BOTTOM_MARGIN
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.CLOSE_ALL_TAB_BUTTON_BOTTOM_PADDING import org.kiwix.kiwixmobile.core.utils.ComposeDimens.CLOSE_ALL_TAB_BUTTON_BOTTOM_PADDING
@ -139,6 +146,7 @@ import org.kiwix.kiwixmobile.core.utils.ComposeDimens.EIGHT_DP
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.FIVE_DP import org.kiwix.kiwixmobile.core.utils.ComposeDimens.FIVE_DP
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.FOUR_DP import org.kiwix.kiwixmobile.core.utils.ComposeDimens.FOUR_DP
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.KIWIX_TOOLBAR_HEIGHT import org.kiwix.kiwixmobile.core.utils.ComposeDimens.KIWIX_TOOLBAR_HEIGHT
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.LARGE_BODY_TEXT_SIZE
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.NAVIGATION_DRAWER_WIDTH import org.kiwix.kiwixmobile.core.utils.ComposeDimens.NAVIGATION_DRAWER_WIDTH
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.ONE_DP import org.kiwix.kiwixmobile.core.utils.ComposeDimens.ONE_DP
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.READER_BOTTOM_APP_BAR_BUTTON_ICON_SIZE import org.kiwix.kiwixmobile.core.utils.ComposeDimens.READER_BOTTOM_APP_BAR_BUTTON_ICON_SIZE
@ -171,60 +179,65 @@ fun ReaderScreen(
mainActivityBottomAppBarScrollBehaviour: BottomAppBarScrollBehavior?, mainActivityBottomAppBarScrollBehaviour: BottomAppBarScrollBehavior?,
navigationIcon: @Composable () -> Unit navigationIcon: @Composable () -> Unit
) { ) {
val bottomNavHeightInDp = with(LocalDensity.current) { state.bottomNavigationHeight.toDp() } val localWebViewScrollState: MutableState<ScrollState?> =
remember { mutableStateOf(ScrollState(0)) }
val topAppBarScrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior() val topAppBarScrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
val bottomAppBarScrollBehavior = BottomAppBarDefaults.exitAlwaysScrollBehavior() val bottomAppBarScrollBehavior = BottomAppBarDefaults.exitAlwaysScrollBehavior()
KiwixDialogTheme { KiwixDialogTheme {
Scaffold( Box(Modifier.fillMaxSize()) {
snackbarHost = { KiwixSnackbarHost(snackbarHostState = state.snackBarHostState) }, Scaffold(
topBar = { snackbarHost = { KiwixSnackbarHost(snackbarHostState = state.snackBarHostState) },
ReaderTopBar( topBar = {
state, ReaderTopBar(
actionMenuItems, state,
topAppBarScrollBehavior, actionMenuItems,
navigationIcon topAppBarScrollBehavior,
) navigationIcon
}, )
floatingActionButton = { BackToTopFab(state) }, },
modifier = Modifier floatingActionButton = { BackToTopFab(state) },
.systemBarsPadding() modifier = Modifier
.padding(bottom = bottomNavHeightInDp) .systemBarsPadding()
.nestedScroll(topAppBarScrollBehavior.nestedScrollConnection) .nestedScroll(topAppBarScrollBehavior.nestedScrollConnection)
.nestedScroll(bottomAppBarScrollBehavior.nestedScrollConnection) .nestedScroll(bottomAppBarScrollBehavior.nestedScrollConnection)
.let { baseModifier -> .let { baseModifier ->
mainActivityBottomAppBarScrollBehaviour?.let { mainActivityBottomAppBarScrollBehaviour?.let {
baseModifier.nestedScroll(it.nestedScrollConnection) baseModifier.nestedScroll(it.nestedScrollConnection)
} ?: baseModifier } ?: baseModifier
} }
.semantics { testTag = READER_SCREEN_TESTING_TAG } .semantics { testTag = READER_SCREEN_TESTING_TAG }
) { paddingValues -> ) { paddingValues ->
Box(Modifier.fillMaxSize()) {
ReaderContentLayout( ReaderContentLayout(
state, state,
Modifier.padding(paddingValues), Modifier.padding(paddingValues),
bottomAppBarScrollBehavior bottomAppBarScrollBehavior,
localWebViewScrollState.value
)
}
if (showTableOfContentDrawer.value) {
// Showing the background color on screen so that it look same as navigation drawer.
Box(
Modifier
.fillMaxSize()
.background(Color.Black.copy(alpha = 0.3f))
.clickable { showTableOfContentDrawer.update { false } }
)
}
AnimatedVisibility(
visible = showTableOfContentDrawer.value,
enter = slideInHorizontally(initialOffsetX = { it }) + fadeIn(),
exit = slideOutHorizontally(targetOffsetX = { it }) + fadeOut(),
modifier = Modifier
.systemBarsPadding()
.align(Alignment.CenterEnd)
) {
TableDrawerSheet(
title = state.tableOfContentTitle,
sections = documentSections.orEmpty(),
localWebViewScrollState,
state.selectedWebView,
showTableOfContentDrawer
) )
AnimatedVisibility(
visible = showTableOfContentDrawer.value,
enter = slideInHorizontally(initialOffsetX = { it }) + fadeIn(),
exit = slideOutHorizontally(targetOffsetX = { it }) + fadeOut(),
modifier = Modifier.align(Alignment.CenterEnd)
) {
TableDrawerSheet(
title = state.tableOfContentTitle,
sections = documentSections.orEmpty(),
onHeaderClick = state.tableContentHeaderClick,
onSectionClick = state.tableOfContentSectionClick
)
}
if (showTableOfContentDrawer.value) {
Box(
Modifier
.fillMaxSize()
.background(Color.Black.copy(alpha = 0.3f))
.clickable { showTableOfContentDrawer.update { false } }
)
}
} }
} }
} }
@ -256,7 +269,8 @@ private fun ReaderTopBar(
private fun ReaderContentLayout( private fun ReaderContentLayout(
state: ReaderScreenState, state: ReaderScreenState,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
bottomAppBarScrollBehavior: BottomAppBarScrollBehavior bottomAppBarScrollBehavior: BottomAppBarScrollBehavior,
webViewScrollState: ScrollState?
) { ) {
Box(modifier = modifier.fillMaxSize()) { Box(modifier = modifier.fillMaxSize()) {
TabSwitcherAnimated(state) TabSwitcherAnimated(state)
@ -266,7 +280,7 @@ private fun ReaderContentLayout(
state.fullScreenItem.first -> ShowFullScreenView(state) state.fullScreenItem.first -> ShowFullScreenView(state)
else -> { else -> {
ShowZIMFileContent(state) ShowZIMFileContent(state, webViewScrollState)
ShowProgressBarIfZIMFilePageIsLoading(state) ShowProgressBarIfZIMFilePageIsLoading(state)
Column(Modifier.align(Alignment.BottomCenter)) { Column(Modifier.align(Alignment.BottomCenter)) {
TtsControls(state) TtsControls(state)
@ -295,23 +309,40 @@ private fun ReaderContentLayout(
fun TableDrawerSheet( fun TableDrawerSheet(
title: String, title: String,
sections: List<DocumentSection>, sections: List<DocumentSection>,
onHeaderClick: () -> Unit, webViewScrollState: MutableState<ScrollState?>,
onSectionClick: (Int) -> Unit selectedWebView: KiwixWebView?,
showTableOfContentDrawer: MutableState<Boolean>
) { ) {
val drawerBackgroundColor = if (isSystemInDarkTheme()) {
MineShaftGray700
} else {
White
}
var scrollToSectionIndex by remember { mutableStateOf<Int?>(null) }
val coroutineScope = rememberCoroutineScope()
LaunchedEffect(scrollToSectionIndex) {
scrollToSectionIndex?.let {
webViewScrollState.value = null
}
}
ModalDrawerSheet( ModalDrawerSheet(
modifier = Modifier.width(NAVIGATION_DRAWER_WIDTH) modifier = Modifier.width(NAVIGATION_DRAWER_WIDTH),
drawerShape = RectangleShape,
drawerContainerColor = drawerBackgroundColor
) { ) {
LazyColumn( LazyColumn(modifier = Modifier.fillMaxHeight()) {
modifier = Modifier
.fillMaxHeight()
) {
item { item {
Text( Text(
text = title.ifEmpty { stringResource(id = R.string.no_section_info) }, text = title.ifEmpty { stringResource(id = R.string.no_section_info) },
style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.Bold), style = MaterialTheme.typography.titleMedium,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable { onHeaderClick() } .clickable {
coroutineScope.launch {
webViewScrollState.value?.animateScrollTo(ZERO)
}
showTableOfContentDrawer.update { false }
}
.padding(horizontal = SIXTEEN_DP, vertical = TWELVE_DP) .padding(horizontal = SIXTEEN_DP, vertical = TWELVE_DP)
) )
} }
@ -319,15 +350,46 @@ fun TableDrawerSheet(
val paddingStart = (section.level - ONE) * TWELVE val paddingStart = (section.level - ONE) * TWELVE
Text( Text(
text = section.title, text = section.title,
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium.copy(
fontWeight = FontWeight.Light,
fontSize = LARGE_BODY_TEXT_SIZE
),
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable { onSectionClick(index) } .clickable { scrollToSectionIndex = index }
.padding(start = paddingStart.dp, top = EIGHT_DP, bottom = EIGHT_DP, end = SIXTEEN_DP) .padding(start = paddingStart.dp, top = EIGHT_DP, bottom = EIGHT_DP, end = SIXTEEN_DP)
) )
} }
} }
} }
LaunchedEffect(webViewScrollState.value) {
if (webViewScrollState.value == null &&
scrollToSectionIndex != null &&
hasItemForPositionInDocumentSectionsList(scrollToSectionIndex!!, sections)
) {
val targetId = sections[scrollToSectionIndex!!].id.replace("'", "\\'")
selectedWebView?.evaluateJavascript(
"document.getElementById('$targetId')?.scrollIntoView();",
null
)
delay(100)
webViewScrollState.value = ScrollState(selectedWebView?.scrollY ?: ZERO)
scrollToSectionIndex = null
showTableOfContentDrawer.update { false }
}
}
}
private fun hasItemForPositionInDocumentSectionsList(
position: Int,
sections: List<DocumentSection>
): Boolean {
val documentListSize = sections.size
return when {
position < 0 -> false
position >= documentListSize -> false
else -> true
}
} }
@Composable @Composable
@ -433,12 +495,13 @@ private fun BoxScope.CloseFullScreenImageButton(
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
private fun ShowZIMFileContent(state: ReaderScreenState) { private fun ShowZIMFileContent(state: ReaderScreenState, webViewScrollState: ScrollState?) {
state.selectedWebView?.let { selectedWebView -> state.selectedWebView?.let { selectedWebView ->
key(selectedWebView) { key(selectedWebView) {
ScrollableWebViewWithNestedScroll( ScrollableWebViewWithNestedScroll(
selectedWebView = selectedWebView, selectedWebView = selectedWebView,
modifier = Modifier.fillMaxSize() modifier = Modifier.fillMaxSize(),
webViewScrollState = webViewScrollState
) )
} }
} }
@ -448,12 +511,19 @@ private fun ShowZIMFileContent(state: ReaderScreenState) {
@Composable @Composable
fun ScrollableWebViewWithNestedScroll( fun ScrollableWebViewWithNestedScroll(
selectedWebView: KiwixWebView, selectedWebView: KiwixWebView,
modifier: Modifier = Modifier modifier: Modifier = Modifier,
webViewScrollState: ScrollState?
) { ) {
Box( Box(
modifier = modifier modifier = modifier
.fillMaxSize() .fillMaxSize()
.verticalScroll(rememberScrollState()) .let { baseModifier ->
webViewScrollState?.let {
baseModifier.verticalScroll(it)
} ?: run {
baseModifier
}
}
) { ) {
AndroidView( AndroidView(
factory = { context -> factory = { context ->

View File

@ -139,10 +139,6 @@ data class ReaderScreenState(
*/ */
val tocButtonItem: Pair<Boolean, () -> Unit>, val tocButtonItem: Pair<Boolean, () -> Unit>,
val onCloseAllTabs: () -> Unit, val onCloseAllTabs: () -> Unit,
/**
* Stores the height of the bottom navigation bar in pixels.
*/
val bottomNavigationHeight: Int,
/** /**
* Manages the showing of Reader's [BottomAppBarOfReaderScreen]. * Manages the showing of Reader's [BottomAppBarOfReaderScreen].
*/ */
@ -172,13 +168,5 @@ data class ReaderScreenState(
/** /**
* Manages the showing of header title of "table of content". * Manages the showing of header title of "table of content".
*/ */
val tableOfContentTitle: String, val tableOfContentTitle: String
/**
* Handles the click when user clicks on the "Header" of "table of content".
*/
val tableContentHeaderClick: () -> Unit,
/**
* Handles the click when user clicks on the "section" of "table of content".
*/
val tableOfContentSectionClick: (Int) -> Unit
) )

View File

@ -173,9 +173,6 @@ class CustomReaderFragment : CoreReaderFragment() {
openHomeScreen() openHomeScreen()
} }
// Since custom apps do not have the bottomNavigationView, so returning null.
override fun getBottomNavigationView(): BottomNavigationView? = null
/** /**
* Restores the view state when the webViewHistory data is valid. * Restores the view state when the webViewHistory data is valid.
* This method restores the tabs with webView pages history. * This method restores the tabs with webView pages history.