preparing the publication
This commit is contained in:
parent
bad1e89c26
commit
128275402e
@ -2,6 +2,10 @@ import org.jetbrains.compose.desktop.application.dsl.TargetFormat
|
|||||||
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
|
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
|
||||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||||
|
|
||||||
|
val appVersionName = "1.0"
|
||||||
|
val appVersionCode = 1
|
||||||
|
val appVersionDisplay = "$appVersionName.$appVersionCode"
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.kotlinMultiplatform)
|
alias(libs.plugins.kotlinMultiplatform)
|
||||||
alias(libs.plugins.androidApplication)
|
alias(libs.plugins.androidApplication)
|
||||||
@ -10,6 +14,38 @@ plugins {
|
|||||||
alias(libs.plugins.composeHotReload)
|
alias(libs.plugins.composeHotReload)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract class GenerateAppVersionConstantsTask : DefaultTask() {
|
||||||
|
@get:Input
|
||||||
|
abstract val versionName: Property<String>
|
||||||
|
|
||||||
|
@get:Input
|
||||||
|
abstract val versionCode: Property<Int>
|
||||||
|
|
||||||
|
@get:OutputDirectory
|
||||||
|
abstract val outputDir: DirectoryProperty
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
fun generate() {
|
||||||
|
val outputFile = outputDir.file("net/sergeych/toread/AppVersion.kt").get().asFile
|
||||||
|
outputFile.parentFile.mkdirs()
|
||||||
|
outputFile.writeText(
|
||||||
|
"""
|
||||||
|
package net.sergeych.toread
|
||||||
|
|
||||||
|
internal const val AppVersionName = "${versionName.get()}"
|
||||||
|
internal const val AppVersionCode = ${versionCode.get()}
|
||||||
|
internal const val AppVersionDisplay = "${versionName.get()}.${versionCode.get()}"
|
||||||
|
""".trimIndent() + "\n",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val generateAppVersionConstants by tasks.registering(GenerateAppVersionConstantsTask::class) {
|
||||||
|
versionName.set(appVersionName)
|
||||||
|
versionCode.set(appVersionCode)
|
||||||
|
outputDir.set(layout.buildDirectory.dir("generated/appVersion/commonMain/kotlin"))
|
||||||
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
jvmToolchain(17)
|
jvmToolchain(17)
|
||||||
|
|
||||||
@ -33,6 +69,9 @@ kotlin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
|
commonMain {
|
||||||
|
kotlin.srcDir(generateAppVersionConstants)
|
||||||
|
}
|
||||||
androidMain.dependencies {
|
androidMain.dependencies {
|
||||||
implementation(libs.compose.uiToolingPreview)
|
implementation(libs.compose.uiToolingPreview)
|
||||||
implementation(libs.androidx.activity.compose)
|
implementation(libs.androidx.activity.compose)
|
||||||
@ -69,8 +108,8 @@ android {
|
|||||||
applicationId = "net.sergeych.toread"
|
applicationId = "net.sergeych.toread"
|
||||||
minSdk = libs.versions.android.minSdk.get().toInt()
|
minSdk = libs.versions.android.minSdk.get().toInt()
|
||||||
targetSdk = libs.versions.android.targetSdk.get().toInt()
|
targetSdk = libs.versions.android.targetSdk.get().toInt()
|
||||||
versionCode = 1
|
versionCode = appVersionCode
|
||||||
versionName = "1.0"
|
versionName = appVersionName
|
||||||
}
|
}
|
||||||
packaging {
|
packaging {
|
||||||
resources {
|
resources {
|
||||||
@ -100,7 +139,7 @@ compose.desktop {
|
|||||||
nativeDistributions {
|
nativeDistributions {
|
||||||
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
|
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
|
||||||
packageName = "To Read"
|
packageName = "To Read"
|
||||||
packageVersion = "1.0.0"
|
packageVersion = appVersionDisplay
|
||||||
|
|
||||||
macOS {
|
macOS {
|
||||||
iconFile.set(project.file("src/jvmMain/resources/icons/icon.icns"))
|
iconFile.set(project.file("src/jvmMain/resources/icons/icon.icns"))
|
||||||
|
|||||||
BIN
composeApp/release/baselineProfiles/0/composeApp-release.dm
Normal file
BIN
composeApp/release/baselineProfiles/0/composeApp-release.dm
Normal file
Binary file not shown.
BIN
composeApp/release/baselineProfiles/1/composeApp-release.dm
Normal file
BIN
composeApp/release/baselineProfiles/1/composeApp-release.dm
Normal file
Binary file not shown.
BIN
composeApp/release/composeApp-release.apk
Normal file
BIN
composeApp/release/composeApp-release.apk
Normal file
Binary file not shown.
37
composeApp/release/output-metadata.json
Normal file
37
composeApp/release/output-metadata.json
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"version": 3,
|
||||||
|
"artifactType": {
|
||||||
|
"type": "APK",
|
||||||
|
"kind": "Directory"
|
||||||
|
},
|
||||||
|
"applicationId": "net.sergeych.toread",
|
||||||
|
"variantName": "release",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"type": "SINGLE",
|
||||||
|
"filters": [],
|
||||||
|
"attributes": [],
|
||||||
|
"versionCode": 1,
|
||||||
|
"versionName": "1.0",
|
||||||
|
"outputFile": "composeApp-release.apk"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"elementType": "File",
|
||||||
|
"baselineProfiles": [
|
||||||
|
{
|
||||||
|
"minApi": 28,
|
||||||
|
"maxApi": 30,
|
||||||
|
"baselineProfiles": [
|
||||||
|
"baselineProfiles/1/composeApp-release.dm"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"minApi": 31,
|
||||||
|
"maxApi": 2147483647,
|
||||||
|
"baselineProfiles": [
|
||||||
|
"baselineProfiles/0/composeApp-release.dm"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"minSdkVersionForDexing": 26
|
||||||
|
}
|
||||||
@ -438,6 +438,18 @@ actual suspend fun saveScanDownloadsAutomatically(enabled: Boolean) = withContex
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
actual suspend fun loadAppLocaleTag(): String? = withContext(Dispatchers.IO) {
|
||||||
|
openLibraryDatabase().useLibrary { db ->
|
||||||
|
db.getAppFlag(AppLocaleTagFlag)?.takeIf { it.isNotBlank() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
actual suspend fun saveAppLocaleTag(localeTag: String?) = withContext(Dispatchers.IO) {
|
||||||
|
openLibraryDatabase().useLibrary { db ->
|
||||||
|
db.setAppFlag(AppLocaleTagFlag, localeTag?.takeIf { it.isNotBlank() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
actual suspend fun loadDownloadsWasScanned(): Boolean = withContext(Dispatchers.IO) {
|
actual suspend fun loadDownloadsWasScanned(): Boolean = withContext(Dispatchers.IO) {
|
||||||
openLibraryDatabase().useLibrary { db ->
|
openLibraryDatabase().useLibrary { db ->
|
||||||
db.getAppFlag(DownloadsWasScannedFlag)?.toBooleanStrictOrNull()
|
db.getAppFlag(DownloadsWasScannedFlag)?.toBooleanStrictOrNull()
|
||||||
@ -781,4 +793,5 @@ private val SearchPrefixRegex = Regex("""[\p{L}\p{N}]+""")
|
|||||||
private const val ActiveReadingFileIdFlag = "active_reading_file_id"
|
private const val ActiveReadingFileIdFlag = "active_reading_file_id"
|
||||||
private const val ThemeModeFlag = "theme_mode"
|
private const val ThemeModeFlag = "theme_mode"
|
||||||
private const val ScanDownloadsAutomaticallyFlag = "scan_downloads_automatically"
|
private const val ScanDownloadsAutomaticallyFlag = "scan_downloads_automatically"
|
||||||
|
private const val AppLocaleTagFlag = "app_locale_tag"
|
||||||
private const val DownloadsWasScannedFlag = "downloads_was_scanned"
|
private const val DownloadsWasScannedFlag = "downloads_was_scanned"
|
||||||
|
|||||||
@ -51,6 +51,7 @@ private data class PendingLibraryDelete(
|
|||||||
fun App() {
|
fun App() {
|
||||||
var themeMode by remember { mutableStateOf(ThemeMode.SYSTEM) }
|
var themeMode by remember { mutableStateOf(ThemeMode.SYSTEM) }
|
||||||
var systemDark by remember { mutableStateOf(isPlatformDarkTheme()) }
|
var systemDark by remember { mutableStateOf(isPlatformDarkTheme()) }
|
||||||
|
var localePreferenceLoaded by remember { mutableStateOf(false) }
|
||||||
var toast by remember { mutableStateOf<AppToastData?>(null) }
|
var toast by remember { mutableStateOf<AppToastData?>(null) }
|
||||||
var nextToastId by remember { mutableStateOf(0L) }
|
var nextToastId by remember { mutableStateOf(0L) }
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
@ -78,6 +79,13 @@ fun App() {
|
|||||||
themeMode = loadThemeMode()
|
themeMode = loadThemeMode()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
selectedAppLocaleTag = loadAppLocaleTag()?.takeIf { saved ->
|
||||||
|
availableAppLanguages.any { it.localeTag == saved }
|
||||||
|
}
|
||||||
|
localePreferenceLoaded = true
|
||||||
|
}
|
||||||
|
|
||||||
LaunchedEffect(toast?.id) {
|
LaunchedEffect(toast?.id) {
|
||||||
val current = toast ?: return@LaunchedEffect
|
val current = toast ?: return@LaunchedEffect
|
||||||
delay(current.durationMillis)
|
delay(current.durationMillis)
|
||||||
@ -89,6 +97,7 @@ fun App() {
|
|||||||
MaterialTheme(colorScheme = if (useDark) darkReaderColorScheme() else lightReaderColorScheme()) {
|
MaterialTheme(colorScheme = if (useDark) darkReaderColorScheme() else lightReaderColorScheme()) {
|
||||||
Box(Modifier.fillMaxSize()) {
|
Box(Modifier.fillMaxSize()) {
|
||||||
Surface(Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
|
Surface(Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
|
||||||
|
if (localePreferenceLoaded) {
|
||||||
BookReaderApp(
|
BookReaderApp(
|
||||||
onThemeToggle = {
|
onThemeToggle = {
|
||||||
val next = themeMode.next()
|
val next = themeMode.next()
|
||||||
@ -100,6 +109,9 @@ fun App() {
|
|||||||
},
|
},
|
||||||
onShowToast = ::showToast,
|
onShowToast = ::showToast,
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
LoadingScreen(strings.loadingOpeningBook)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
AppToast(toast, modifier = Modifier.align(Alignment.BottomCenter))
|
AppToast(toast, modifier = Modifier.align(Alignment.BottomCenter))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -145,6 +145,10 @@ expect suspend fun loadScanDownloadsAutomatically(): Boolean
|
|||||||
|
|
||||||
expect suspend fun saveScanDownloadsAutomatically(enabled: Boolean)
|
expect suspend fun saveScanDownloadsAutomatically(enabled: Boolean)
|
||||||
|
|
||||||
|
expect suspend fun loadAppLocaleTag(): String?
|
||||||
|
|
||||||
|
expect suspend fun saveAppLocaleTag(localeTag: String?)
|
||||||
|
|
||||||
expect suspend fun loadDownloadsWasScanned(): Boolean
|
expect suspend fun loadDownloadsWasScanned(): Boolean
|
||||||
|
|
||||||
expect suspend fun saveDownloadsWasScanned(scanned: Boolean)
|
expect suspend fun saveDownloadsWasScanned(scanned: Boolean)
|
||||||
|
|||||||
@ -24,7 +24,9 @@ import androidx.compose.foundation.lazy.itemsIndexed
|
|||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.text.BasicTextField
|
import androidx.compose.foundation.text.BasicTextField
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
|
||||||
import androidx.compose.material.icons.filled.Add
|
import androidx.compose.material.icons.filled.Add
|
||||||
|
import androidx.compose.material.icons.filled.Check
|
||||||
import androidx.compose.material.icons.filled.Close
|
import androidx.compose.material.icons.filled.Close
|
||||||
import androidx.compose.material.icons.filled.Favorite
|
import androidx.compose.material.icons.filled.Favorite
|
||||||
import androidx.compose.material.icons.filled.KeyboardArrowDown
|
import androidx.compose.material.icons.filled.KeyboardArrowDown
|
||||||
@ -101,6 +103,7 @@ internal fun LibraryScreen(
|
|||||||
var recentlyAddedItems by remember(state.items) { mutableStateOf<List<LibraryItem>>(emptyList()) }
|
var recentlyAddedItems by remember(state.items) { mutableStateOf<List<LibraryItem>>(emptyList()) }
|
||||||
var wasScanning by remember { mutableStateOf(false) }
|
var wasScanning by remember { mutableStateOf(false) }
|
||||||
var settingsMenuOpen by remember { mutableStateOf(false) }
|
var settingsMenuOpen by remember { mutableStateOf(false) }
|
||||||
|
var localeMenuOpen by remember { mutableStateOf(false) }
|
||||||
var autoScanDownloads by remember { mutableStateOf(true) }
|
var autoScanDownloads by remember { mutableStateOf(true) }
|
||||||
var autoScanSettingLoaded by remember { mutableStateOf(false) }
|
var autoScanSettingLoaded by remember { mutableStateOf(false) }
|
||||||
var backgroundDownloadsRescanStarted by remember { mutableStateOf(false) }
|
var backgroundDownloadsRescanStarted by remember { mutableStateOf(false) }
|
||||||
@ -221,6 +224,7 @@ internal fun LibraryScreen(
|
|||||||
|
|
||||||
fun rescanAllLibrary() {
|
fun rescanAllLibrary() {
|
||||||
settingsMenuOpen = false
|
settingsMenuOpen = false
|
||||||
|
localeMenuOpen = false
|
||||||
scope.launch {
|
scope.launch {
|
||||||
busy = true
|
busy = true
|
||||||
message = strings.rescanningLibrary
|
message = strings.rescanningLibrary
|
||||||
@ -282,7 +286,10 @@ internal fun LibraryScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(searchFocused) {
|
LaunchedEffect(searchFocused) {
|
||||||
if (searchFocused) settingsMenuOpen = false
|
if (searchFocused) {
|
||||||
|
settingsMenuOpen = false
|
||||||
|
localeMenuOpen = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(searchActive, loadingPage, endReached, libraryItems, recentlyAdded) {
|
LaunchedEffect(searchActive, loadingPage, endReached, libraryItems, recentlyAdded) {
|
||||||
@ -366,7 +373,13 @@ internal fun LibraryScreen(
|
|||||||
IconButton(onClick = { settingsMenuOpen = true }) {
|
IconButton(onClick = { settingsMenuOpen = true }) {
|
||||||
Icon(Icons.Filled.MoreVert, contentDescription = strings.libraryOptions)
|
Icon(Icons.Filled.MoreVert, contentDescription = strings.libraryOptions)
|
||||||
}
|
}
|
||||||
DropdownMenu(expanded = settingsMenuOpen, onDismissRequest = { settingsMenuOpen = false }) {
|
DropdownMenu(
|
||||||
|
expanded = settingsMenuOpen,
|
||||||
|
onDismissRequest = {
|
||||||
|
settingsMenuOpen = false
|
||||||
|
localeMenuOpen = false
|
||||||
|
},
|
||||||
|
) {
|
||||||
// DropdownMenuItem(
|
// DropdownMenuItem(
|
||||||
// text = { Text("Rescan all library") },
|
// text = { Text("Rescan all library") },
|
||||||
// enabled = !busy && activeScan == null,
|
// enabled = !busy && activeScan == null,
|
||||||
@ -385,6 +398,56 @@ internal fun LibraryScreen(
|
|||||||
scope.launch { saveScanDownloadsAutomatically(next) }
|
scope.launch { saveScanDownloadsAutomatically(next) }
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
HorizontalDivider()
|
||||||
|
Box {
|
||||||
|
DropdownMenuItem(
|
||||||
|
text = { Text(strings.locale) },
|
||||||
|
trailingIcon = {
|
||||||
|
Icon(Icons.AutoMirrored.Filled.KeyboardArrowRight, contentDescription = null)
|
||||||
|
},
|
||||||
|
onClick = { localeMenuOpen = true },
|
||||||
|
)
|
||||||
|
DropdownMenu(expanded = localeMenuOpen, onDismissRequest = { localeMenuOpen = false }) {
|
||||||
|
availableAppLanguages.forEach { language ->
|
||||||
|
val checked = selectedAppLocaleTag == language.localeTag
|
||||||
|
DropdownMenuItem(
|
||||||
|
leadingIcon = {
|
||||||
|
if (checked) {
|
||||||
|
Icon(Icons.Filled.Check, contentDescription = null)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
text = { Text(strings.appLanguageName(language)) },
|
||||||
|
onClick = {
|
||||||
|
selectedAppLocaleTag = language.localeTag
|
||||||
|
localeMenuOpen = false
|
||||||
|
settingsMenuOpen = false
|
||||||
|
scope.launch { saveAppLocaleTag(language.localeTag) }
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
HorizontalDivider()
|
||||||
|
DropdownMenuItem(
|
||||||
|
leadingIcon = {
|
||||||
|
if (selectedAppLocaleTag == null) {
|
||||||
|
Icon(Icons.Filled.Check, contentDescription = null)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
text = { Text(strings.systemLocale) },
|
||||||
|
onClick = {
|
||||||
|
selectedAppLocaleTag = null
|
||||||
|
localeMenuOpen = false
|
||||||
|
settingsMenuOpen = false
|
||||||
|
scope.launch { saveAppLocaleTag(null) }
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HorizontalDivider()
|
||||||
|
DropdownMenuItem(
|
||||||
|
text = { Text(strings.appVersion(AppVersionDisplay)) },
|
||||||
|
enabled = false,
|
||||||
|
onClick = {},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,15 +1,22 @@
|
|||||||
package net.sergeych.toread
|
package net.sergeych.toread
|
||||||
|
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import net.sergeych.toread.storage.BookReadingStatus
|
import net.sergeych.toread.storage.BookReadingStatus
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
internal enum class AppLanguage {
|
internal enum class AppLanguage(val localeTag: String) {
|
||||||
English,
|
English("en"),
|
||||||
Russian,
|
Russian("ru"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal val availableAppLanguages: List<AppLanguage> = AppLanguage.entries
|
||||||
|
|
||||||
|
internal var selectedAppLocaleTag: String? by mutableStateOf(null)
|
||||||
|
|
||||||
internal val appLanguage: AppLanguage
|
internal val appLanguage: AppLanguage
|
||||||
get() = if (platformLocaleTags().any { it.isRussianLanguageTag() }) {
|
get() = if (effectiveAppLocaleTags().any { it.isRussianLanguageTag() }) {
|
||||||
AppLanguage.Russian
|
AppLanguage.Russian
|
||||||
} else {
|
} else {
|
||||||
AppLanguage.English
|
AppLanguage.English
|
||||||
@ -23,6 +30,9 @@ internal val strings: AppStrings
|
|||||||
|
|
||||||
internal expect fun platformLocaleTags(): List<String>
|
internal expect fun platformLocaleTags(): List<String>
|
||||||
|
|
||||||
|
private fun effectiveAppLocaleTags(): List<String> =
|
||||||
|
selectedAppLocaleTag?.let(::listOf) ?: platformLocaleTags()
|
||||||
|
|
||||||
private fun String.isRussianLanguageTag(): Boolean =
|
private fun String.isRussianLanguageTag(): Boolean =
|
||||||
substringBefore('-').substringBefore('_').equals("ru", ignoreCase = true)
|
substringBefore('-').substringBefore('_').equals("ru", ignoreCase = true)
|
||||||
|
|
||||||
@ -53,6 +63,9 @@ internal open class AppStrings {
|
|||||||
|
|
||||||
open val libraryOptions = "Library options"
|
open val libraryOptions = "Library options"
|
||||||
open val scanDownloadsAutomatically = "Scan Downloads automatically"
|
open val scanDownloadsAutomatically = "Scan Downloads automatically"
|
||||||
|
open val locale = "Locale"
|
||||||
|
open val systemLocale = "Default"
|
||||||
|
open fun appVersion(version: String): String = "Version $version"
|
||||||
open val scanFolder = "Scan folder"
|
open val scanFolder = "Scan folder"
|
||||||
open val chooseLibraryFilter = "Choose library filter"
|
open val chooseLibraryFilter = "Choose library filter"
|
||||||
open val searchLibrary = "Search library"
|
open val searchLibrary = "Search library"
|
||||||
@ -197,6 +210,11 @@ internal open class AppStrings {
|
|||||||
open fun couldNotRead(name: String): String = "Could not read $name"
|
open fun couldNotRead(name: String): String = "Could not read $name"
|
||||||
open fun progressLabel(progress: Double?): String =
|
open fun progressLabel(progress: Double?): String =
|
||||||
progress?.let { "Progress ${(it * 100).roundToInt()}%" } ?: "Progress not recorded"
|
progress?.let { "Progress ${(it * 100).roundToInt()}%" } ?: "Progress not recorded"
|
||||||
|
open fun appLanguageName(language: AppLanguage): String =
|
||||||
|
when (language) {
|
||||||
|
AppLanguage.English -> "English"
|
||||||
|
AppLanguage.Russian -> "Russian"
|
||||||
|
}
|
||||||
open fun sectionFallback(index: Int): String = "Section ${index + 1}"
|
open fun sectionFallback(index: Int): String = "Section ${index + 1}"
|
||||||
open fun readingStatus(status: BookReadingStatus): String =
|
open fun readingStatus(status: BookReadingStatus): String =
|
||||||
when (status) {
|
when (status) {
|
||||||
@ -237,6 +255,9 @@ internal object RussianStrings : AppStrings() {
|
|||||||
|
|
||||||
override val libraryOptions = "Параметры библиотеки"
|
override val libraryOptions = "Параметры библиотеки"
|
||||||
override val scanDownloadsAutomatically = "Автоматически сканировать Загрузки"
|
override val scanDownloadsAutomatically = "Автоматически сканировать Загрузки"
|
||||||
|
override val locale = "Локализация"
|
||||||
|
override val systemLocale = "Системная"
|
||||||
|
override fun appVersion(version: String): String = "Версия $version"
|
||||||
override val scanFolder = "Сканировать папку"
|
override val scanFolder = "Сканировать папку"
|
||||||
override val chooseLibraryFilter = "Выбрать фильтр библиотеки"
|
override val chooseLibraryFilter = "Выбрать фильтр библиотеки"
|
||||||
override val searchLibrary = "Поиск"
|
override val searchLibrary = "Поиск"
|
||||||
@ -384,6 +405,11 @@ internal object RussianStrings : AppStrings() {
|
|||||||
override fun couldNotRead(name: String): String = "Не удалось прочитать $name"
|
override fun couldNotRead(name: String): String = "Не удалось прочитать $name"
|
||||||
override fun progressLabel(progress: Double?): String =
|
override fun progressLabel(progress: Double?): String =
|
||||||
progress?.let { "Прогресс ${(it * 100).roundToInt()}%" } ?: "Прогресс не записан"
|
progress?.let { "Прогресс ${(it * 100).roundToInt()}%" } ?: "Прогресс не записан"
|
||||||
|
override fun appLanguageName(language: AppLanguage): String =
|
||||||
|
when (language) {
|
||||||
|
AppLanguage.English -> "Английский"
|
||||||
|
AppLanguage.Russian -> "Русский"
|
||||||
|
}
|
||||||
override fun sectionFallback(index: Int): String = "Раздел ${index + 1}"
|
override fun sectionFallback(index: Int): String = "Раздел ${index + 1}"
|
||||||
override fun readingStatus(status: BookReadingStatus): String =
|
override fun readingStatus(status: BookReadingStatus): String =
|
||||||
when (status) {
|
when (status) {
|
||||||
|
|||||||
@ -373,6 +373,18 @@ actual suspend fun saveScanDownloadsAutomatically(enabled: Boolean) = withContex
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
actual suspend fun loadAppLocaleTag(): String? = withContext(Dispatchers.IO) {
|
||||||
|
openLibraryDatabase().useLibrary { db ->
|
||||||
|
db.getAppFlag(AppLocaleTagFlag)?.takeIf { it.isNotBlank() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
actual suspend fun saveAppLocaleTag(localeTag: String?) = withContext(Dispatchers.IO) {
|
||||||
|
openLibraryDatabase().useLibrary { db ->
|
||||||
|
db.setAppFlag(AppLocaleTagFlag, localeTag?.takeIf { it.isNotBlank() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
actual suspend fun loadDownloadsWasScanned(): Boolean = withContext(Dispatchers.IO) {
|
actual suspend fun loadDownloadsWasScanned(): Boolean = withContext(Dispatchers.IO) {
|
||||||
openLibraryDatabase().useLibrary { db ->
|
openLibraryDatabase().useLibrary { db ->
|
||||||
db.getAppFlag(DownloadsWasScannedFlag)?.toBooleanStrictOrNull()
|
db.getAppFlag(DownloadsWasScannedFlag)?.toBooleanStrictOrNull()
|
||||||
@ -589,4 +601,5 @@ private fun runCommand(vararg command: String): String? =
|
|||||||
private const val ActiveReadingFileIdFlag = "active_reading_file_id"
|
private const val ActiveReadingFileIdFlag = "active_reading_file_id"
|
||||||
private const val ThemeModeFlag = "theme_mode"
|
private const val ThemeModeFlag = "theme_mode"
|
||||||
private const val ScanDownloadsAutomaticallyFlag = "scan_downloads_automatically"
|
private const val ScanDownloadsAutomaticallyFlag = "scan_downloads_automatically"
|
||||||
|
private const val AppLocaleTagFlag = "app_locale_tag"
|
||||||
private const val DownloadsWasScannedFlag = "downloads_was_scanned"
|
private const val DownloadsWasScannedFlag = "downloads_was_scanned"
|
||||||
|
|||||||
@ -77,6 +77,17 @@ actual suspend fun loadScanDownloadsAutomatically(): Boolean = true
|
|||||||
|
|
||||||
actual suspend fun saveScanDownloadsAutomatically(enabled: Boolean) = Unit
|
actual suspend fun saveScanDownloadsAutomatically(enabled: Boolean) = Unit
|
||||||
|
|
||||||
|
actual suspend fun loadAppLocaleTag(): String? =
|
||||||
|
window.localStorage.getItem(AppLocaleStorageKey)?.takeIf { it.isNotBlank() }
|
||||||
|
|
||||||
|
actual suspend fun saveAppLocaleTag(localeTag: String?) {
|
||||||
|
if (localeTag.isNullOrBlank()) {
|
||||||
|
window.localStorage.removeItem(AppLocaleStorageKey)
|
||||||
|
} else {
|
||||||
|
window.localStorage.setItem(AppLocaleStorageKey, localeTag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
actual suspend fun loadDownloadsWasScanned(): Boolean = false
|
actual suspend fun loadDownloadsWasScanned(): Boolean = false
|
||||||
|
|
||||||
actual suspend fun saveDownloadsWasScanned(scanned: Boolean) = Unit
|
actual suspend fun saveDownloadsWasScanned(scanned: Boolean) = Unit
|
||||||
@ -99,6 +110,8 @@ actual fun watchPlatformDarkTheme(onChange: (Boolean) -> Unit): () -> Unit {
|
|||||||
|
|
||||||
actual fun libraryLogPath(): String? = null
|
actual fun libraryLogPath(): String? = null
|
||||||
|
|
||||||
|
private const val AppLocaleStorageKey = "toread.appLocaleTag"
|
||||||
|
|
||||||
actual fun formatLibraryLastReadTime(millis: Long): String {
|
actual fun formatLibraryLastReadTime(millis: Long): String {
|
||||||
val totalMinutes = millis / 60_000L
|
val totalMinutes = millis / 60_000L
|
||||||
val minute = (totalMinutes % 60).toString().padStart(2, '0')
|
val minute = (totalMinutes % 60).toString().padStart(2, '0')
|
||||||
|
|||||||
BIN
screenshots/Screenshot_20260523_170411.png
Normal file
BIN
screenshots/Screenshot_20260523_170411.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 194 KiB |
BIN
screenshots/Screenshot_20260523_170540.png
Normal file
BIN
screenshots/Screenshot_20260523_170540.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 200 KiB |
BIN
screenshots/Screenshot_20260523_170604 (101).
Normal file
BIN
screenshots/Screenshot_20260523_170604 (101).
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 179 KiB |
BIN
screenshots/Screenshot_20260523_170621.png
Normal file
BIN
screenshots/Screenshot_20260523_170621.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 171 KiB |
Loading…
x
Reference in New Issue
Block a user