Use a bottom sheet for theme, set header based on theme

This commit is contained in:
Jackson Harper 2023-05-05 21:45:04 +08:00
parent 34f0a964ec
commit 135ecbaa11
3 changed files with 368 additions and 252 deletions

View file

@ -1,47 +1,31 @@
package app.omnivore.omnivore.ui.reader
import android.util.Log
import androidx.compose.foundation.clickable
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Switch
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.KeyboardArrowDown
import androidx.compose.material.icons.filled.KeyboardArrowRight
import androidx.compose.material3.Divider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.unit.sp
import app.omnivore.omnivore.R
import app.omnivore.omnivore.ui.components.SegmentedControl
@Composable
fun WebPreferencesDialog(onDismiss: () -> Unit, webReaderViewModel: WebReaderViewModel) {
Dialog(onDismissRequest = { onDismiss() }) {
Surface(
shape = RoundedCornerShape(16.dp),
color = Color.White,
modifier = Modifier
.height(350.dp)
) {
WebPreferencesView(webReaderViewModel)
}
}
}
import app.omnivore.omnivore.ui.theme.OmnivoreTheme
@Composable
fun WebPreferencesView(webReaderViewModel: WebReaderViewModel) {
@ -54,125 +38,207 @@ fun WebPreferencesView(webReaderViewModel: WebReaderViewModel) {
val selectedWebFontRawValue = remember { mutableStateOf(currentWebPreferences.fontFamily.rawValue) }
var fontSizeSliderValue by remember { mutableStateOf(currentWebPreferences.textFontSize.toFloat()) }
var marginSliderValue by remember { mutableStateOf(currentWebPreferences.maxWidthPercentage.toFloat()) }
var lineSpacingSliderValue by remember { mutableStateOf(currentWebPreferences.lineHeight.toFloat()) }
val themeState = remember { mutableStateOf(currentWebPreferences.storedThemePreference) }
val themeListState = rememberLazyListState()
OmnivoreTheme() {
Column(
modifier = Modifier
.padding(top = 6.dp, start = 6.dp, end = 6.dp, bottom = 6.dp)
.padding(horizontal = 15.dp)
.verticalScroll(rememberScrollState())
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(top = 12.dp, bottom = 12.dp),
horizontalArrangement = Arrangement.Center
.padding(bottom = 15.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Text("Web Preferences")
}
Column(
modifier = Modifier
.verticalScroll(rememberScrollState())
) {
Stepper(
label = "Font Size:",
onIncrease = { webReaderViewModel.updateFontSize(isIncrease = true) },
onDecrease = { webReaderViewModel.updateFontSize(isIncrease = false) }
)
Stepper(
label = "Margin:",
onIncrease = { webReaderViewModel.updateMaxWidthPercentage(isIncrease = false) },
onDecrease = { webReaderViewModel.updateMaxWidthPercentage(isIncrease = true) }
)
Stepper(
label = "Line Spacing:",
onIncrease = { webReaderViewModel.updateLineSpacing(isIncrease = true) },
onDecrease = { webReaderViewModel.updateLineSpacing(isIncrease = false) }
)
Row(verticalAlignment = Alignment.CenterVertically) {
Text("High Contrast Text")
Spacer(modifier = Modifier.weight(1.0F))
Switch(
checked = highContrastTextSwitchState.value,
onCheckedChange = {
highContrastTextSwitchState.value = it
webReaderViewModel.updateHighContrastTextPreference(it)
}
)
}
Row(verticalAlignment = Alignment.CenterVertically) {
Text("Justify Text")
Spacer(modifier = Modifier.weight(1.0F))
Switch(
checked = justifyTextSwitchState.value,
onCheckedChange = {
justifyTextSwitchState.value = it
webReaderViewModel.updateJustifyText(it)
}
)
}
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.padding(vertical = 4.dp)
) {
Text("Theme:")
Spacer(modifier = Modifier.weight(1.0F))
SegmentedControl(
items = webReaderViewModel.systemThemeKeys,
initialSelectedItemIndex = webReaderViewModel.systemThemeKeys.indexOf(currentWebPreferences.storedThemePreference)
Text("Font", style = TextStyle(
fontSize = 15.sp,
fontWeight = FontWeight.Normal,
color = Color(red = 137, green = 137, blue = 137)
))
Spacer(modifier = Modifier.weight(1.0F))
Box {
OutlinedButton(
shape = RoundedCornerShape(4.dp),
onClick = { isFontListExpanded.value = true },
colors = ButtonDefaults.buttonColors(
contentColor = Color(red = 137, green = 137, blue = 137),
// containerColor = Color.Transparent,
),
) {
webReaderViewModel.updateStoredThemePreference(it, isDark)
Text(selectedWebFontRawValue.value)
}
}
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clickable(onClick = { isFontListExpanded.value = !isFontListExpanded.value })
) {
Text("Font Family")
Spacer(modifier = Modifier.weight(1.0F))
Icon(
imageVector =
if (isFontListExpanded.value)
Icons.Filled.KeyboardArrowDown
else
Icons.Filled.KeyboardArrowRight,
contentDescription = null
)
}
if (isFontListExpanded.value) {
WebFont.values().forEach {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clickable(onClick = {
webReaderViewModel.applyWebFont(it)
selectedWebFontRawValue.value = it.rawValue
})
if (isFontListExpanded.value) {
DropdownMenu(
expanded = isFontListExpanded.value,
onDismissRequest = { isFontListExpanded.value = false }
) {
Text(
it.displayText,
modifier = Modifier
.padding(top = 6.dp, start = 6.dp, end = 6.dp, bottom = 6.dp)
)
Spacer(modifier = Modifier.weight(1.0F))
if (it.rawValue == selectedWebFontRawValue.value) {
Icon(
imageVector = Icons.Filled.Check,
contentDescription = null
WebFont.values().forEach {
DropdownMenuItem(
text = { Text(it.displayText) },
onClick = {
webReaderViewModel.applyWebFont(it)
selectedWebFontRawValue.value = it.rawValue
isFontListExpanded.value = false
},
colors = MenuDefaults.itemColors(
textColor = Color(red = 137, green = 137, blue = 137),
)
)
}
}
}
}
}
Text("Font Size:", style = TextStyle(
fontSize = 15.sp,
fontWeight = FontWeight.Normal,
color = Color(red = 137, green = 137, blue = 137)
))
Slider(
value = fontSizeSliderValue,
onValueChange = {
fontSizeSliderValue = it
webReaderViewModel.setFontSize(it.toInt())
},
steps = 10,
valueRange = 8f..28f,
)
Text("Margin", style = TextStyle(
fontSize = 15.sp,
fontWeight = FontWeight.Normal,
color = Color(red = 137, green = 137, blue = 137)
))
Slider(
value = marginSliderValue,
onValueChange = {
marginSliderValue = it
webReaderViewModel.setMaxWidthPercentage(it.toInt())
},
steps = 4,
valueRange = 60f..100f,
)
Text("Line Spacing", style = TextStyle(
fontSize = 15.sp,
fontWeight = FontWeight.Normal,
color = Color(red = 137, green = 137, blue = 137)
))
Slider(
value = lineSpacingSliderValue,
onValueChange = {
lineSpacingSliderValue = it
webReaderViewModel.setLineHeight(it.toInt())
},
steps = 8,
valueRange = 100f..300f,
)
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.padding(vertical = 4.dp)
) {
Text("Theme:", style = TextStyle(
fontSize = 15.sp,
fontWeight = FontWeight.Normal,
color = Color(red = 137, green = 137, blue = 137)
))
Spacer(modifier = Modifier.weight(1.0F))
Text("Auto", style = TextStyle(
fontSize = 10.sp,
fontWeight = FontWeight.Normal,
color = Color(red = 137, green = 137, blue = 137)
))
Checkbox(
checked = themeState.value == "System",
onCheckedChange = {
if (it) {
themeState.value = "System"
webReaderViewModel.updateStoredThemePreference("System", isDark)
} else {
val newThemeKey = if (isDark) "Black" else "Light"
themeState.value = newThemeKey
webReaderViewModel.updateStoredThemePreference(newThemeKey, isDark)
}
})
}
Row(
modifier = Modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.Start,
) {
for(theme in Themes.values()) {
if (theme.themeKey != "System") {
val isSelected = theme.themeKey == themeState.value
Button(
onClick = {
themeState.value = theme.themeKey
webReaderViewModel.updateStoredThemePreference(theme.themeKey, isDark)
},
shape = CircleShape,
border = BorderStroke(3.dp, if (isSelected) colorResource(R.color.cta_yellow) else Color.Transparent),
modifier = Modifier.size(35.dp),
colors = ButtonDefaults.buttonColors(
containerColor = Color(theme.backgroundColor)
)
) {
}
Spacer(modifier = Modifier.weight(0.1F))
}
}
Spacer(modifier = Modifier.weight(2.0F))
}
Row(verticalAlignment = Alignment.CenterVertically) {
Text("High Contrast Text",
style = TextStyle(
fontSize = 15.sp,
fontWeight = FontWeight.Normal,
color = Color(red = 137, green = 137, blue = 137)
))
Spacer(modifier = Modifier.weight(1.0F))
Switch(
checked = highContrastTextSwitchState.value,
onCheckedChange = {
highContrastTextSwitchState.value = it
webReaderViewModel.updateHighContrastTextPreference(it)
}
)
}
Row(verticalAlignment = Alignment.CenterVertically) {
Text("Justify Text",
style = TextStyle(
fontSize = 15.sp,
fontWeight = FontWeight.Normal,
color = Color(red = 137, green = 137, blue = 137))
)
Spacer(modifier = Modifier.weight(1.0F))
Switch(
checked = justifyTextSwitchState.value,
onCheckedChange = {
justifyTextSwitchState.value = it
webReaderViewModel.updateJustifyText(it)
}
)
}
}
}
}
@Composable
fun Stepper(label: String, onIncrease: () -> Unit, onDecrease: () -> Unit) {

View file

@ -11,12 +11,14 @@ import androidx.activity.viewModels
import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.*
import androidx.compose.material.TopAppBar
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Menu
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.*
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
@ -25,6 +27,7 @@ import androidx.compose.runtime.*
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
@ -42,9 +45,8 @@ import app.omnivore.omnivore.ui.theme.OmnivoreTheme
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import dagger.hilt.android.AndroidEntryPoint
import kotlin.math.roundToInt
import androidx.navigation.compose.rememberNavController
import app.omnivore.omnivore.Routes
import app.omnivore.omnivore.ui.notebook.NotebookActivity
import kotlinx.coroutines.launch
@AndroidEntryPoint
@ -105,6 +107,7 @@ class WebReaderLoadingContainerActivity: ComponentActivity() {
}
}
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun WebReaderLoadingContainer(slug: String? = null, requestID: String? = null, onLibraryIconTap: (() -> Unit)? = null, webReaderViewModel: WebReaderViewModel) {
val onBackPressedDispatcher = LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher
@ -112,6 +115,10 @@ fun WebReaderLoadingContainer(slug: String? = null, requestID: String? = null, o
var isMenuExpanded by remember { mutableStateOf(false) }
var showWebPreferencesDialog by remember { mutableStateOf(false ) }
val isDark = isSystemInDarkTheme()
val currentWebPreferences = webReaderViewModel.storedWebPreferences(isDark)
val currentTheme = Themes.values().find { it.themeKey == currentWebPreferences.themeKey }
val webReaderParams: WebReaderParams? by webReaderViewModel.webReaderParamsLiveData.observeAsState(null)
val annotation: String? by webReaderViewModel.annotationLiveData.observeAsState(null)
val shouldPopView: Boolean by webReaderViewModel.shouldPopViewLiveData.observeAsState(false)
@ -123,6 +130,7 @@ fun WebReaderLoadingContainer(slug: String? = null, requestID: String? = null, o
webReaderViewModel.loadItem(slug = slug, requestID = requestID)
val context = LocalContext.current
val coroutineScope = rememberCoroutineScope()
val styledContent = webReaderParams?.let {
val webReaderContent = WebReaderContent(
@ -133,111 +141,169 @@ fun WebReaderLoadingContainer(slug: String? = null, requestID: String? = null, o
webReaderContent.styledContent()
} ?: null
Box(
modifier = Modifier
.fillMaxSize()
.systemBarsPadding()
.background(color = backgroundColor)
) {
if (styledContent != null) {
WebReader(
preferences = webReaderViewModel.storedWebPreferences(isSystemInDarkTheme()),
styledContent = styledContent,
webReaderViewModel = webReaderViewModel
)
val modalBottomSheetState = rememberModalBottomSheetState(
ModalBottomSheetValue.Hidden,
)
TopAppBar(
modifier = Modifier
.height(height = with(LocalDensity.current) {
toolbarHeightPx.roundToInt().toDp()
}),
backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
title = {},
navigationIcon = {
IconButton(onClick = {
onBackPressedDispatcher?.onBackPressed()
}) {
Icon(
imageVector = androidx.compose.material.icons.Icons.Filled.ArrowBack,
modifier = Modifier,
contentDescription = "Back"
)
}
},
actions = {
if (onLibraryIconTap != null) {
IconButton(onClick = { onLibraryIconTap() }) {
Icon(
imageVector = Icons.Default.Home,
contentDescription = null
)
}
}
webReaderParams?.let {
ModalBottomSheetLayout(
modifier = Modifier.statusBarsPadding(),
sheetBackgroundColor = Color.Transparent,
sheetState = modalBottomSheetState,
sheetContent = {
if (showWebPreferencesDialog) {
BottomSheetUI("Reader Preferences") {
WebPreferencesView(webReaderViewModel)
}
}
Spacer(modifier = Modifier.weight(1.0F))
}
) {
Box(
modifier = Modifier
.fillMaxSize()
.systemBarsPadding()
.background(color = backgroundColor)
) {
if (styledContent != null) {
WebReader(
preferences = webReaderViewModel.storedWebPreferences(isSystemInDarkTheme()),
styledContent = styledContent,
webReaderViewModel = webReaderViewModel
)
TopAppBar(
modifier = Modifier
.height(height = with(LocalDensity.current) {
toolbarHeightPx.roundToInt().toDp()
}),
backgroundColor = Color(currentTheme?.backgroundColor ?: 0xFFFFFFFF), // MaterialTheme.colorScheme.surfaceVariant,
title = {},
navigationIcon = {
IconButton(onClick = {
val intent = Intent(context, NotebookActivity::class.java)
intent.putExtra("SAVED_ITEM_ID", it.item.savedItemId)
context.startActivity(intent)
onBackPressedDispatcher?.onBackPressed()
}) {
Icon(
painter = painterResource(id = R.drawable.notebook),
imageVector = Icons.Filled.ArrowBack,
modifier = Modifier,
contentDescription = "Back"
)
}
},
actions = {
if (onLibraryIconTap != null) {
IconButton(onClick = { onLibraryIconTap() }) {
Icon(
imageVector = Icons.Default.Home,
contentDescription = null
)
}
}
webReaderParams?.let {
IconButton(onClick = {
val intent = Intent(context, NotebookActivity::class.java)
intent.putExtra("SAVED_ITEM_ID", it.item.savedItemId)
context.startActivity(intent)
}) {
Icon(
painter = painterResource(id = R.drawable.notebook),
contentDescription = null
)
}
}
IconButton(onClick = {
showWebPreferencesDialog = true
coroutineScope.launch {
modalBottomSheetState.show()
}
}) {
Icon(
painter = painterResource(id = R.drawable.format_letter_case),
contentDescription = null
)
}
}
IconButton(onClick = { showWebPreferencesDialog = true }) {
Icon(
painter = painterResource(id = R.drawable.format_letter_case),
contentDescription = null
)
}
IconButton(onClick = { isMenuExpanded = true }) {
Icon(
painter = painterResource(id = R.drawable.dots_horizontal),
contentDescription = null
)
}
SavedItemContextMenu(
isExpanded = isMenuExpanded,
isArchived = webReaderParams!!.item.isArchived,
onDismiss = { isMenuExpanded = false },
actionHandler = {
webReaderViewModel.handleSavedItemAction(
webReaderParams!!.item.savedItemId,
it
IconButton(onClick = { isMenuExpanded = true }) {
Icon(
painter = painterResource(id = R.drawable.dots_horizontal),
contentDescription = null
)
}
SavedItemContextMenu(
isExpanded = isMenuExpanded,
isArchived = webReaderParams!!.item.isArchived,
onDismiss = { isMenuExpanded = false },
actionHandler = {
webReaderViewModel.handleSavedItemAction(
webReaderParams!!.item.savedItemId,
it
)
}
)
}
)
if (annotation != null) {
AnnotationEditView(
initialAnnotation = annotation!!,
onSave = {
webReaderViewModel.saveAnnotation(it)
},
onCancel = {
webReaderViewModel.cancelAnnotationEdit()
}
)
}
)
if (showWebPreferencesDialog) {
WebPreferencesDialog(
onDismiss = {
showWebPreferencesDialog = false
},
webReaderViewModel = webReaderViewModel
)
WebReaderLabelsSelectionSheet(webReaderViewModel)
}
if (annotation != null) {
AnnotationEditView(
initialAnnotation = annotation!!,
onSave = {
webReaderViewModel.saveAnnotation(it)
},
onCancel = {
webReaderViewModel.cancelAnnotationEdit()
}
)
}
WebReaderLabelsSelectionSheet(webReaderViewModel)
}
LaunchedEffect(shouldPopView) {
if (shouldPopView) {
onBackPressedDispatcher?.onBackPressed()
LaunchedEffect(shouldPopView) {
if (shouldPopView) {
onBackPressedDispatcher?.onBackPressed()
}
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class)
@Composable
fun BottomSheetUI(title: String?, content: @Composable () -> Unit) {
val onBackPressedDispatcher = LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher
Box(
modifier = Modifier
.wrapContentHeight()
.fillMaxWidth()
.clip(RoundedCornerShape(topEnd = 20.dp, topStart = 20.dp))
.background(Color.White)
.statusBarsPadding()
) {
Scaffold(
topBar = {
TopAppBar(
title = { Text(title ?: "") },
modifier = Modifier.statusBarsPadding(),
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.background
),
// navigationIcon = {
// IconButton(onClick = {
// onBackPressedDispatcher?.onBackPressed()
// }) {
// Icon(
// imageVector = Icons.Filled.ArrowBack,
// modifier = Modifier,
// contentDescription = "Back"
// )
// }
// }
)
}
) { paddingValues ->
Box(modifier = Modifier
.padding(paddingValues)
.fillMaxSize()) {
content()
}
}
}

View file

@ -37,6 +37,15 @@ data class AnnotationWebViewMessage(
val annotation: String?
)
enum class Themes(val themeKey: String, val backgroundColor: Long) {
SYSTEM("System", 0xFFFFFFFF),
LIGHT("Light", 0xFF000000),
SEPIA("Sepia", 0xFFFBF0D9),
DARK("Dark", 0xFF2a2a2a),
APOLLO("Apollo", 0xFF6A6968),
BLACK("Black", 0xFFFFFFFF),
}
@HiltViewModel
class WebReaderViewModel @Inject constructor(
private val datastoreRepo: DatastoreRepository,
@ -56,9 +65,6 @@ class WebReaderViewModel @Inject constructor(
val showLabelsSelectionSheetLiveData = MutableLiveData(false)
val savedItemLabelsLiveData = dataService.db.savedItemLabelDao().getSavedItemLabelsLiveData()
// "Sepia", "Apollo",
val systemThemeKeys = listOf("Light", "Black", "System")
var hasTappedExistingHighlight = false
var lastTapCoordinates: TapCoordinates? = null
private var isLoading = false
@ -288,59 +294,37 @@ class WebReaderViewModel @Inject constructor(
return storedThemePreference
}
fun updateStoredThemePreference(index: Int, isDarkMode: Boolean) {
val newThemeKey = themeKey(isDarkMode, systemThemeKeys[index])
fun updateStoredThemePreference(newThemeKey: String, isDarkMode: Boolean) {
Log.d("theme", "Setting theme key: ${newThemeKey}")
runBlocking {
datastoreRepo.putString(DatastoreKeys.preferredTheme, systemThemeKeys[index])
datastoreRepo.putString(DatastoreKeys.preferredTheme, newThemeKey)
}
val script = "var event = new Event('updateTheme');event.themeName = '$newThemeKey';document.dispatchEvent(event);"
enqueueScript(script)
}
fun updateFontSize(isIncrease: Boolean) {
val delta = if (isIncrease) 2 else -2
var newFontSize: Int
fun setFontSize(newFontSize: Int) {
runBlocking {
val storedFontSize = datastoreRepo.getInt(DatastoreKeys.preferredWebFontSize)
newFontSize = ((storedFontSize ?: 12) + delta).coerceIn(8, 28)
datastoreRepo.putInt(DatastoreKeys.preferredWebFontSize, newFontSize)
}
// Get value from data store and then update it
val script = "var event = new Event('updateFontSize');event.fontSize = '$newFontSize';document.dispatchEvent(event);"
enqueueScript(script)
}
fun updateMaxWidthPercentage(isIncrease: Boolean) {
val delta = if (isIncrease) 10 else -10
var newMaxWidthPercentageValue: Int
fun setMaxWidthPercentage(newMaxWidthPercentageValue: Int) {
runBlocking {
val storedWidth = datastoreRepo.getInt(DatastoreKeys.preferredWebMaxWidthPercentage)
newMaxWidthPercentageValue = ((storedWidth ?: 100) + delta).coerceIn(40, 100)
datastoreRepo.putInt(DatastoreKeys.preferredWebMaxWidthPercentage, newMaxWidthPercentageValue)
}
// Get value from data store and then update it
val script = "var event = new Event('updateMaxWidthPercentage');event.maxWidthPercentage = '$newMaxWidthPercentageValue';document.dispatchEvent(event);"
enqueueScript(script)
}
fun updateLineSpacing(isIncrease: Boolean) {
val delta = if (isIncrease) 25 else -25
var newLineHeight: Int
fun setLineHeight(newLineHeight: Int) {
runBlocking {
val storedHeight = datastoreRepo.getInt(DatastoreKeys.preferredWebLineHeight)
newLineHeight = ((storedHeight ?: 150) + delta).coerceIn(100, 300)
datastoreRepo.putInt(DatastoreKeys.preferredWebLineHeight, newLineHeight)
}
// Get value from data store and then update it
val script = "var event = new Event('updateLineHeight');event.lineHeight = '$newLineHeight';document.dispatchEvent(event);"
enqueueScript(script)
}