mirror of
https://github.com/mihonapp/mihon.git
synced 2024-11-15 15:02:49 +01:00
feat(sync): Allow to choose what to sync.
Various improvement and added the option to choose what they want to sync. Added sync library button to LibraryTab as well. Signed-off-by: KaiserBh <kaiserbh@proton.me>
This commit is contained in:
parent
89c577952c
commit
ffe6efdd7a
@ -37,6 +37,7 @@ fun LibraryToolbar(
|
|||||||
onClickRefresh: () -> Unit,
|
onClickRefresh: () -> Unit,
|
||||||
onClickGlobalUpdate: () -> Unit,
|
onClickGlobalUpdate: () -> Unit,
|
||||||
onClickOpenRandomManga: () -> Unit,
|
onClickOpenRandomManga: () -> Unit,
|
||||||
|
onClickSyncNow: () -> Unit,
|
||||||
searchQuery: String?,
|
searchQuery: String?,
|
||||||
onSearchQueryChange: (String?) -> Unit,
|
onSearchQueryChange: (String?) -> Unit,
|
||||||
scrollBehavior: TopAppBarScrollBehavior?,
|
scrollBehavior: TopAppBarScrollBehavior?,
|
||||||
@ -56,6 +57,7 @@ fun LibraryToolbar(
|
|||||||
onClickRefresh = onClickRefresh,
|
onClickRefresh = onClickRefresh,
|
||||||
onClickGlobalUpdate = onClickGlobalUpdate,
|
onClickGlobalUpdate = onClickGlobalUpdate,
|
||||||
onClickOpenRandomManga = onClickOpenRandomManga,
|
onClickOpenRandomManga = onClickOpenRandomManga,
|
||||||
|
onClickSyncNow = onClickSyncNow,
|
||||||
scrollBehavior = scrollBehavior,
|
scrollBehavior = scrollBehavior,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -70,6 +72,7 @@ private fun LibraryRegularToolbar(
|
|||||||
onClickRefresh: () -> Unit,
|
onClickRefresh: () -> Unit,
|
||||||
onClickGlobalUpdate: () -> Unit,
|
onClickGlobalUpdate: () -> Unit,
|
||||||
onClickOpenRandomManga: () -> Unit,
|
onClickOpenRandomManga: () -> Unit,
|
||||||
|
onClickSyncNow: () -> Unit,
|
||||||
scrollBehavior: TopAppBarScrollBehavior?,
|
scrollBehavior: TopAppBarScrollBehavior?,
|
||||||
) {
|
) {
|
||||||
val pillAlpha = if (isSystemInDarkTheme()) 0.12f else 0.08f
|
val pillAlpha = if (isSystemInDarkTheme()) 0.12f else 0.08f
|
||||||
@ -115,6 +118,10 @@ private fun LibraryRegularToolbar(
|
|||||||
title = stringResource(MR.strings.action_open_random_manga),
|
title = stringResource(MR.strings.action_open_random_manga),
|
||||||
onClick = onClickOpenRandomManga,
|
onClick = onClickOpenRandomManga,
|
||||||
),
|
),
|
||||||
|
AppBar.OverflowAction(
|
||||||
|
title = stringResource(MR.strings.sync_library),
|
||||||
|
onClick = onClickSyncNow,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -39,6 +39,7 @@ import eu.kanade.presentation.more.settings.Preference
|
|||||||
import eu.kanade.presentation.more.settings.screen.data.CreateBackupScreen
|
import eu.kanade.presentation.more.settings.screen.data.CreateBackupScreen
|
||||||
import eu.kanade.presentation.more.settings.screen.data.RestoreBackupScreen
|
import eu.kanade.presentation.more.settings.screen.data.RestoreBackupScreen
|
||||||
import eu.kanade.presentation.more.settings.screen.data.StorageInfo
|
import eu.kanade.presentation.more.settings.screen.data.StorageInfo
|
||||||
|
import eu.kanade.presentation.more.settings.screen.data.SyncSettingsSelector
|
||||||
import eu.kanade.presentation.more.settings.widget.BasePreferenceWidget
|
import eu.kanade.presentation.more.settings.widget.BasePreferenceWidget
|
||||||
import eu.kanade.presentation.more.settings.widget.PrefsHorizontalPadding
|
import eu.kanade.presentation.more.settings.widget.PrefsHorizontalPadding
|
||||||
import eu.kanade.presentation.util.relativeTimeSpanString
|
import eu.kanade.presentation.util.relativeTimeSpanString
|
||||||
@ -462,24 +463,7 @@ object SettingsDataScreen : SearchableSettings {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun getSyncNowPref(): Preference.PreferenceGroup {
|
private fun getSyncNowPref(): Preference.PreferenceGroup {
|
||||||
val scope = rememberCoroutineScope()
|
val navigator = LocalNavigator.currentOrThrow
|
||||||
var showDialog by remember { mutableStateOf(false) }
|
|
||||||
val context = LocalContext.current
|
|
||||||
if (showDialog) {
|
|
||||||
SyncConfirmationDialog(
|
|
||||||
onConfirm = {
|
|
||||||
showDialog = false
|
|
||||||
scope.launch {
|
|
||||||
if (!SyncDataJob.isAnyJobRunning(context)) {
|
|
||||||
SyncDataJob.startNow(context)
|
|
||||||
} else {
|
|
||||||
context.toast(MR.strings.sync_in_progress)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onDismissRequest = { showDialog = false },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return Preference.PreferenceGroup(
|
return Preference.PreferenceGroup(
|
||||||
title = stringResource(MR.strings.pref_sync_now_group_title),
|
title = stringResource(MR.strings.pref_sync_now_group_title),
|
||||||
preferenceItems = persistentListOf(
|
preferenceItems = persistentListOf(
|
||||||
@ -487,7 +471,7 @@ object SettingsDataScreen : SearchableSettings {
|
|||||||
title = stringResource(MR.strings.pref_sync_now),
|
title = stringResource(MR.strings.pref_sync_now),
|
||||||
subtitle = stringResource(MR.strings.pref_sync_now_subtitle),
|
subtitle = stringResource(MR.strings.pref_sync_now_subtitle),
|
||||||
onClick = {
|
onClick = {
|
||||||
showDialog = true
|
navigator.push(SyncSettingsSelector())
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -528,26 +512,4 @@ object SettingsDataScreen : SearchableSettings {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun SyncConfirmationDialog(
|
|
||||||
onConfirm: () -> Unit,
|
|
||||||
onDismissRequest: () -> Unit,
|
|
||||||
) {
|
|
||||||
AlertDialog(
|
|
||||||
onDismissRequest = onDismissRequest,
|
|
||||||
title = { Text(text = stringResource(MR.strings.pref_sync_confirmation_title)) },
|
|
||||||
text = { Text(text = stringResource(MR.strings.pref_sync_confirmation_message)) },
|
|
||||||
dismissButton = {
|
|
||||||
TextButton(onClick = onDismissRequest) {
|
|
||||||
Text(text = stringResource(MR.strings.action_cancel))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
confirmButton = {
|
|
||||||
TextButton(onClick = onConfirm) {
|
|
||||||
Text(text = stringResource(MR.strings.action_ok))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,142 @@
|
|||||||
|
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.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.domain.sync.SyncOptions
|
||||||
|
import tachiyomi.domain.sync.SyncPreferences
|
||||||
|
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.getSyncOptions())),
|
||||||
|
) {
|
||||||
|
fun toggle(setter: (BackupOptions, Boolean) -> BackupOptions, enabled: Boolean) {
|
||||||
|
mutableState.update {
|
||||||
|
val updatedOptions = setter(it.options, enabled)
|
||||||
|
syncPreferences.setSyncOptions(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(syncOptions: SyncOptions): BackupOptions {
|
||||||
|
return BackupOptions(
|
||||||
|
libraryEntries = syncOptions.libraryEntries,
|
||||||
|
categories = syncOptions.categories,
|
||||||
|
chapters = syncOptions.chapters,
|
||||||
|
tracking = syncOptions.tracking,
|
||||||
|
history = syncOptions.history,
|
||||||
|
appSettings = syncOptions.appSettings,
|
||||||
|
sourceSettings = syncOptions.sourceSettings,
|
||||||
|
privateSettings = syncOptions.privateSettings,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun backupOptionsToSyncOptions(backupOptions: BackupOptions): SyncOptions {
|
||||||
|
return SyncOptions(
|
||||||
|
libraryEntries = backupOptions.libraryEntries,
|
||||||
|
categories = backupOptions.categories,
|
||||||
|
chapters = backupOptions.chapters,
|
||||||
|
tracking = backupOptions.tracking,
|
||||||
|
history = backupOptions.history,
|
||||||
|
appSettings = backupOptions.appSettings,
|
||||||
|
sourceSettings = backupOptions.sourceSettings,
|
||||||
|
privateSettings = backupOptions.privateSettings,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -80,7 +80,6 @@ class SyncDataJob(private val context: Context, workerParams: WorkerParameters)
|
|||||||
TimeUnit.MINUTES,
|
TimeUnit.MINUTES,
|
||||||
)
|
)
|
||||||
.addTag(TAG_AUTO)
|
.addTag(TAG_AUTO)
|
||||||
// No initial delay is needed, remove the randomDelayMillis
|
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
context.workManager.enqueueUniquePeriodicWork(TAG_AUTO, ExistingPeriodicWorkPolicy.UPDATE, request)
|
context.workManager.enqueueUniquePeriodicWork(TAG_AUTO, ExistingPeriodicWorkPolicy.UPDATE, request)
|
||||||
|
@ -72,16 +72,17 @@ class SyncManager(
|
|||||||
* from the database using the BackupManager, then synchronizes the data with a sync service.
|
* from the database using the BackupManager, then synchronizes the data with a sync service.
|
||||||
*/
|
*/
|
||||||
suspend fun syncData() {
|
suspend fun syncData() {
|
||||||
|
val syncOptions = syncPreferences.getSyncOptions()
|
||||||
val databaseManga = getAllMangaFromDB()
|
val databaseManga = getAllMangaFromDB()
|
||||||
val backupOptions = BackupOptions(
|
val backupOptions = BackupOptions(
|
||||||
libraryEntries = true,
|
libraryEntries = syncOptions.libraryEntries,
|
||||||
categories = true,
|
categories = syncOptions.categories,
|
||||||
chapters = true,
|
chapters = syncOptions.chapters,
|
||||||
tracking = true,
|
tracking = syncOptions.tracking,
|
||||||
history = true,
|
history = syncOptions.history,
|
||||||
appSettings = true,
|
appSettings = syncOptions.appSettings,
|
||||||
sourceSettings = true,
|
sourceSettings = syncOptions.sourceSettings,
|
||||||
privateSettings = true,
|
privateSettings = syncOptions.privateSettings,
|
||||||
)
|
)
|
||||||
val backup = Backup(
|
val backup = Backup(
|
||||||
backupManga = backupCreator.backupMangas(databaseManga, backupOptions),
|
backupManga = backupCreator.backupMangas(databaseManga, backupOptions),
|
||||||
@ -119,10 +120,17 @@ class SyncManager(
|
|||||||
|
|
||||||
val remoteBackup = syncService?.doSync(syncData)
|
val remoteBackup = syncService?.doSync(syncData)
|
||||||
|
|
||||||
|
// Stop the sync early if the remote backup is null or empty
|
||||||
|
if (remoteBackup?.backupManga?.size == 0) {
|
||||||
|
notifier.showSyncError("No data found on remote server.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Check if it's first sync based on lastSyncTimestamp
|
// Check if it's first sync based on lastSyncTimestamp
|
||||||
if (syncPreferences.lastSyncTimestamp().get() == 0L && databaseManga.isNotEmpty()) {
|
if (syncPreferences.lastSyncTimestamp().get() == 0L && databaseManga.isNotEmpty()) {
|
||||||
// It's first sync no need to restore data. (just update remote data)
|
// It's first sync no need to restore data. (just update remote data)
|
||||||
syncPreferences.lastSyncTimestamp().set(Date().time)
|
syncPreferences.lastSyncTimestamp().set(Date().time)
|
||||||
|
notifier.showSyncSuccess("Updated remote data successfully")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,10 +138,8 @@ class SyncManager(
|
|||||||
val (filteredFavorites, nonFavorites) = filterFavoritesAndNonFavorites(remoteBackup)
|
val (filteredFavorites, nonFavorites) = filterFavoritesAndNonFavorites(remoteBackup)
|
||||||
updateNonFavorites(nonFavorites)
|
updateNonFavorites(nonFavorites)
|
||||||
|
|
||||||
val mangas = processFavoriteManga(filteredFavorites)
|
|
||||||
|
|
||||||
val newSyncData = backup.copy(
|
val newSyncData = backup.copy(
|
||||||
backupManga = mangas,
|
backupManga = filteredFavorites,
|
||||||
backupCategories = remoteBackup.backupCategories,
|
backupCategories = remoteBackup.backupCategories,
|
||||||
backupSources = remoteBackup.backupSources,
|
backupSources = remoteBackup.backupSources,
|
||||||
backupPreferences = remoteBackup.backupPreferences,
|
backupPreferences = remoteBackup.backupPreferences,
|
||||||
@ -141,9 +147,10 @@ class SyncManager(
|
|||||||
)
|
)
|
||||||
|
|
||||||
// It's local sync no need to restore data. (just update remote data)
|
// It's local sync no need to restore data. (just update remote data)
|
||||||
if (mangas.isEmpty()) {
|
if (filteredFavorites.isEmpty()) {
|
||||||
// update the sync timestamp
|
// update the sync timestamp
|
||||||
syncPreferences.lastSyncTimestamp().set(Date().time)
|
syncPreferences.lastSyncTimestamp().set(Date().time)
|
||||||
|
notifier.showSyncSuccess("Sync completed successfully")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -328,6 +335,7 @@ class SyncManager(
|
|||||||
val favorites = mutableListOf<BackupManga>()
|
val favorites = mutableListOf<BackupManga>()
|
||||||
val nonFavorites = mutableListOf<BackupManga>()
|
val nonFavorites = mutableListOf<BackupManga>()
|
||||||
val logTag = "filterFavoritesAndNonFavorites"
|
val logTag = "filterFavoritesAndNonFavorites"
|
||||||
|
|
||||||
val elapsedTimeMillis = measureTimeMillis {
|
val elapsedTimeMillis = measureTimeMillis {
|
||||||
val databaseMangaFavorites = getFavorites.await()
|
val databaseMangaFavorites = getFavorites.await()
|
||||||
val localMangaMap = databaseMangaFavorites.associateBy {
|
val localMangaMap = databaseMangaFavorites.associateBy {
|
||||||
@ -368,51 +376,6 @@ class SyncManager(
|
|||||||
return Pair(favorites, nonFavorites)
|
return Pair(favorites, nonFavorites)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processFavoriteManga(backupManga: List<BackupManga>): List<BackupManga> {
|
|
||||||
val mangas = mutableListOf<BackupManga>()
|
|
||||||
val lastSyncTimeStamp = syncPreferences.lastSyncTimestamp().get()
|
|
||||||
|
|
||||||
val elapsedTimeMillis = measureTimeMillis {
|
|
||||||
logcat(LogPriority.DEBUG) { "Starting to process BackupMangas." }
|
|
||||||
backupManga.forEach { manga ->
|
|
||||||
val mangaLastUpdatedStatus = manga.lastModifiedAt * 1000L > lastSyncTimeStamp
|
|
||||||
val chaptersUpdatedStatus = chaptersUpdatedAfterSync(manga, lastSyncTimeStamp)
|
|
||||||
|
|
||||||
if (mangaLastUpdatedStatus || chaptersUpdatedStatus) {
|
|
||||||
mangas.add(manga)
|
|
||||||
logcat(LogPriority.DEBUG) {
|
|
||||||
"Added ${manga.title} to the process list. Manga Last Updated: $mangaLastUpdatedStatus, " +
|
|
||||||
"Chapters Updated: $chaptersUpdatedStatus."
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logcat(LogPriority.DEBUG) {
|
|
||||||
"Skipped ${manga.title} as it has not been updated since the last sync " +
|
|
||||||
"(Last Modified: ${manga.lastModifiedAt * 1000L}, Last Sync: $lastSyncTimeStamp)."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val minutes = elapsedTimeMillis / 60000
|
|
||||||
val seconds = (elapsedTimeMillis % 60000) / 1000
|
|
||||||
logcat(LogPriority.DEBUG) { "Processing completed in ${minutes}m ${seconds}s. Total Processed: ${mangas.size}" }
|
|
||||||
|
|
||||||
return mangas
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun chaptersUpdatedAfterSync(manga: BackupManga, lastSyncTimeStamp: Long): Boolean {
|
|
||||||
return manga.chapters.any { chapter ->
|
|
||||||
val updated = chapter.lastModifiedAt * 1000L > lastSyncTimeStamp
|
|
||||||
if (updated) {
|
|
||||||
logcat(LogPriority.DEBUG) {
|
|
||||||
"Chapter ${chapter.name} of ${manga.title} updated after last sync " +
|
|
||||||
"(Chapter Last Modified: ${chapter.lastModifiedAt * 1000L}, Last Sync: $lastSyncTimeStamp)."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
updated
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the non-favorite manga in the local database with their favorite status from the backup.
|
* Updates the non-favorite manga in the local database with their favorite status from the backup.
|
||||||
* @param nonFavorites the list of non-favorite BackupManga objects from the backup.
|
* @param nonFavorites the list of non-favorite BackupManga objects from the backup.
|
||||||
|
@ -72,4 +72,15 @@ class SyncNotifier(private val context: Context) {
|
|||||||
show(Notifications.ID_RESTORE_COMPLETE)
|
show(Notifications.ID_RESTORE_COMPLETE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun showSyncSuccess(message: String?) {
|
||||||
|
context.cancelNotification(Notifications.ID_RESTORE_PROGRESS)
|
||||||
|
|
||||||
|
with(completeNotificationBuilder) {
|
||||||
|
setContentTitle(context.getString(R.string.sync_complete))
|
||||||
|
setContentText(message)
|
||||||
|
|
||||||
|
show(Notifications.ID_RESTORE_COMPLETE)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,12 +37,14 @@ import eu.kanade.presentation.more.onboarding.GETTING_STARTED_URL
|
|||||||
import eu.kanade.presentation.util.Tab
|
import eu.kanade.presentation.util.Tab
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
|
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
|
||||||
|
import eu.kanade.tachiyomi.data.sync.SyncDataJob
|
||||||
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchScreen
|
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchScreen
|
||||||
import eu.kanade.tachiyomi.ui.category.CategoryScreen
|
import eu.kanade.tachiyomi.ui.category.CategoryScreen
|
||||||
import eu.kanade.tachiyomi.ui.home.HomeScreen
|
import eu.kanade.tachiyomi.ui.home.HomeScreen
|
||||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||||
import eu.kanade.tachiyomi.ui.manga.MangaScreen
|
import eu.kanade.tachiyomi.ui.manga.MangaScreen
|
||||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
||||||
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.coroutines.channels.Channel
|
import kotlinx.coroutines.channels.Channel
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
@ -135,6 +137,13 @@ object LibraryTab : Tab {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onClickSyncNow = {
|
||||||
|
if (!SyncDataJob.isAnyJobRunning(context)) {
|
||||||
|
SyncDataJob.startNow(context)
|
||||||
|
} else {
|
||||||
|
context.toast(MR.strings.sync_in_progress)
|
||||||
|
}
|
||||||
|
},
|
||||||
searchQuery = state.searchQuery,
|
searchQuery = state.searchQuery,
|
||||||
onSearchQueryChange = screenModel::search,
|
onSearchQueryChange = screenModel::search,
|
||||||
scrollBehavior = scrollBehavior.takeIf { !tabVisible }, // For scroll overlay when no tab
|
scrollBehavior = scrollBehavior.takeIf { !tabVisible }, // For scroll overlay when no tab
|
||||||
|
@ -36,4 +36,39 @@ class SyncPreferences(
|
|||||||
|
|
||||||
return uniqueID
|
return uniqueID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getSyncOptions(): SyncOptions {
|
||||||
|
return SyncOptions(
|
||||||
|
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 setSyncOptions(syncOptions: SyncOptions) {
|
||||||
|
preferenceStore.getBoolean("library_entries", true).set(syncOptions.libraryEntries)
|
||||||
|
preferenceStore.getBoolean("categories", true).set(syncOptions.categories)
|
||||||
|
preferenceStore.getBoolean("chapters", true).set(syncOptions.chapters)
|
||||||
|
preferenceStore.getBoolean("tracking", true).set(syncOptions.tracking)
|
||||||
|
preferenceStore.getBoolean("history", true).set(syncOptions.history)
|
||||||
|
preferenceStore.getBoolean("appSettings", true).set(syncOptions.appSettings)
|
||||||
|
preferenceStore.getBoolean("sourceSettings", true).set(syncOptions.sourceSettings)
|
||||||
|
preferenceStore.getBoolean("privateSettings", true).set(syncOptions.privateSettings)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class SyncOptions(
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
@ -563,6 +563,7 @@
|
|||||||
<string name="pref_sync_interval">Synchronization frequency</string>
|
<string name="pref_sync_interval">Synchronization frequency</string>
|
||||||
<string name="pref_reset_sync_timestamp">Reset last sync timestamp</string>
|
<string name="pref_reset_sync_timestamp">Reset last sync timestamp</string>
|
||||||
<string name="pref_reset_sync_timestamp_subtitle">Reset the last sync timestamp to force a full sync</string>
|
<string name="pref_reset_sync_timestamp_subtitle">Reset the last sync timestamp to force a full sync</string>
|
||||||
|
<string name="pref_choose_what_to_sync">Choose what to sync</string>
|
||||||
<string name="success_reset_sync_timestamp">Last sync timestamp reset</string>
|
<string name="success_reset_sync_timestamp">Last sync timestamp reset</string>
|
||||||
<string name="syncyomi">SyncYomi</string>
|
<string name="syncyomi">SyncYomi</string>
|
||||||
<string name="sync_completed_message">Done in %1$s</string>
|
<string name="sync_completed_message">Done in %1$s</string>
|
||||||
@ -582,7 +583,7 @@
|
|||||||
<string name="error_deleting_google_drive_lock_file">Error Deleting Google Drive Lock File</string>
|
<string name="error_deleting_google_drive_lock_file">Error Deleting Google Drive Lock File</string>
|
||||||
<string name="pref_purge_confirmation_title">Purge confirmation</string>
|
<string name="pref_purge_confirmation_title">Purge confirmation</string>
|
||||||
<string name="pref_purge_confirmation_message">Purging sync data will delete all your sync data from Google Drive. Are you sure you want to continue?</string>
|
<string name="pref_purge_confirmation_message">Purging sync data will delete all your sync data from Google Drive. Are you sure you want to continue?</string>
|
||||||
|
<string name="sync_library">Sync library</string>
|
||||||
|
|
||||||
<!-- Advanced section -->
|
<!-- Advanced section -->
|
||||||
<string name="label_network">Networking</string>
|
<string name="label_network">Networking</string>
|
||||||
|
Loading…
Reference in New Issue
Block a user