working and rendering, but pretty bad
This commit is contained in:
@@ -30,19 +30,48 @@ import timber.log.Timber
|
|||||||
import java.nio.charset.Charset
|
import java.nio.charset.Charset
|
||||||
import nl.siegmann.epublib.domain.MediaType
|
import nl.siegmann.epublib.domain.MediaType
|
||||||
import nl.siegmann.epublib.domain.Resource
|
import nl.siegmann.epublib.domain.Resource
|
||||||
|
import inhale.rip.epook.data.Settings
|
||||||
|
|
||||||
// Update the JavaScript for accurate page calculation
|
// Update the JavaScript for accurate page calculation
|
||||||
private const val PAGE_CALCULATION_JS = """
|
private const val PAGE_CALCULATION_JS = """
|
||||||
(function() {
|
(function() {
|
||||||
const content = document.body;
|
const content = document.body;
|
||||||
const contentWidth = content.scrollWidth;
|
const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
|
||||||
const pageWidth = window.innerWidth;
|
const pageWidth = window.innerWidth;
|
||||||
const totalPages = Math.max(1, Math.ceil(contentWidth / pageWidth));
|
const totalWidth = document.documentElement.scrollWidth;
|
||||||
console.log('Content width:', contentWidth, 'Page width:', pageWidth, 'Total pages:', totalPages);
|
const currentPage = Math.floor(scrollLeft / pageWidth) + 1;
|
||||||
return totalPages;
|
const totalPages = Math.max(1, Math.ceil(totalWidth / pageWidth));
|
||||||
|
return JSON.stringify({
|
||||||
|
currentPage: currentPage,
|
||||||
|
totalPages: totalPages
|
||||||
|
});
|
||||||
})();
|
})();
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
// Add this function to handle chapter navigation
|
||||||
|
private const val SCROLL_TO_CHAPTER_JS = """
|
||||||
|
function scrollToChapter(chapterIndex) {
|
||||||
|
const chapter = document.getElementById('chapter_' + chapterIndex);
|
||||||
|
if (chapter) {
|
||||||
|
chapter.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
// Add this to track WebView scroll position
|
||||||
|
class WebViewScrollClient : WebViewClient() {
|
||||||
|
var onPageLoaded: ((WebView) -> Unit)? = null
|
||||||
|
|
||||||
|
override fun onPageFinished(view: WebView?, url: String?) {
|
||||||
|
super.onPageFinished(view, url)
|
||||||
|
view?.let { webView ->
|
||||||
|
onPageLoaded?.invoke(webView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun ReaderScreen(
|
fun ReaderScreen(
|
||||||
@@ -70,6 +99,7 @@ fun ReaderScreen(
|
|||||||
var totalPages by remember { mutableStateOf(1) }
|
var totalPages by remember { mutableStateOf(1) }
|
||||||
var bookContent by remember { mutableStateOf("") }
|
var bookContent by remember { mutableStateOf("") }
|
||||||
var webViewRef by remember { mutableStateOf<WebView?>(null) }
|
var webViewRef by remember { mutableStateOf<WebView?>(null) }
|
||||||
|
var webViewClient = remember { WebViewScrollClient() }
|
||||||
|
|
||||||
// Move processChapterHtml outside the Composable
|
// Move processChapterHtml outside the Composable
|
||||||
fun processChapterHtml(rawHtml: String, chapterIndex: Int): String {
|
fun processChapterHtml(rawHtml: String, chapterIndex: Int): String {
|
||||||
@@ -104,7 +134,8 @@ fun ReaderScreen(
|
|||||||
Timber.d("📚 Resource href: ${resource.href}")
|
Timber.d("📚 Resource href: ${resource.href}")
|
||||||
Timber.d("📚 Resource media type: ${resource.mediaType}")
|
Timber.d("📚 Resource media type: ${resource.mediaType}")
|
||||||
|
|
||||||
if (resource.mediaType == MediaType.XHTML) {
|
if (resource.mediaType.toString().contains("application/xhtml+xml") ||
|
||||||
|
resource.mediaType.toString().contains("text/html")) {
|
||||||
val rawHtml = String(resource.data, Charset.forName("UTF-8"))
|
val rawHtml = String(resource.data, Charset.forName("UTF-8"))
|
||||||
Timber.d("📚 Raw HTML length: ${rawHtml.length}")
|
Timber.d("📚 Raw HTML length: ${rawHtml.length}")
|
||||||
|
|
||||||
@@ -166,237 +197,235 @@ fun ReaderScreen(
|
|||||||
}
|
}
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
|
|
||||||
BackHandler(enabled = showSettings || showChapterList) {
|
BackHandler {
|
||||||
when {
|
scope.launch {
|
||||||
showSettings -> showSettings = false
|
settingsStore.saveSettings(Settings(
|
||||||
showChapterList -> showChapterList = false
|
currentPage = currentPage,
|
||||||
|
fontSize = fontSize,
|
||||||
|
lineHeight = lineHeight,
|
||||||
|
padding = padding,
|
||||||
|
fontFamily = fontFamily,
|
||||||
|
isDarkMode = isDarkMode
|
||||||
|
))
|
||||||
|
}
|
||||||
|
onNavigateBack()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the calculateCurrentPage function to use a callback
|
||||||
|
fun calculateCurrentPage(webView: WebView) {
|
||||||
|
webView.evaluateJavascript("""
|
||||||
|
(function() {
|
||||||
|
const height = document.documentElement.scrollHeight;
|
||||||
|
const scrollTop = document.documentElement.scrollTop;
|
||||||
|
const clientHeight = document.documentElement.clientHeight;
|
||||||
|
const totalPages = Math.ceil(height / clientHeight);
|
||||||
|
const currentPage = Math.ceil((scrollTop + clientHeight) / clientHeight);
|
||||||
|
return JSON.stringify({currentPage: currentPage, totalPages: totalPages});
|
||||||
|
})()
|
||||||
|
""".trimIndent()) { result ->
|
||||||
|
result?.let {
|
||||||
|
try {
|
||||||
|
val jsonStr = result.removeSurrounding("\"")
|
||||||
|
val regex = """.*"currentPage":(\d+).*"totalPages":(\d+).*""".toRegex()
|
||||||
|
regex.find(jsonStr)?.let { matchResult ->
|
||||||
|
val (current, total) = matchResult.destructured
|
||||||
|
// Update state values
|
||||||
|
scope.launch {
|
||||||
|
currentPage = current.toInt()
|
||||||
|
totalPages = total.toInt()
|
||||||
|
Timber.d("📚 Current page: $currentPage of $totalPages")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e(e, "Error calculating current page")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Scaffold(
|
// Update webViewClient callback
|
||||||
topBar = {
|
webViewClient.onPageLoaded = { webView ->
|
||||||
AnimatedVisibility(
|
calculateCurrentPage(webView)
|
||||||
visible = showControls,
|
}
|
||||||
enter = slideInVertically() + fadeIn(),
|
|
||||||
exit = slideOutVertically() + fadeOut()
|
// Update the HTML content creation
|
||||||
) {
|
fun createHtmlContent(): String = """
|
||||||
TopAppBar(
|
<!DOCTYPE html>
|
||||||
title = {
|
<html>
|
||||||
Column {
|
<head>
|
||||||
Text("Chapter ${currentChapter + 1}")
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
Text(
|
<style>
|
||||||
"Page $currentPage of $totalPages",
|
html {
|
||||||
style = MaterialTheme.typography.bodyMedium
|
scroll-snap-type: x mandatory;
|
||||||
)
|
overflow-x: scroll;
|
||||||
}
|
scroll-behavior: smooth;
|
||||||
},
|
}
|
||||||
navigationIcon = {
|
body {
|
||||||
IconButton(onClick = onNavigateBack) {
|
font-family: '$fontFamily';
|
||||||
Icon(Icons.AutoMirrored.Filled.ArrowBack, "Back")
|
font-size: ${fontSize}px;
|
||||||
}
|
line-height: ${lineHeight};
|
||||||
},
|
padding: ${padding}px;
|
||||||
actions = {
|
margin: 0;
|
||||||
IconButton(onClick = { showChapterList = true }) {
|
background-color: ${if (isDarkMode) "#1c1c1c" else "#ffffff"};
|
||||||
Icon(Icons.Default.List, "Chapters")
|
color: ${if (isDarkMode) "#ffffff" else "#000000"};
|
||||||
}
|
display: flex;
|
||||||
IconButton(onClick = { showSettings = true }) {
|
flex-direction: row;
|
||||||
Icon(Icons.Default.Settings, "Settings")
|
width: fit-content;
|
||||||
}
|
}
|
||||||
IconButton(onClick = { isDarkMode = !isDarkMode }) {
|
.chapter {
|
||||||
Icon(
|
width: calc(100vw - ${padding * 2}px);
|
||||||
if (isDarkMode) Icons.Default.LightMode else Icons.Default.DarkMode,
|
height: calc(100vh - ${padding * 2}px);
|
||||||
"Toggle theme"
|
overflow: hidden;
|
||||||
)
|
scroll-snap-align: start;
|
||||||
}
|
break-inside: avoid;
|
||||||
}
|
flex-shrink: 0;
|
||||||
)
|
}
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
display: block;
|
||||||
|
margin: 1em auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
$bookContent
|
||||||
|
<script>$SCROLL_TO_CHAPTER_JS</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
// Update chapter navigation
|
||||||
|
fun navigateToChapter(index: Int) {
|
||||||
|
webViewRef?.evaluateJavascript("scrollToChapter($index);") { result ->
|
||||||
|
if (result == "true") {
|
||||||
|
currentChapter = index
|
||||||
|
showChapterList = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
) { padding ->
|
}
|
||||||
Box(modifier = Modifier.padding(padding)) {
|
|
||||||
AndroidView(
|
|
||||||
factory = { context ->
|
|
||||||
WebView(context).apply {
|
|
||||||
settings.apply {
|
|
||||||
javaScriptEnabled = true
|
|
||||||
useWideViewPort = true
|
|
||||||
loadWithOverviewMode = true
|
|
||||||
defaultFontSize = fontSize.toInt()
|
|
||||||
Timber.d("📚 WebView settings configured")
|
|
||||||
}
|
|
||||||
|
|
||||||
webViewClient = object : WebViewClient() {
|
|
||||||
override fun onPageStarted(view: WebView?, url: String?, favicon: android.graphics.Bitmap?) {
|
|
||||||
super.onPageStarted(view, url, favicon)
|
|
||||||
Timber.d("📚 WebView page load started")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPageFinished(view: WebView?, url: String?) {
|
// Update the WebView setup
|
||||||
super.onPageFinished(view, url)
|
AndroidView(
|
||||||
Timber.d("📚 WebView page load finished")
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
factory = { context ->
|
||||||
// Add a slight delay to ensure content is fully laid out
|
WebView(context).apply {
|
||||||
view?.postDelayed({
|
settings.apply {
|
||||||
Timber.d("📚 Starting page calculation")
|
javaScriptEnabled = true
|
||||||
view.evaluateJavascript(PAGE_CALCULATION_JS) { result ->
|
defaultFontSize = fontSize.toInt()
|
||||||
try {
|
domStorageEnabled = true
|
||||||
val pages = result.toInt()
|
allowFileAccess = true
|
||||||
Timber.d("📚 Page calculation complete - Total pages: $pages")
|
allowContentAccess = true
|
||||||
totalPages = pages
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Timber.e(e, "📚 Error calculating pages")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log the current HTML content for debugging
|
|
||||||
view.evaluateJavascript(
|
|
||||||
"(function() { return document.documentElement.outerHTML; })()",
|
|
||||||
) { result ->
|
|
||||||
Timber.d("📚 Current HTML content length: ${result.length}")
|
|
||||||
Timber.d("📚 First 100 chars of HTML: ${result.take(100)}")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scroll to last position
|
|
||||||
view.evaluateJavascript(
|
|
||||||
"window.scrollTo({left: (${currentPage - 1}) * window.innerWidth, behavior: 'auto'})",
|
|
||||||
null
|
|
||||||
)
|
|
||||||
}, 500)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onReceivedError(
|
|
||||||
view: WebView?,
|
|
||||||
errorCode: Int,
|
|
||||||
description: String?,
|
|
||||||
failingUrl: String?
|
|
||||||
) {
|
|
||||||
super.onReceivedError(view, errorCode, description, failingUrl)
|
|
||||||
Timber.e("📚 WebView error: $errorCode - $description")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Timber.d("📚 Loading content into WebView")
|
|
||||||
loadDataWithBaseURL(
|
|
||||||
null,
|
|
||||||
"""
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
|
|
||||||
<style>$css</style>
|
|
||||||
</head>
|
|
||||||
<body>$bookContent</body>
|
|
||||||
</html>
|
|
||||||
""".trimIndent(),
|
|
||||||
"text/html",
|
|
||||||
"UTF-8",
|
|
||||||
null
|
|
||||||
)
|
|
||||||
webViewRef = this
|
|
||||||
}
|
|
||||||
},
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.pointerInput(Unit) {
|
|
||||||
detectTapGestures { offset ->
|
|
||||||
val screenWidth = this.size.width.toFloat()
|
|
||||||
when {
|
|
||||||
offset.x < screenWidth * 0.3f && currentPage > 1 -> {
|
|
||||||
currentPage--
|
|
||||||
webViewRef?.evaluateJavascript(
|
|
||||||
"window.scrollTo({left: (${currentPage - 1}) * window.innerWidth, behavior: 'smooth'})",
|
|
||||||
null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
offset.x > screenWidth * 0.7f && currentPage < totalPages -> {
|
|
||||||
currentPage++
|
|
||||||
webViewRef?.evaluateJavascript(
|
|
||||||
"window.scrollTo({left: (${currentPage - 1}) * window.innerWidth, behavior: 'smooth'})",
|
|
||||||
null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
showControls = !showControls
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Settings sheet
|
|
||||||
if (showSettings) {
|
|
||||||
ModalBottomSheet(
|
|
||||||
onDismissRequest = { showSettings = false }
|
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(16.dp)
|
|
||||||
) {
|
|
||||||
Text("Reading Settings", style = MaterialTheme.typography.titleLarge)
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
|
||||||
|
|
||||||
Text("Font Size: $fontSize", style = MaterialTheme.typography.bodyMedium)
|
|
||||||
Slider(
|
|
||||||
value = fontSize,
|
|
||||||
onValueChange = {
|
|
||||||
fontSize = it
|
|
||||||
// Update WebView font size
|
|
||||||
webViewRef?.evaluateJavascript(
|
|
||||||
"document.body.style.fontSize = '${fontSize}px'",
|
|
||||||
null
|
|
||||||
)
|
|
||||||
},
|
|
||||||
valueRange = 12f..24f,
|
|
||||||
steps = 11
|
|
||||||
)
|
|
||||||
|
|
||||||
// Similar sliders for lineHeight, padding, brightness
|
|
||||||
// Font family selector
|
|
||||||
// Color theme selector
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
webViewClient = webViewClient
|
||||||
// Chapter list
|
|
||||||
if (showChapterList) {
|
setOnScrollChangeListener { _, _, _, _, _ ->
|
||||||
ModalBottomSheet(
|
evaluateJavascript(PAGE_CALCULATION_JS) { result ->
|
||||||
onDismissRequest = { showChapterList = false }
|
result?.let {
|
||||||
) {
|
try {
|
||||||
LazyColumn(
|
val jsonStr = result.removeSurrounding("\"")
|
||||||
modifier = Modifier.fillMaxWidth()
|
val regex = """.*"currentPage":(\d+).*"totalPages":(\d+).*""".toRegex()
|
||||||
) {
|
regex.find(jsonStr)?.let { matchResult ->
|
||||||
items(chapters) { chapter ->
|
val (current, total) = matchResult.destructured
|
||||||
ListItem(
|
scope.launch {
|
||||||
headlineContent = { Text(chapter) },
|
currentPage = current.toInt()
|
||||||
modifier = Modifier.clickable {
|
totalPages = total.toInt()
|
||||||
// Navigate to chapter
|
}
|
||||||
webViewRef?.evaluateJavascript(
|
|
||||||
"document.getElementById('chapter_$currentChapter').scrollIntoView()",
|
|
||||||
null
|
|
||||||
)
|
|
||||||
showChapterList = false
|
|
||||||
}
|
}
|
||||||
)
|
} catch (e: Exception) {
|
||||||
|
Timber.e(e, "Error calculating page")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
// Brightness overlay
|
update = { webView ->
|
||||||
Box(
|
webViewRef = webView
|
||||||
modifier = Modifier
|
val htmlContent = """
|
||||||
.fillMaxSize()
|
<!DOCTYPE html>
|
||||||
.background(Color.Black.copy(alpha = 1f - brightness))
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: '$fontFamily';
|
||||||
|
font-size: ${fontSize}px;
|
||||||
|
line-height: ${lineHeight};
|
||||||
|
padding: ${padding}px;
|
||||||
|
margin: 0;
|
||||||
|
background-color: ${if (isDarkMode) "#1c1c1c" else "#ffffff"};
|
||||||
|
color: ${if (isDarkMode) "#ffffff" else "#000000"};
|
||||||
|
}
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
display: block;
|
||||||
|
margin: 1em auto;
|
||||||
|
}
|
||||||
|
.chapter {
|
||||||
|
margin-bottom: 2em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
$bookContent
|
||||||
|
<script>$SCROLL_TO_CHAPTER_JS</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
webView.loadDataWithBaseURL(
|
||||||
|
"file:///android_asset/",
|
||||||
|
htmlContent,
|
||||||
|
"text/html",
|
||||||
|
"UTF-8",
|
||||||
|
null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Update chapter list dialog
|
||||||
|
if (showChapterList) {
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = { showChapterList = false },
|
||||||
|
title = { Text("Chapters") },
|
||||||
|
text = {
|
||||||
|
LazyColumn {
|
||||||
|
items(chapters) { chapter ->
|
||||||
|
Text(
|
||||||
|
text = chapter,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clickable {
|
||||||
|
val index = chapters.indexOf(chapter)
|
||||||
|
navigateToChapter(index)
|
||||||
|
}
|
||||||
|
.padding(16.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(onClick = { showChapterList = false }) {
|
||||||
|
Text("Close")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save reading progress when leaving
|
// Save reading progress when leaving
|
||||||
DisposableEffect(Unit) {
|
DisposableEffect(Unit) {
|
||||||
onDispose {
|
onDispose {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
settingsStore.saveLastPosition(bookId, currentPage)
|
settingsStore.saveSettings(Settings(
|
||||||
|
currentPage = currentPage,
|
||||||
|
fontSize = fontSize,
|
||||||
|
lineHeight = lineHeight,
|
||||||
|
padding = padding,
|
||||||
|
fontFamily = fontFamily,
|
||||||
|
isDarkMode = isDarkMode
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,83 +2,94 @@ package inhale.rip.epook.data
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.datastore.core.DataStore
|
import androidx.datastore.core.DataStore
|
||||||
import androidx.datastore.preferences.core.Preferences
|
import androidx.datastore.preferences.core.*
|
||||||
import androidx.datastore.preferences.core.edit
|
|
||||||
import androidx.datastore.preferences.core.floatPreferencesKey
|
|
||||||
import androidx.datastore.preferences.core.intPreferencesKey
|
|
||||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
|
||||||
import androidx.datastore.preferences.preferencesDataStore
|
import androidx.datastore.preferences.preferencesDataStore
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
|
||||||
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
|
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
|
||||||
|
|
||||||
|
data class Settings(
|
||||||
|
val currentPage: Int = 1,
|
||||||
|
val fontSize: Float = 16f,
|
||||||
|
val lineHeight: Float = 1.5f,
|
||||||
|
val padding: Float = 16f,
|
||||||
|
val fontFamily: String = "Roboto",
|
||||||
|
val isDarkMode: Boolean = false
|
||||||
|
)
|
||||||
|
|
||||||
class SettingsStore(private val context: Context) {
|
class SettingsStore(private val context: Context) {
|
||||||
private object PreferencesKeys {
|
private object PreferencesKeys {
|
||||||
|
val CURRENT_PAGE = intPreferencesKey("current_page")
|
||||||
val FONT_SIZE = floatPreferencesKey("font_size")
|
val FONT_SIZE = floatPreferencesKey("font_size")
|
||||||
val LINE_HEIGHT = floatPreferencesKey("line_height")
|
val LINE_HEIGHT = floatPreferencesKey("line_height")
|
||||||
val PADDING = floatPreferencesKey("padding")
|
val PADDING = floatPreferencesKey("padding")
|
||||||
val FONT_FAMILY = stringPreferencesKey("font_family")
|
val FONT_FAMILY = stringPreferencesKey("font_family")
|
||||||
fun lastPositionKey(bookId: String) = intPreferencesKey("last_position_$bookId")
|
val IS_DARK_MODE = booleanPreferencesKey("is_dark_mode")
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getFontSize(): Float {
|
suspend fun getFontSize(): Float {
|
||||||
return context.dataStore.data.map { preferences ->
|
return context.dataStore.data.first()[PreferencesKeys.FONT_SIZE] ?: 16f
|
||||||
preferences[PreferencesKeys.FONT_SIZE] ?: 16f
|
|
||||||
}.first()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getLineHeight(): Float {
|
suspend fun getLineHeight(): Float {
|
||||||
return context.dataStore.data.map { preferences ->
|
return context.dataStore.data.first()[PreferencesKeys.LINE_HEIGHT] ?: 1.5f
|
||||||
preferences[PreferencesKeys.LINE_HEIGHT] ?: 1.5f
|
|
||||||
}.first()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getPadding(): Float {
|
suspend fun getPadding(): Float {
|
||||||
return context.dataStore.data.map { preferences ->
|
return context.dataStore.data.first()[PreferencesKeys.PADDING] ?: 16f
|
||||||
preferences[PreferencesKeys.PADDING] ?: 16f
|
|
||||||
}.first()
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun updateFontSize(size: Float) {
|
|
||||||
context.dataStore.edit { preferences ->
|
|
||||||
preferences[PreferencesKeys.FONT_SIZE] = size
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun updateLineHeight(height: Float) {
|
|
||||||
context.dataStore.edit { preferences ->
|
|
||||||
preferences[PreferencesKeys.LINE_HEIGHT] = height
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun updatePadding(padding: Float) {
|
|
||||||
context.dataStore.edit { preferences ->
|
|
||||||
preferences[PreferencesKeys.PADDING] = padding
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getFontFamily(): String {
|
suspend fun getFontFamily(): String {
|
||||||
return context.dataStore.data.map { preferences ->
|
return context.dataStore.data.first()[PreferencesKeys.FONT_FAMILY] ?: "Roboto"
|
||||||
preferences[PreferencesKeys.FONT_FAMILY] ?: "Roboto"
|
|
||||||
}.first()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateFontFamily(family: String) {
|
suspend fun updateFontSize(value: Float) {
|
||||||
context.dataStore.edit { preferences ->
|
context.dataStore.edit { preferences ->
|
||||||
preferences[PreferencesKeys.FONT_FAMILY] = family
|
preferences[PreferencesKeys.FONT_SIZE] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getLastPosition(bookId: String): Int? {
|
suspend fun updateLineHeight(value: Float) {
|
||||||
return context.dataStore.data.map { preferences ->
|
context.dataStore.edit { preferences ->
|
||||||
preferences[PreferencesKeys.lastPositionKey(bookId)]
|
preferences[PreferencesKeys.LINE_HEIGHT] = value
|
||||||
}.first()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun saveLastPosition(bookId: String, position: Int) {
|
suspend fun updatePadding(value: Float) {
|
||||||
context.dataStore.edit { preferences ->
|
context.dataStore.edit { preferences ->
|
||||||
preferences[PreferencesKeys.lastPositionKey(bookId)] = position
|
preferences[PreferencesKeys.PADDING] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun updateFontFamily(value: String) {
|
||||||
|
context.dataStore.edit { preferences ->
|
||||||
|
preferences[PreferencesKeys.FONT_FAMILY] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun loadSettings(): Settings {
|
||||||
|
return context.dataStore.data.first().let { preferences ->
|
||||||
|
Settings(
|
||||||
|
currentPage = preferences[PreferencesKeys.CURRENT_PAGE] ?: 1,
|
||||||
|
fontSize = preferences[PreferencesKeys.FONT_SIZE] ?: 16f,
|
||||||
|
lineHeight = preferences[PreferencesKeys.LINE_HEIGHT] ?: 1.5f,
|
||||||
|
padding = preferences[PreferencesKeys.PADDING] ?: 16f,
|
||||||
|
fontFamily = preferences[PreferencesKeys.FONT_FAMILY] ?: "Roboto",
|
||||||
|
isDarkMode = preferences[PreferencesKeys.IS_DARK_MODE] ?: false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun saveSettings(settings: Settings) {
|
||||||
|
context.dataStore.edit { preferences ->
|
||||||
|
preferences[PreferencesKeys.CURRENT_PAGE] = settings.currentPage
|
||||||
|
preferences[PreferencesKeys.FONT_SIZE] = settings.fontSize
|
||||||
|
preferences[PreferencesKeys.LINE_HEIGHT] = settings.lineHeight
|
||||||
|
preferences[PreferencesKeys.PADDING] = settings.padding
|
||||||
|
preferences[PreferencesKeys.FONT_FAMILY] = settings.fontFamily
|
||||||
|
preferences[PreferencesKeys.IS_DARK_MODE] = settings.isDarkMode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user