mirror of
https://github.com/mihonapp/mihon.git
synced 2025-02-08 00:05:02 +01:00
chore: review pointers.
This commit is contained in:
parent
26aa126ecb
commit
ba63253fe2
93
app/src/main/java/eu/kanade/domain/sync/SyncPreferences.kt
Normal file
93
app/src/main/java/eu/kanade/domain/sync/SyncPreferences.kt
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
|
||||||
|
package eu.kanade.domain.sync
|
||||||
|
|
||||||
|
import eu.kanade.domain.sync.models.SyncSettings
|
||||||
|
import eu.kanade.tachiyomi.data.sync.models.SyncTriggerOptions
|
||||||
|
import tachiyomi.core.preference.Preference
|
||||||
|
import tachiyomi.core.preference.PreferenceStore
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
class SyncPreferences(
|
||||||
|
private val preferenceStore: PreferenceStore,
|
||||||
|
) {
|
||||||
|
fun clientHost() = preferenceStore.getString("sync_client_host", "https://sync.tachiyomi.org")
|
||||||
|
fun clientAPIKey() = preferenceStore.getString("sync_client_api_key", "")
|
||||||
|
fun lastSyncTimestamp() = preferenceStore.getLong(Preference.appStateKey("last_sync_timestamp"), 0L)
|
||||||
|
|
||||||
|
fun syncInterval() = preferenceStore.getInt("sync_interval", 0)
|
||||||
|
fun syncService() = preferenceStore.getInt("sync_service", 0)
|
||||||
|
|
||||||
|
fun googleDriveAccessToken() = preferenceStore.getString(
|
||||||
|
Preference.appStateKey("google_drive_access_token"),
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
|
||||||
|
fun googleDriveRefreshToken() = preferenceStore.getString(
|
||||||
|
Preference.appStateKey("google_drive_refresh_token"),
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
|
||||||
|
fun uniqueDeviceID(): String {
|
||||||
|
val uniqueIDPreference = preferenceStore.getString("unique_device_id", "")
|
||||||
|
|
||||||
|
// Retrieve the current value of the preference
|
||||||
|
var uniqueID = uniqueIDPreference.get()
|
||||||
|
if (uniqueID.isBlank()) {
|
||||||
|
uniqueID = UUID.randomUUID().toString()
|
||||||
|
uniqueIDPreference.set(uniqueID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return uniqueID
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isSyncEnabled(): Boolean {
|
||||||
|
return syncService().get() != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSyncSettings(): SyncSettings {
|
||||||
|
return SyncSettings(
|
||||||
|
libraryEntries = preferenceStore.getBoolean("library_entries", true).get(),
|
||||||
|
categories = preferenceStore.getBoolean("categories", true).get(),
|
||||||
|
chapters = preferenceStore.getBoolean("chapters", true).get(),
|
||||||
|
tracking = preferenceStore.getBoolean("tracking", true).get(),
|
||||||
|
history = preferenceStore.getBoolean("history", true).get(),
|
||||||
|
appSettings = preferenceStore.getBoolean("appSettings", true).get(),
|
||||||
|
sourceSettings = preferenceStore.getBoolean("sourceSettings", true).get(),
|
||||||
|
privateSettings = preferenceStore.getBoolean("privateSettings", true).get(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSyncSettings(syncSettings: SyncSettings) {
|
||||||
|
preferenceStore.getBoolean("library_entries", true).set(syncSettings.libraryEntries)
|
||||||
|
preferenceStore.getBoolean("categories", true).set(syncSettings.categories)
|
||||||
|
preferenceStore.getBoolean("chapters", true).set(syncSettings.chapters)
|
||||||
|
preferenceStore.getBoolean("tracking", true).set(syncSettings.tracking)
|
||||||
|
preferenceStore.getBoolean("history", true).set(syncSettings.history)
|
||||||
|
preferenceStore.getBoolean("appSettings", true).set(syncSettings.appSettings)
|
||||||
|
preferenceStore.getBoolean("sourceSettings", true).set(syncSettings.sourceSettings)
|
||||||
|
preferenceStore.getBoolean("privateSettings", true).set(syncSettings.privateSettings)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSyncTriggerOptions(): SyncTriggerOptions {
|
||||||
|
return SyncTriggerOptions(
|
||||||
|
syncOnChapterRead = preferenceStore.getBoolean("sync_on_chapter_read", false).get(),
|
||||||
|
syncOnChapterOpen = preferenceStore.getBoolean("sync_on_chapter_open", false).get(),
|
||||||
|
syncOnAppStart = preferenceStore.getBoolean("sync_on_app_start", false).get(),
|
||||||
|
syncOnAppResume = preferenceStore.getBoolean("sync_on_app_resume", false).get(),
|
||||||
|
syncOnLibraryUpdate = preferenceStore.getBoolean("sync_on_library_update", false).get(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSyncTriggerOptions(syncTriggerOptions: SyncTriggerOptions) {
|
||||||
|
preferenceStore.getBoolean("sync_on_chapter_read", false)
|
||||||
|
.set(syncTriggerOptions.syncOnChapterRead)
|
||||||
|
preferenceStore.getBoolean("sync_on_chapter_open", false)
|
||||||
|
.set(syncTriggerOptions.syncOnChapterOpen)
|
||||||
|
preferenceStore.getBoolean("sync_on_app_start", false)
|
||||||
|
.set(syncTriggerOptions.syncOnAppStart)
|
||||||
|
preferenceStore.getBoolean("sync_on_app_resume", false)
|
||||||
|
.set(syncTriggerOptions.syncOnAppResume)
|
||||||
|
preferenceStore.getBoolean("sync_on_library_update", false)
|
||||||
|
.set(syncTriggerOptions.syncOnLibraryUpdate)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package eu.kanade.domain.sync.models
|
||||||
|
|
||||||
|
data class SyncSettings(
|
||||||
|
val libraryEntries: Boolean = true,
|
||||||
|
val categories: Boolean = true,
|
||||||
|
val chapters: Boolean = true,
|
||||||
|
val tracking: Boolean = true,
|
||||||
|
val history: Boolean = true,
|
||||||
|
val appSettings: Boolean = true,
|
||||||
|
val sourceSettings: Boolean = true,
|
||||||
|
val privateSettings: Boolean = false,
|
||||||
|
)
|
@ -0,0 +1,143 @@
|
|||||||
|
|
||||||
|
package eu.kanade.presentation.more.settings.screen.data
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import cafe.adriel.voyager.core.model.StateScreenModel
|
||||||
|
import cafe.adriel.voyager.core.model.rememberScreenModel
|
||||||
|
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||||
|
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||||
|
import eu.kanade.domain.sync.SyncPreferences
|
||||||
|
import eu.kanade.domain.sync.models.SyncSettings
|
||||||
|
import eu.kanade.presentation.components.AppBar
|
||||||
|
import eu.kanade.presentation.util.Screen
|
||||||
|
import eu.kanade.tachiyomi.data.backup.create.BackupOptions
|
||||||
|
import eu.kanade.tachiyomi.data.sync.SyncDataJob
|
||||||
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import tachiyomi.i18n.MR
|
||||||
|
import tachiyomi.presentation.core.components.LabeledCheckbox
|
||||||
|
import tachiyomi.presentation.core.components.LazyColumnWithAction
|
||||||
|
import tachiyomi.presentation.core.components.SectionCard
|
||||||
|
import tachiyomi.presentation.core.components.material.Scaffold
|
||||||
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
|
class SyncSettingsSelector : Screen() {
|
||||||
|
@Composable
|
||||||
|
override fun Content() {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val navigator = LocalNavigator.currentOrThrow
|
||||||
|
val model = rememberScreenModel { SyncSettingsSelectorModel() }
|
||||||
|
val state by model.state.collectAsState()
|
||||||
|
|
||||||
|
Scaffold(
|
||||||
|
topBar = {
|
||||||
|
AppBar(
|
||||||
|
title = stringResource(MR.strings.pref_choose_what_to_sync),
|
||||||
|
navigateUp = navigator::pop,
|
||||||
|
scrollBehavior = it,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
) { contentPadding ->
|
||||||
|
LazyColumnWithAction(
|
||||||
|
contentPadding = contentPadding,
|
||||||
|
actionLabel = stringResource(MR.strings.label_sync),
|
||||||
|
actionEnabled = state.options.anyEnabled(),
|
||||||
|
onClickAction = {
|
||||||
|
if (!SyncDataJob.isAnyJobRunning(context)) {
|
||||||
|
model.syncNow(context)
|
||||||
|
navigator.pop()
|
||||||
|
} else {
|
||||||
|
context.toast(MR.strings.sync_in_progress)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
item {
|
||||||
|
SectionCard(MR.strings.label_library) {
|
||||||
|
Options(BackupOptions.libraryOptions, state, model)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
item {
|
||||||
|
SectionCard(MR.strings.label_settings) {
|
||||||
|
Options(BackupOptions.settingsOptions, state, model)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun Options(
|
||||||
|
options: ImmutableList<BackupOptions.Entry>,
|
||||||
|
state: SyncSettingsSelectorModel.State,
|
||||||
|
model: SyncSettingsSelectorModel,
|
||||||
|
) {
|
||||||
|
options.forEach { option ->
|
||||||
|
LabeledCheckbox(
|
||||||
|
label = stringResource(option.label),
|
||||||
|
checked = option.getter(state.options),
|
||||||
|
onCheckedChange = {
|
||||||
|
model.toggle(option.setter, it)
|
||||||
|
},
|
||||||
|
enabled = option.enabled(state.options),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SyncSettingsSelectorModel(
|
||||||
|
val syncPreferences: SyncPreferences = Injekt.get(),
|
||||||
|
) : StateScreenModel<SyncSettingsSelectorModel.State>(
|
||||||
|
State(syncOptionsToBackupOptions(syncPreferences.getSyncSettings())),
|
||||||
|
) {
|
||||||
|
fun toggle(setter: (BackupOptions, Boolean) -> BackupOptions, enabled: Boolean) {
|
||||||
|
mutableState.update {
|
||||||
|
val updatedOptions = setter(it.options, enabled)
|
||||||
|
syncPreferences.setSyncSettings(backupOptionsToSyncOptions(updatedOptions))
|
||||||
|
it.copy(options = updatedOptions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun syncNow(context: Context) {
|
||||||
|
SyncDataJob.startNow(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
data class State(
|
||||||
|
val options: BackupOptions = BackupOptions(),
|
||||||
|
) companion object {
|
||||||
|
private fun syncOptionsToBackupOptions(syncSettings: SyncSettings): BackupOptions {
|
||||||
|
return BackupOptions(
|
||||||
|
libraryEntries = syncSettings.libraryEntries,
|
||||||
|
categories = syncSettings.categories,
|
||||||
|
chapters = syncSettings.chapters,
|
||||||
|
tracking = syncSettings.tracking,
|
||||||
|
history = syncSettings.history,
|
||||||
|
appSettings = syncSettings.appSettings,
|
||||||
|
sourceSettings = syncSettings.sourceSettings,
|
||||||
|
privateSettings = syncSettings.privateSettings,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun backupOptionsToSyncOptions(backupOptions: BackupOptions): SyncSettings {
|
||||||
|
return SyncSettings(
|
||||||
|
libraryEntries = backupOptions.libraryEntries,
|
||||||
|
categories = backupOptions.categories,
|
||||||
|
chapters = backupOptions.chapters,
|
||||||
|
tracking = backupOptions.tracking,
|
||||||
|
history = backupOptions.history,
|
||||||
|
appSettings = backupOptions.appSettings,
|
||||||
|
sourceSettings = backupOptions.sourceSettings,
|
||||||
|
privateSettings = backupOptions.privateSettings,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,101 @@
|
|||||||
|
package eu.kanade.presentation.more.settings.screen.data
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import cafe.adriel.voyager.core.model.StateScreenModel
|
||||||
|
import cafe.adriel.voyager.core.model.rememberScreenModel
|
||||||
|
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||||
|
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||||
|
import eu.kanade.domain.sync.SyncPreferences
|
||||||
|
import eu.kanade.presentation.components.AppBar
|
||||||
|
import eu.kanade.presentation.util.Screen
|
||||||
|
import eu.kanade.tachiyomi.data.sync.models.SyncTriggerOptions
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import tachiyomi.i18n.MR
|
||||||
|
import tachiyomi.presentation.core.components.LabeledCheckbox
|
||||||
|
import tachiyomi.presentation.core.components.LazyColumnWithAction
|
||||||
|
import tachiyomi.presentation.core.components.SectionCard
|
||||||
|
import tachiyomi.presentation.core.components.material.Scaffold
|
||||||
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
|
class SyncTriggerOptionsScreen : Screen() {
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
override fun Content() {
|
||||||
|
val navigator = LocalNavigator.currentOrThrow
|
||||||
|
val model = rememberScreenModel { SyncOptionsScreenModel() }
|
||||||
|
val state by model.state.collectAsState()
|
||||||
|
|
||||||
|
Scaffold(
|
||||||
|
topBar = {
|
||||||
|
AppBar(
|
||||||
|
title = stringResource(MR.strings.pref_sync_options),
|
||||||
|
navigateUp = navigator::pop,
|
||||||
|
scrollBehavior = it,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
) { contentPadding ->
|
||||||
|
LazyColumnWithAction(
|
||||||
|
contentPadding = contentPadding,
|
||||||
|
actionLabel = stringResource(MR.strings.action_save),
|
||||||
|
actionEnabled = state.options.anyEnabled(),
|
||||||
|
onClickAction = {
|
||||||
|
navigator.pop()
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
item {
|
||||||
|
SectionCard(MR.strings.label_triggers) {
|
||||||
|
Options(SyncTriggerOptions.mainOptions, state, model)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun Options(
|
||||||
|
options: ImmutableList<SyncTriggerOptions.Entry>,
|
||||||
|
state: SyncOptionsScreenModel.State,
|
||||||
|
model: SyncOptionsScreenModel,
|
||||||
|
) {
|
||||||
|
options.forEach { option ->
|
||||||
|
LabeledCheckbox(
|
||||||
|
label = stringResource(option.label),
|
||||||
|
checked = option.getter(state.options),
|
||||||
|
onCheckedChange = {
|
||||||
|
model.toggle(option.setter, it)
|
||||||
|
},
|
||||||
|
enabled = option.enabled(state.options),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SyncOptionsScreenModel(
|
||||||
|
val syncPreferences: SyncPreferences = Injekt.get(),
|
||||||
|
) : StateScreenModel<SyncOptionsScreenModel.State>(
|
||||||
|
State(
|
||||||
|
syncPreferences.getSyncTriggerOptions(),
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun toggle(setter: (SyncTriggerOptions, Boolean) -> SyncTriggerOptions, enabled: Boolean) {
|
||||||
|
mutableState.update {
|
||||||
|
val updatedTriggerOptions = setter(it.options, enabled)
|
||||||
|
syncPreferences.setSyncTriggerOptions(updatedTriggerOptions)
|
||||||
|
it.copy(
|
||||||
|
options = updatedTriggerOptions,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
data class State(
|
||||||
|
val options: SyncTriggerOptions = SyncTriggerOptions(),
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
package eu.kanade.tachiyomi.data.sync.models
|
||||||
|
|
||||||
|
import dev.icerock.moko.resources.StringResource
|
||||||
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
import tachiyomi.i18n.MR
|
||||||
|
|
||||||
|
data class SyncTriggerOptions(
|
||||||
|
val syncOnChapterRead: Boolean = false,
|
||||||
|
val syncOnChapterOpen: Boolean = false,
|
||||||
|
val syncOnAppStart: Boolean = false,
|
||||||
|
val syncOnAppResume: Boolean = false,
|
||||||
|
val syncOnLibraryUpdate: Boolean = false,
|
||||||
|
) {
|
||||||
|
fun asBooleanArray() = booleanArrayOf(
|
||||||
|
syncOnChapterRead,
|
||||||
|
syncOnChapterOpen,
|
||||||
|
syncOnAppStart,
|
||||||
|
syncOnAppResume,
|
||||||
|
syncOnLibraryUpdate,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun anyEnabled() = syncOnChapterRead ||
|
||||||
|
syncOnChapterOpen ||
|
||||||
|
syncOnAppStart ||
|
||||||
|
syncOnAppResume ||
|
||||||
|
syncOnLibraryUpdate
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val mainOptions = persistentListOf(
|
||||||
|
Entry(
|
||||||
|
label = MR.strings.sync_on_chapter_read,
|
||||||
|
getter = SyncTriggerOptions::syncOnChapterRead,
|
||||||
|
setter = { options, enabled -> options.copy(syncOnChapterRead = enabled) },
|
||||||
|
),
|
||||||
|
Entry(
|
||||||
|
label = MR.strings.sync_on_chapter_open,
|
||||||
|
getter = SyncTriggerOptions::syncOnChapterOpen,
|
||||||
|
setter = { options, enabled -> options.copy(syncOnChapterOpen = enabled) },
|
||||||
|
),
|
||||||
|
Entry(
|
||||||
|
label = MR.strings.sync_on_app_start,
|
||||||
|
getter = SyncTriggerOptions::syncOnAppStart,
|
||||||
|
setter = { options, enabled -> options.copy(syncOnAppStart = enabled) },
|
||||||
|
),
|
||||||
|
Entry(
|
||||||
|
label = MR.strings.sync_on_app_resume,
|
||||||
|
getter = SyncTriggerOptions::syncOnAppResume,
|
||||||
|
setter = { options, enabled -> options.copy(syncOnAppResume = enabled) },
|
||||||
|
),
|
||||||
|
Entry(
|
||||||
|
label = MR.strings.sync_on_library_update,
|
||||||
|
getter = SyncTriggerOptions::syncOnLibraryUpdate,
|
||||||
|
setter = { options, enabled -> options.copy(syncOnLibraryUpdate = enabled) },
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
fun fromBooleanArray(array: BooleanArray) = SyncTriggerOptions(
|
||||||
|
syncOnChapterRead = array[0],
|
||||||
|
syncOnChapterOpen = array[1],
|
||||||
|
syncOnAppStart = array[2],
|
||||||
|
syncOnAppResume = array[3],
|
||||||
|
syncOnLibraryUpdate = array[4],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Entry(
|
||||||
|
val label: StringResource,
|
||||||
|
val getter: (SyncTriggerOptions) -> Boolean,
|
||||||
|
val setter: (SyncTriggerOptions, Boolean) -> SyncTriggerOptions,
|
||||||
|
val enabled: (SyncTriggerOptions) -> Boolean = { true },
|
||||||
|
)
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user