Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ class AndroidWindowState(
insetsController.show(statusBars() or navigationBars())
insetsController.systemBarsBehavior = BEHAVIOR_DEFAULT
isFullscreen.value = false

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,7 @@ interface EpubReaderSettingsRepository {

suspend fun getTtsuReaderSettings(): TtsuReaderSettings
suspend fun putTtsuReaderSettings(settings: TtsuReaderSettings)

fun getFullscreenEnabled(): Flow<Boolean>
fun putFullscreenEnabled(enabled: Boolean)
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ data class EpubReaderSettings(
val readerType: EpubReaderType = EpubReaderType.TTSU_EPUB,
val komgaReaderSettings: JsonObject = buildJsonObject { },
val ttsuReaderSettings: TtsuReaderSettings = TtsuReaderSettings(),
val fullscreenEnabled: Boolean = true,
)
Original file line number Diff line number Diff line change
@@ -1,25 +1,38 @@
package snd.komelia.db

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch

class SettingsStateWrapper<T>(
settings: T,
private val saveSettings: suspend (T) -> Unit
) {
private val _state: MutableStateFlow<T> = MutableStateFlow(settings)
val state = _state.asStateFlow()
private val persistScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)

inline fun <R> mapState(crossinline transform: suspend (value: T) -> R): Flow<R> {
return state.map(transform).distinctUntilChanged()
}

// Updates in-memory state immediately and fires a background DB save.
// Use this when the caller cannot wait for the save (e.g. non-suspend context).
fun update(transform: (T) -> T) {
val transformed = transform(_state.value)
_state.value = transformed
persistScope.launch { saveSettings(transformed) }
}

suspend fun transform(transform: suspend (settings: T) -> T) {
val transformed = transform(_state.value)
saveSettings(transformed)
_state.value = transformed
saveSettings(transformed)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,12 @@ class EpubReaderSettingsRepositoryWrapper(
override suspend fun putTtsuReaderSettings(settings: TtsuReaderSettings) {
return wrapper.transform { it.copy(ttsuReaderSettings = settings) }
}

override fun getFullscreenEnabled(): Flow<Boolean> {
return wrapper.mapState { it.fullscreenEnabled }
}

override fun putFullscreenEnabled(enabled: Boolean) {
wrapper.update { it.copy(fullscreenEnabled = enabled) }
}
}
12 changes: 12 additions & 0 deletions komelia-infra/database/sqlite/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,15 @@ tasks.register<Sync>("android-x86-ExtractSqliteLib") {
from(file)
into("$projectDir/src/androidMain/jniLibs/x86")
}

afterEvaluate {
val extractTasks = listOf(
"android-arm64-ExtractSqliteLib",
"android-armv7a-ExtractSqliteLib",
"android-x86_64-ExtractSqliteLib",
"android-x86-ExtractSqliteLib"
)
tasks.named("preBuild") {
dependsOn(extractTasks)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE EpubReaderSettings
ADD COLUMN fullscreen_enabled BOOLEAN DEFAULT 1 NOT NULL;
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class AppMigrations : MigrationResourcesProvider() {
"V10__komf_settings.sql",
"V11__home_filters.sql",
"V12__offline_mode.sql",
"V13__epub_fullscreen_setting.sql",
)

override suspend fun getMigration(name: String): ByteArray? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ class ExposedEpubReaderSettingsRepository(database: Database) : ExposedRepositor
EpubReaderSettings(
readerType = EpubReaderType.valueOf(it[EpubReaderSettingsTable.readerType]),
komgaReaderSettings = it[EpubReaderSettingsTable.komgaSettingsJson],
ttsuReaderSettings = it[EpubReaderSettingsTable.ttsuSettingsJson]
ttsuReaderSettings = it[EpubReaderSettingsTable.ttsuSettingsJson],
fullscreenEnabled = it[EpubReaderSettingsTable.fullscreenEnabled],
)
}
}
Expand All @@ -34,6 +35,7 @@ class ExposedEpubReaderSettingsRepository(database: Database) : ExposedRepositor
it[readerType] = settings.readerType.name
it[komgaSettingsJson] = settings.komgaReaderSettings
it[ttsuSettingsJson] = settings.ttsuReaderSettings
it[fullscreenEnabled] = settings.fullscreenEnabled
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ object EpubReaderSettingsTable : Table("EpubReaderSettings") {
val readerType = text("reader_type")
val komgaSettingsJson = json<JsonObject>("komga_settings_json", JsonDbDefault)
val ttsuSettingsJson = json<TtsuReaderSettings>("ttsu_settings_json", JsonDbDefault)
val fullscreenEnabled = bool("fullscreen_enabled").default(true)

override val primaryKey = PrimaryKey(bookId)
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package snd.komelia.ui.reader

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.core.screen.ScreenKey
Expand Down Expand Up @@ -60,10 +64,10 @@ class EpubScreen(
}

val state = vm.state.collectAsState().value
val isFullscreen = LocalWindowState.current.isFullscreen.collectAsState(false)
Column {
PlatformTitleBar(applyInsets = false) {
if (canIntegrateWithSystemBar()) {
val isFullscreen = LocalWindowState.current.isFullscreen.collectAsState(false)
if (state is LoadState.Success && !isFullscreen.value) {
val book = state.value.book.collectAsState().value
TitleBarContent(
Expand All @@ -85,10 +89,19 @@ class EpubScreen(
}
)

is LoadState.Success -> EpubContent(
onWebviewCreated = { state.value.onWebviewCreated(it) },
onBackButtonPress = state.value::onBackButtonPress
)
is LoadState.Success -> {
// On mobile (edge-to-edge, no integrated title bar), pad below status bar when not fullscreen
val modifier = if (!isFullscreen.value && !canIntegrateWithSystemBar())
Modifier.statusBarsPadding().fillMaxSize()
else
Modifier.fillMaxSize()
Box(modifier = modifier) {
EpubContent(
onWebviewCreated = { state.value.onWebviewCreated(it) },
onBackButtonPress = state.value::onBackButtonPress
)
}
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ class KomgaEpubReaderState(
@OptIn(ExperimentalResourceApi::class)
override suspend fun initialize(navigator: Navigator) {
this.navigator.value = navigator
if (platformType == PlatformType.MOBILE) windowState.setFullscreen(true)
if (platformType == PlatformType.MOBILE && epubSettingsRepository.getFullscreenEnabled().first()) windowState.setFullscreen(true)
if (state.value !is Uninitialized) return

state.value = LoadState.Loading
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ class TtsuReaderState(
@OptIn(ExperimentalResourceApi::class)
override suspend fun initialize(navigator: Navigator) {
this.navigator.value = navigator
if (platformType == PlatformType.MOBILE) windowState.setFullscreen(true)
if (state.value !is LoadState.Uninitialized) return

state.value = LoadState.Loading
Expand Down Expand Up @@ -123,8 +122,6 @@ class TtsuReaderState(

override fun closeWebview() {
webview.value?.close()
if (platformType == PlatformType.MOBILE) windowState.setFullscreen(false)

navigator.value?.let { nav ->
if (nav.canPop) nav.pop()
else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,15 @@ import snd.komelia.settings.model.EpubReaderType.TTSU_EPUB
import snd.komelia.ui.LocalStrings
import snd.komelia.ui.common.components.DropdownChoiceMenu
import snd.komelia.ui.common.components.LabeledEntry
import snd.komelia.ui.common.components.SwitchWithLabel
import snd.komelia.ui.platform.cursorForHand

@Composable
fun EpubReaderSettingsContent(
readerType: EpubReaderType,
onReaderChange: (EpubReaderType) -> Unit,
fullscreenEnabled: Boolean,
onFullscreenEnabledChange: (Boolean) -> Unit,
) {
val strings = LocalStrings.current.settings
Column(
Expand Down Expand Up @@ -70,5 +73,14 @@ fun EpubReaderSettingsContent(
KOMGA_EPUB -> Text("Komga webui epub reader adapted for use in Komelia")

}

if (readerType == KOMGA_EPUB) {
SwitchWithLabel(
checked = fullscreenEnabled,
onCheckedChange = onFullscreenEnabledChange,
label = { Text("Fullscreen mode") },
supportingText = { Text("Automatically enter fullscreen when opening a book on mobile") },
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ class EpubReaderSettingsScreen : Screen {
is LoadState.Error -> ErrorContent(result.exception)
LoadState.Uninitialized, LoadState.Loading -> LoadingMaxSizeIndicator()
is LoadState.Success<Unit> -> EpubReaderSettingsContent(
vm.selectedEpubReader.collectAsState().value,
vm::onSelectedTypeChange
readerType = vm.selectedEpubReader.collectAsState().value,
onReaderChange = vm::onSelectedTypeChange,
fullscreenEnabled = vm.fullscreenEnabled.collectAsState().value,
onFullscreenEnabledChange = vm::onFullscreenEnabledChange,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,22 @@ class EpubReaderSettingsViewModel(
private val settingsRepository: EpubReaderSettingsRepository
) : StateScreenModel<LoadState<Unit>>(LoadState.Uninitialized) {
val selectedEpubReader = MutableStateFlow(TTSU_EPUB)
val fullscreenEnabled = MutableStateFlow(true)

suspend fun initialize() {
if (state.value !is LoadState.Uninitialized) return
selectedEpubReader.value = settingsRepository.getReaderType().first()
fullscreenEnabled.value = settingsRepository.getFullscreenEnabled().first()
mutableState.value = LoadState.Success(Unit)
}

fun onSelectedTypeChange(type: EpubReaderType) {
selectedEpubReader.value = type
screenModelScope.launch { settingsRepository.putReaderType(type) }
}

fun onFullscreenEnabledChange(enabled: Boolean) {
fullscreenEnabled.value = enabled
settingsRepository.putFullscreenEnabled(enabled)
}
}