mirror of
https://github.com/mihonapp/mihon.git
synced 2025-10-24 12:08:55 +02:00
chore: merge changes from upstream.
Signed-off-by: KaiserBh <kaiserbh@proton.me>
This commit is contained in:
@@ -40,11 +40,14 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import eu.kanade.presentation.browse.components.BaseBrowseItem
|
||||
import eu.kanade.presentation.browse.components.ExtensionIcon
|
||||
import eu.kanade.presentation.components.WarningBanner
|
||||
import eu.kanade.presentation.manga.components.DotSeparatorNoSpaceText
|
||||
import eu.kanade.presentation.more.settings.screen.browse.ExtensionReposScreen
|
||||
import eu.kanade.presentation.util.rememberRequestPackageInstallsPermissionState
|
||||
import eu.kanade.tachiyomi.extension.model.Extension
|
||||
import eu.kanade.tachiyomi.extension.model.InstallStep
|
||||
@@ -52,6 +55,7 @@ import eu.kanade.tachiyomi.ui.browse.extension.ExtensionUiModel
|
||||
import eu.kanade.tachiyomi.ui.browse.extension.ExtensionsScreenModel
|
||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||
import eu.kanade.tachiyomi.util.system.launchRequestPackageInstallsPermission
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.components.FastScrollLazyColumn
|
||||
import tachiyomi.presentation.core.components.material.PullRefresh
|
||||
@@ -59,6 +63,7 @@ import tachiyomi.presentation.core.components.material.padding
|
||||
import tachiyomi.presentation.core.components.material.topSmallPaddingValues
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
import tachiyomi.presentation.core.screens.EmptyScreen
|
||||
import tachiyomi.presentation.core.screens.EmptyScreenAction
|
||||
import tachiyomi.presentation.core.screens.LoadingScreen
|
||||
import tachiyomi.presentation.core.theme.header
|
||||
import tachiyomi.presentation.core.util.plus
|
||||
@@ -80,6 +85,8 @@ fun ExtensionScreen(
|
||||
onClickUpdateAll: () -> Unit,
|
||||
onRefresh: () -> Unit,
|
||||
) {
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
|
||||
PullRefresh(
|
||||
refreshing = state.isRefreshing,
|
||||
onRefresh = onRefresh,
|
||||
@@ -96,6 +103,13 @@ fun ExtensionScreen(
|
||||
EmptyScreen(
|
||||
stringRes = msg,
|
||||
modifier = Modifier.padding(contentPadding),
|
||||
actions = persistentListOf(
|
||||
EmptyScreenAction(
|
||||
stringRes = MR.strings.label_extension_repos,
|
||||
icon = Icons.Outlined.Settings,
|
||||
onClick = { navigator.push(ExtensionReposScreen()) },
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
|
||||
@@ -6,7 +6,7 @@ import androidx.compose.material.icons.outlined.Refresh
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||
import eu.kanade.presentation.theme.TachiyomiPreviewTheme
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.screens.EmptyScreen
|
||||
@@ -15,7 +15,7 @@ import tachiyomi.presentation.core.screens.EmptyScreenAction
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun NoActionPreview() {
|
||||
TachiyomiTheme {
|
||||
TachiyomiPreviewTheme {
|
||||
Surface {
|
||||
EmptyScreen(
|
||||
stringRes = MR.strings.empty_screen,
|
||||
@@ -27,7 +27,7 @@ private fun NoActionPreview() {
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun WithActionPreview() {
|
||||
TachiyomiTheme {
|
||||
TachiyomiPreviewTheme {
|
||||
Surface {
|
||||
EmptyScreen(
|
||||
stringRes = MR.strings.empty_screen,
|
||||
|
||||
@@ -101,6 +101,6 @@ data class TabContent(
|
||||
val titleRes: StringResource,
|
||||
val badgeNumber: Int? = null,
|
||||
val searchEnabled: Boolean = false,
|
||||
val actions: ImmutableList<AppBar.Action> = persistentListOf(),
|
||||
val actions: ImmutableList<AppBar.AppBarAction> = persistentListOf(),
|
||||
val content: @Composable (contentPadding: PaddingValues, snackbarHostState: SnackbarHostState) -> Unit,
|
||||
)
|
||||
|
||||
@@ -14,7 +14,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||
import eu.kanade.presentation.theme.TachiyomiPreviewTheme
|
||||
import eu.kanade.tachiyomi.util.CrashLogUtil
|
||||
import kotlinx.coroutines.launch
|
||||
import tachiyomi.i18n.MR
|
||||
@@ -63,7 +63,7 @@ fun CrashScreen(
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun CrashScreenPreview() {
|
||||
TachiyomiTheme {
|
||||
TachiyomiPreviewTheme {
|
||||
CrashScreen(exception = RuntimeException("Dummy")) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import eu.kanade.presentation.components.AppBarTitle
|
||||
import eu.kanade.presentation.components.SearchToolbar
|
||||
import eu.kanade.presentation.components.relativeDateText
|
||||
import eu.kanade.presentation.history.components.HistoryItem
|
||||
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||
import eu.kanade.presentation.theme.TachiyomiPreviewTheme
|
||||
import eu.kanade.tachiyomi.ui.history.HistoryScreenModel
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import tachiyomi.domain.history.model.HistoryWithRelations
|
||||
@@ -143,7 +143,7 @@ internal fun HistoryScreenPreviews(
|
||||
@PreviewParameter(HistoryScreenModelStateProvider::class)
|
||||
historyState: HistoryScreenModel.State,
|
||||
) {
|
||||
TachiyomiTheme {
|
||||
TachiyomiPreviewTheme {
|
||||
HistoryScreen(
|
||||
state = historyState,
|
||||
snackbarHostState = SnackbarHostState(),
|
||||
|
||||
@@ -12,7 +12,7 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||
import eu.kanade.presentation.theme.TachiyomiPreviewTheme
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.components.LabeledCheckbox
|
||||
import tachiyomi.presentation.core.components.material.padding
|
||||
@@ -91,7 +91,7 @@ fun HistoryDeleteAllDialog(
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun HistoryDeleteDialogPreview() {
|
||||
TachiyomiTheme {
|
||||
TachiyomiPreviewTheme {
|
||||
HistoryDeleteDialog(
|
||||
onDismissRequest = {},
|
||||
onDelete = {},
|
||||
|
||||
@@ -23,7 +23,7 @@ import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import eu.kanade.presentation.manga.components.MangaCover
|
||||
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||
import eu.kanade.presentation.theme.TachiyomiPreviewTheme
|
||||
import eu.kanade.presentation.util.formatChapterNumber
|
||||
import eu.kanade.tachiyomi.util.lang.toTimestampString
|
||||
import tachiyomi.domain.history.model.HistoryWithRelations
|
||||
@@ -98,7 +98,7 @@ private fun HistoryItemPreviews(
|
||||
@PreviewParameter(HistoryWithRelationsProvider::class)
|
||||
historyWithRelations: HistoryWithRelations,
|
||||
) {
|
||||
TachiyomiTheme {
|
||||
TachiyomiPreviewTheme {
|
||||
Surface {
|
||||
HistoryItem(
|
||||
history = historyWithRelations,
|
||||
|
||||
@@ -6,7 +6,7 @@ import androidx.compose.material.icons.outlined.Folder
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||
import eu.kanade.presentation.theme.TachiyomiPreviewTheme
|
||||
import tachiyomi.presentation.core.components.Badge
|
||||
|
||||
@Composable
|
||||
@@ -50,7 +50,7 @@ internal fun LanguageBadge(
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun BadgePreview() {
|
||||
TachiyomiTheme {
|
||||
TachiyomiPreviewTheme {
|
||||
Column {
|
||||
DownloadsBadge(count = 10)
|
||||
UnreadBadge(count = 10)
|
||||
|
||||
@@ -37,6 +37,7 @@ fun LibraryToolbar(
|
||||
onClickRefresh: () -> Unit,
|
||||
onClickGlobalUpdate: () -> Unit,
|
||||
onClickOpenRandomManga: () -> Unit,
|
||||
onClickSyncNow: () -> Unit,
|
||||
searchQuery: String?,
|
||||
onSearchQueryChange: (String?) -> Unit,
|
||||
scrollBehavior: TopAppBarScrollBehavior?,
|
||||
@@ -56,6 +57,7 @@ fun LibraryToolbar(
|
||||
onClickRefresh = onClickRefresh,
|
||||
onClickGlobalUpdate = onClickGlobalUpdate,
|
||||
onClickOpenRandomManga = onClickOpenRandomManga,
|
||||
onClickSyncNow = onClickSyncNow,
|
||||
scrollBehavior = scrollBehavior,
|
||||
)
|
||||
}
|
||||
@@ -70,6 +72,7 @@ private fun LibraryRegularToolbar(
|
||||
onClickRefresh: () -> Unit,
|
||||
onClickGlobalUpdate: () -> Unit,
|
||||
onClickOpenRandomManga: () -> Unit,
|
||||
onClickSyncNow: () -> Unit,
|
||||
scrollBehavior: TopAppBarScrollBehavior?,
|
||||
) {
|
||||
val pillAlpha = if (isSystemInDarkTheme()) 0.12f else 0.08f
|
||||
@@ -115,6 +118,10 @@ private fun LibraryRegularToolbar(
|
||||
title = stringResource(MR.strings.action_open_random_manga),
|
||||
onClick = onClickOpenRandomManga,
|
||||
),
|
||||
AppBar.OverflowAction(
|
||||
title = stringResource(MR.strings.sync_library),
|
||||
onClick = onClickSyncNow,
|
||||
),
|
||||
),
|
||||
)
|
||||
},
|
||||
|
||||
@@ -11,7 +11,7 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||
import eu.kanade.presentation.theme.TachiyomiPreviewTheme
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.components.material.padding
|
||||
import tachiyomi.presentation.core.i18n.pluralStringResource
|
||||
@@ -44,7 +44,7 @@ fun MissingChapterCountListItem(
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun Preview() {
|
||||
TachiyomiTheme {
|
||||
TachiyomiPreviewTheme {
|
||||
Surface {
|
||||
MissingChapterCountListItem(count = 42)
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import com.halilibo.richtext.markdown.Markdown
|
||||
import com.halilibo.richtext.ui.RichTextStyle
|
||||
import com.halilibo.richtext.ui.material3.RichText
|
||||
import com.halilibo.richtext.ui.string.RichTextStringStyle
|
||||
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||
import eu.kanade.presentation.theme.TachiyomiPreviewTheme
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.components.material.padding
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
@@ -69,7 +69,7 @@ fun NewUpdateScreen(
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun NewUpdateScreenPreview() {
|
||||
TachiyomiTheme {
|
||||
TachiyomiPreviewTheme {
|
||||
NewUpdateScreen(
|
||||
versionName = "v0.99.9",
|
||||
changelogInfo = """
|
||||
|
||||
@@ -13,7 +13,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import androidx.compose.ui.unit.dp
|
||||
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||
import eu.kanade.presentation.theme.TachiyomiPreviewTheme
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.components.material.padding
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
@@ -61,7 +61,7 @@ const val GETTING_STARTED_URL = "https://tachiyomi.org/docs/guides/getting-start
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun GuidesStepPreview() {
|
||||
TachiyomiTheme {
|
||||
TachiyomiPreviewTheme {
|
||||
GuidesStep(
|
||||
onRestoreBackup = {},
|
||||
).Content()
|
||||
|
||||
@@ -40,6 +40,7 @@ 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.StorageInfo
|
||||
import eu.kanade.presentation.more.settings.screen.data.SyncOptionsScreen
|
||||
import eu.kanade.presentation.more.settings.screen.data.SyncSettingsSelector
|
||||
import eu.kanade.presentation.more.settings.widget.BasePreferenceWidget
|
||||
import eu.kanade.presentation.more.settings.widget.PrefsHorizontalPadding
|
||||
import eu.kanade.presentation.util.relativeTimeSpanString
|
||||
@@ -463,24 +464,7 @@ object SettingsDataScreen : SearchableSettings {
|
||||
|
||||
@Composable
|
||||
private fun getSyncNowPref(): Preference.PreferenceGroup {
|
||||
val scope = rememberCoroutineScope()
|
||||
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 },
|
||||
)
|
||||
}
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
return Preference.PreferenceGroup(
|
||||
title = stringResource(MR.strings.pref_sync_now_group_title),
|
||||
preferenceItems = persistentListOf(
|
||||
@@ -489,7 +473,7 @@ object SettingsDataScreen : SearchableSettings {
|
||||
title = stringResource(MR.strings.pref_sync_now),
|
||||
subtitle = stringResource(MR.strings.pref_sync_now_subtitle),
|
||||
onClick = {
|
||||
showDialog = true
|
||||
navigator.push(SyncSettingsSelector())
|
||||
},
|
||||
),
|
||||
),
|
||||
@@ -540,26 +524,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))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.outlined.Label
|
||||
import androidx.compose.material.icons.outlined.ContentCopy
|
||||
import androidx.compose.material.icons.outlined.Delete
|
||||
import androidx.compose.material3.ElevatedCard
|
||||
import androidx.compose.material3.Icon
|
||||
@@ -18,8 +19,12 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
||||
import kotlinx.collections.immutable.ImmutableSet
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.components.material.padding
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
|
||||
@Composable
|
||||
fun ExtensionReposContent(
|
||||
@@ -53,6 +58,8 @@ private fun ExtensionRepoListItem(
|
||||
onDelete: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
ElevatedCard(
|
||||
modifier = modifier,
|
||||
) {
|
||||
@@ -74,8 +81,23 @@ private fun ExtensionRepoListItem(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.End,
|
||||
) {
|
||||
IconButton(
|
||||
onClick = {
|
||||
val url = "$repo/index.min.json"
|
||||
context.copyToClipboard(url, url)
|
||||
},
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.ContentCopy,
|
||||
contentDescription = stringResource(MR.strings.action_copy_to_clipboard),
|
||||
)
|
||||
}
|
||||
|
||||
IconButton(onClick = onDelete) {
|
||||
Icon(imageVector = Icons.Outlined.Delete, contentDescription = null)
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Delete,
|
||||
contentDescription = stringResource(MR.strings.action_delete),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,15 +42,19 @@ import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.app.ActivityCompat
|
||||
import eu.kanade.domain.ui.UiPreferences
|
||||
import eu.kanade.domain.ui.model.AppTheme
|
||||
import eu.kanade.presentation.manga.components.MangaCover
|
||||
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||
import eu.kanade.tachiyomi.util.system.DeviceUtil
|
||||
import eu.kanade.tachiyomi.util.system.isDynamicColorAvailable
|
||||
import tachiyomi.core.preference.InMemoryPreferenceStore
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.components.material.padding
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
import tachiyomi.presentation.core.util.secondaryItemAlpha
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.fullType
|
||||
|
||||
@Composable
|
||||
internal fun AppThemePreferenceWidget(
|
||||
@@ -258,7 +262,8 @@ fun AppThemePreviewItem(
|
||||
@Composable
|
||||
private fun AppThemesListPreview() {
|
||||
var appTheme by remember { mutableStateOf(AppTheme.DEFAULT) }
|
||||
TachiyomiTheme {
|
||||
Injekt.addSingleton(fullType<UiPreferences>(), UiPreferences(InMemoryPreferenceStore()))
|
||||
TachiyomiTheme(appTheme = appTheme) {
|
||||
Surface {
|
||||
AppThemesList(
|
||||
currentTheme = appTheme,
|
||||
|
||||
@@ -12,7 +12,7 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||
import eu.kanade.presentation.theme.TachiyomiPreviewTheme
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.components.material.padding
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
@@ -43,7 +43,7 @@ internal fun InfoWidget(text: String) {
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun InfoWidgetPreview() {
|
||||
TachiyomiTheme {
|
||||
TachiyomiPreviewTheme {
|
||||
Surface {
|
||||
InfoWidget(text = stringResource(MR.strings.download_ahead_info))
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||
import eu.kanade.presentation.theme.TachiyomiPreviewTheme
|
||||
|
||||
@Composable
|
||||
fun SwitchPreferenceWidget(
|
||||
@@ -40,7 +40,7 @@ fun SwitchPreferenceWidget(
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun SwitchPreferenceWidgetPreview() {
|
||||
TachiyomiTheme {
|
||||
TachiyomiPreviewTheme {
|
||||
Surface {
|
||||
Column {
|
||||
SwitchPreferenceWidget(
|
||||
|
||||
@@ -13,7 +13,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||
import eu.kanade.presentation.theme.TachiyomiPreviewTheme
|
||||
import tachiyomi.presentation.core.util.secondaryItemAlpha
|
||||
|
||||
@Composable
|
||||
@@ -62,7 +62,7 @@ fun TextPreferenceWidget(
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun TextPreferenceWidgetPreview() {
|
||||
TachiyomiTheme {
|
||||
TachiyomiPreviewTheme {
|
||||
Surface {
|
||||
Column {
|
||||
TextPreferenceWidget(
|
||||
|
||||
@@ -33,7 +33,7 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||
import eu.kanade.presentation.theme.TachiyomiPreviewTheme
|
||||
import eu.kanade.tachiyomi.data.database.models.toDomainChapter
|
||||
import eu.kanade.tachiyomi.ui.reader.model.ChapterTransition
|
||||
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
|
||||
@@ -306,7 +306,7 @@ private val FakeChapterLongTitle = previewChapter(
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun TransitionTextPreview() {
|
||||
TachiyomiTheme {
|
||||
TachiyomiPreviewTheme {
|
||||
Surface(modifier = Modifier.padding(48.dp)) {
|
||||
ChapterTransition(
|
||||
transition = ChapterTransition.Next(ReaderChapter(FakeChapter), ReaderChapter(FakeChapter)),
|
||||
@@ -320,7 +320,7 @@ private fun TransitionTextPreview() {
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun TransitionTextLongTitlePreview() {
|
||||
TachiyomiTheme {
|
||||
TachiyomiPreviewTheme {
|
||||
Surface(modifier = Modifier.padding(48.dp)) {
|
||||
ChapterTransition(
|
||||
transition = ChapterTransition.Next(ReaderChapter(FakeChapterLongTitle), ReaderChapter(FakeChapter)),
|
||||
@@ -334,7 +334,7 @@ private fun TransitionTextLongTitlePreview() {
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun TransitionTextWithGapPreview() {
|
||||
TachiyomiTheme {
|
||||
TachiyomiPreviewTheme {
|
||||
Surface(modifier = Modifier.padding(48.dp)) {
|
||||
ChapterTransition(
|
||||
transition = ChapterTransition.Next(ReaderChapter(FakeChapter), ReaderChapter(FakeGapChapter)),
|
||||
@@ -348,7 +348,7 @@ private fun TransitionTextWithGapPreview() {
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun TransitionTextNoNextPreview() {
|
||||
TachiyomiTheme {
|
||||
TachiyomiPreviewTheme {
|
||||
Surface(modifier = Modifier.padding(48.dp)) {
|
||||
ChapterTransition(
|
||||
transition = ChapterTransition.Next(ReaderChapter(FakeChapter), null),
|
||||
@@ -362,7 +362,7 @@ private fun TransitionTextNoNextPreview() {
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun TransitionTextNoPreviousPreview() {
|
||||
TachiyomiTheme {
|
||||
TachiyomiPreviewTheme {
|
||||
Surface(modifier = Modifier.padding(48.dp)) {
|
||||
ChapterTransition(
|
||||
transition = ChapterTransition.Prev(ReaderChapter(FakeChapter), null),
|
||||
|
||||
@@ -16,7 +16,7 @@ import dev.icerock.moko.resources.StringResource
|
||||
import eu.kanade.domain.manga.model.readerOrientation
|
||||
import eu.kanade.presentation.components.AdaptiveSheet
|
||||
import eu.kanade.presentation.reader.components.ModeSelectionDialog
|
||||
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||
import eu.kanade.presentation.theme.TachiyomiPreviewTheme
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel
|
||||
import tachiyomi.i18n.MR
|
||||
@@ -81,7 +81,7 @@ private fun DialogContent(
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun DialogContentPreview() {
|
||||
TachiyomiTheme {
|
||||
TachiyomiPreviewTheme {
|
||||
Surface {
|
||||
Column {
|
||||
DialogContent(
|
||||
|
||||
@@ -12,7 +12,7 @@ import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import androidx.compose.ui.unit.sp
|
||||
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||
import eu.kanade.presentation.theme.TachiyomiPreviewTheme
|
||||
|
||||
@Composable
|
||||
fun PageIndicatorText(
|
||||
@@ -51,7 +51,7 @@ fun PageIndicatorText(
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun PageIndicatorTextPreview() {
|
||||
TachiyomiTheme {
|
||||
TachiyomiPreviewTheme {
|
||||
Surface {
|
||||
PageIndicatorText(currentPage = 10, totalPages = 69)
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import dev.icerock.moko.resources.StringResource
|
||||
import eu.kanade.domain.manga.model.readingMode
|
||||
import eu.kanade.presentation.components.AdaptiveSheet
|
||||
import eu.kanade.presentation.reader.components.ModeSelectionDialog
|
||||
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||
import eu.kanade.presentation.theme.TachiyomiPreviewTheme
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode
|
||||
import tachiyomi.i18n.MR
|
||||
@@ -79,7 +79,7 @@ private fun DialogContent(
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun DialogContentPreview() {
|
||||
TachiyomiTheme {
|
||||
TachiyomiPreviewTheme {
|
||||
Surface {
|
||||
Column {
|
||||
DialogContent(
|
||||
|
||||
@@ -19,7 +19,7 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import androidx.compose.ui.unit.dp
|
||||
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||
import eu.kanade.presentation.theme.TachiyomiPreviewTheme
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.components.SettingsItemsPaddings
|
||||
import tachiyomi.presentation.core.components.material.padding
|
||||
@@ -70,7 +70,7 @@ fun ModeSelectionDialog(
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun Preview() {
|
||||
TachiyomiTheme {
|
||||
TachiyomiPreviewTheme {
|
||||
Surface {
|
||||
Column {
|
||||
ModeSelectionDialog(
|
||||
|
||||
@@ -28,9 +28,30 @@ fun TachiyomiTheme(
|
||||
appTheme: AppTheme? = null,
|
||||
amoled: Boolean? = null,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
val uiPreferences = Injekt.get<UiPreferences>()
|
||||
BaseTachiyomiTheme(
|
||||
appTheme = appTheme ?: uiPreferences.appTheme().get(),
|
||||
isAmoled = amoled ?: uiPreferences.themeDarkAmoled().get(),
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TachiyomiPreviewTheme(
|
||||
appTheme: AppTheme = AppTheme.DEFAULT,
|
||||
isAmoled: Boolean = false,
|
||||
content: @Composable () -> Unit,
|
||||
) = BaseTachiyomiTheme(appTheme, isAmoled, content)
|
||||
|
||||
@Composable
|
||||
private fun BaseTachiyomiTheme(
|
||||
appTheme: AppTheme,
|
||||
isAmoled: Boolean,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
MaterialTheme(
|
||||
colorScheme = getThemeColorScheme(appTheme, amoled),
|
||||
colorScheme = getThemeColorScheme(appTheme, isAmoled),
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
@@ -38,11 +59,10 @@ fun TachiyomiTheme(
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun getThemeColorScheme(
|
||||
appTheme: AppTheme?,
|
||||
amoled: Boolean?,
|
||||
appTheme: AppTheme,
|
||||
isAmoled: Boolean,
|
||||
): ColorScheme {
|
||||
val uiPreferences = Injekt.get<UiPreferences>()
|
||||
val colorScheme = when (appTheme ?: uiPreferences.appTheme().get()) {
|
||||
val colorScheme = when (appTheme) {
|
||||
AppTheme.DEFAULT -> TachiyomiColorScheme
|
||||
AppTheme.MONET -> MonetColorScheme(LocalContext.current)
|
||||
AppTheme.GREEN_APPLE -> GreenAppleColorScheme
|
||||
@@ -59,6 +79,6 @@ private fun getThemeColorScheme(
|
||||
}
|
||||
return colorScheme.getColorScheme(
|
||||
isSystemInDarkTheme(),
|
||||
amoled ?: uiPreferences.themeDarkAmoled().get(),
|
||||
isAmoled,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import eu.kanade.presentation.components.DropdownMenu
|
||||
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||
import eu.kanade.presentation.theme.TachiyomiPreviewTheme
|
||||
import eu.kanade.presentation.track.components.TrackLogoIcon
|
||||
import eu.kanade.tachiyomi.data.track.Tracker
|
||||
import eu.kanade.tachiyomi.ui.manga.track.TrackItem
|
||||
@@ -323,7 +323,7 @@ private fun TrackInfoDialogHomePreviews(
|
||||
@PreviewParameter(TrackInfoDialogHomePreviewProvider::class)
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
TachiyomiTheme {
|
||||
TachiyomiPreviewTheme {
|
||||
Surface {
|
||||
content()
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||
import eu.kanade.presentation.theme.TachiyomiPreviewTheme
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentMapOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
@@ -229,7 +229,7 @@ private fun BaseSelector(
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun TrackStatusSelectorPreviews() {
|
||||
TachiyomiTheme {
|
||||
TachiyomiPreviewTheme {
|
||||
Surface {
|
||||
TrackStatusSelector(
|
||||
selection = 1,
|
||||
|
||||
@@ -68,7 +68,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import eu.kanade.presentation.components.DropdownMenu
|
||||
import eu.kanade.presentation.manga.components.MangaCover
|
||||
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||
import eu.kanade.presentation.theme.TachiyomiPreviewTheme
|
||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
||||
import eu.kanade.tachiyomi.util.system.openInBrowser
|
||||
import tachiyomi.i18n.MR
|
||||
@@ -380,5 +380,5 @@ private fun TrackerSearchPreviews(
|
||||
@PreviewParameter(TrackerSearchPreviewProvider::class)
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
TachiyomiTheme { content() }
|
||||
TachiyomiPreviewTheme { content() }
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||
import eu.kanade.presentation.theme.TachiyomiPreviewTheme
|
||||
import eu.kanade.tachiyomi.data.track.Tracker
|
||||
import tachiyomi.presentation.core.util.clickableNoIndication
|
||||
|
||||
@@ -49,7 +49,7 @@ private fun TrackLogoIconPreviews(
|
||||
@PreviewParameter(TrackLogoIconPreviewProvider::class)
|
||||
tracker: Tracker,
|
||||
) {
|
||||
TachiyomiTheme {
|
||||
TachiyomiPreviewTheme {
|
||||
TrackLogoIcon(
|
||||
tracker = tracker,
|
||||
onClick = null,
|
||||
|
||||
@@ -58,22 +58,24 @@ class MangaRestorer(
|
||||
backupManga: BackupManga,
|
||||
backupCategories: List<BackupCategory>,
|
||||
) {
|
||||
val dbManga = findExistingManga(backupManga)
|
||||
val manga = backupManga.getMangaImpl()
|
||||
val restoredManga = if (dbManga == null) {
|
||||
restoreNewManga(manga)
|
||||
} else {
|
||||
restoreExistingManga(manga, dbManga)
|
||||
}
|
||||
handler.await(inTransaction = true) {
|
||||
val dbManga = findExistingManga(backupManga)
|
||||
val manga = backupManga.getMangaImpl()
|
||||
val restoredManga = if (dbManga == null) {
|
||||
restoreNewManga(manga)
|
||||
} else {
|
||||
restoreExistingManga(manga, dbManga)
|
||||
}
|
||||
|
||||
restoreMangaDetails(
|
||||
manga = restoredManga,
|
||||
chapters = backupManga.chapters,
|
||||
categories = backupManga.categories,
|
||||
backupCategories = backupCategories,
|
||||
history = backupManga.history + backupManga.brokenHistory.map { it.toBackupHistory() },
|
||||
tracks = backupManga.tracking,
|
||||
)
|
||||
restoreMangaDetails(
|
||||
manga = restoredManga,
|
||||
chapters = backupManga.chapters,
|
||||
categories = backupManga.categories,
|
||||
backupCategories = backupCategories,
|
||||
history = backupManga.history + backupManga.brokenHistory.map { it.toBackupHistory() },
|
||||
tracks = backupManga.tracking,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun findExistingManga(backupManga: BackupManga): Manga? {
|
||||
|
||||
@@ -103,12 +103,15 @@ class DownloadCache(
|
||||
scope.launch {
|
||||
rootDownloadsDirLock.withLock {
|
||||
try {
|
||||
val diskCache = diskCacheFile.inputStream().use {
|
||||
ProtoBuf.decodeFromByteArray<RootDirectory>(it.readBytes())
|
||||
if (diskCacheFile.exists()) {
|
||||
val diskCache = diskCacheFile.inputStream().use {
|
||||
ProtoBuf.decodeFromByteArray<RootDirectory>(it.readBytes())
|
||||
}
|
||||
rootDownloadsDir = diskCache
|
||||
lastRenew = System.currentTimeMillis()
|
||||
}
|
||||
rootDownloadsDir = diskCache
|
||||
lastRenew = System.currentTimeMillis()
|
||||
} catch (e: Throwable) {
|
||||
logcat(LogPriority.ERROR, e) { "Failed to initialize disk cache" }
|
||||
diskCacheFile.delete()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +80,6 @@ class SyncDataJob(private val context: Context, workerParams: WorkerParameters)
|
||||
TimeUnit.MINUTES,
|
||||
)
|
||||
.addTag(TAG_AUTO)
|
||||
// No initial delay is needed, remove the randomDelayMillis
|
||||
.build()
|
||||
|
||||
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.
|
||||
*/
|
||||
suspend fun syncData() {
|
||||
val syncOptions = syncPreferences.getSyncOptions()
|
||||
val databaseManga = getAllMangaFromDB()
|
||||
val backupOptions = BackupOptions(
|
||||
libraryEntries = true,
|
||||
categories = true,
|
||||
chapters = true,
|
||||
tracking = true,
|
||||
history = true,
|
||||
appSettings = true,
|
||||
sourceSettings = true,
|
||||
privateSettings = true,
|
||||
libraryEntries = syncOptions.libraryEntries,
|
||||
categories = syncOptions.categories,
|
||||
chapters = syncOptions.chapters,
|
||||
tracking = syncOptions.tracking,
|
||||
history = syncOptions.history,
|
||||
appSettings = syncOptions.appSettings,
|
||||
sourceSettings = syncOptions.sourceSettings,
|
||||
privateSettings = syncOptions.privateSettings,
|
||||
)
|
||||
val backup = Backup(
|
||||
backupManga = backupCreator.backupMangas(databaseManga, backupOptions),
|
||||
@@ -119,10 +120,17 @@ class SyncManager(
|
||||
|
||||
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
|
||||
if (syncPreferences.lastSyncTimestamp().get() == 0L && databaseManga.isNotEmpty()) {
|
||||
// It's first sync no need to restore data. (just update remote data)
|
||||
syncPreferences.lastSyncTimestamp().set(Date().time)
|
||||
notifier.showSyncSuccess("Updated remote data successfully")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -130,10 +138,8 @@ class SyncManager(
|
||||
val (filteredFavorites, nonFavorites) = filterFavoritesAndNonFavorites(remoteBackup)
|
||||
updateNonFavorites(nonFavorites)
|
||||
|
||||
val mangas = processFavoriteManga(filteredFavorites)
|
||||
|
||||
val newSyncData = backup.copy(
|
||||
backupManga = mangas,
|
||||
backupManga = filteredFavorites,
|
||||
backupCategories = remoteBackup.backupCategories,
|
||||
backupSources = remoteBackup.backupSources,
|
||||
backupPreferences = remoteBackup.backupPreferences,
|
||||
@@ -141,9 +147,10 @@ class SyncManager(
|
||||
)
|
||||
|
||||
// It's local sync no need to restore data. (just update remote data)
|
||||
if (mangas.isEmpty()) {
|
||||
if (filteredFavorites.isEmpty()) {
|
||||
// update the sync timestamp
|
||||
syncPreferences.lastSyncTimestamp().set(Date().time)
|
||||
notifier.showSyncSuccess("Sync completed successfully")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -328,6 +335,7 @@ class SyncManager(
|
||||
val favorites = mutableListOf<BackupManga>()
|
||||
val nonFavorites = mutableListOf<BackupManga>()
|
||||
val logTag = "filterFavoritesAndNonFavorites"
|
||||
|
||||
val elapsedTimeMillis = measureTimeMillis {
|
||||
val databaseMangaFavorites = getFavorites.await()
|
||||
val localMangaMap = databaseMangaFavorites.associateBy {
|
||||
@@ -368,51 +376,6 @@ class SyncManager(
|
||||
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.
|
||||
* @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)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,15 +35,7 @@ internal class ExtensionApi {
|
||||
|
||||
suspend fun findExtensions(): List<Extension.Available> {
|
||||
return withIOContext {
|
||||
val extensions = sourcePreferences.extensionRepos().get().flatMap { getExtensions(it) }
|
||||
|
||||
// Sanity check - a small number of extensions probably means something broke
|
||||
// with the repo generator
|
||||
if (extensions.isEmpty()) {
|
||||
throw Exception()
|
||||
}
|
||||
|
||||
extensions
|
||||
sourcePreferences.extensionRepos().get().flatMap { getExtensions(it) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package eu.kanade.tachiyomi.ui.browse.extension
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Translate
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -10,6 +8,7 @@ import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import eu.kanade.presentation.browse.ExtensionScreen
|
||||
import eu.kanade.presentation.components.AppBar
|
||||
import eu.kanade.presentation.components.TabContent
|
||||
import eu.kanade.presentation.more.settings.screen.browse.ExtensionReposScreen
|
||||
import eu.kanade.tachiyomi.extension.model.Extension
|
||||
import eu.kanade.tachiyomi.ui.browse.extension.details.ExtensionDetailsScreen
|
||||
import eu.kanade.tachiyomi.ui.webview.WebViewScreen
|
||||
@@ -29,11 +28,14 @@ fun extensionsTab(
|
||||
badgeNumber = state.updates.takeIf { it > 0 },
|
||||
searchEnabled = true,
|
||||
actions = persistentListOf(
|
||||
AppBar.Action(
|
||||
AppBar.OverflowAction(
|
||||
title = stringResource(MR.strings.action_filter),
|
||||
icon = Icons.Outlined.Translate,
|
||||
onClick = { navigator.push(ExtensionFilterScreen()) },
|
||||
),
|
||||
AppBar.OverflowAction(
|
||||
title = stringResource(MR.strings.label_extension_repos),
|
||||
onClick = { navigator.push(ExtensionReposScreen()) },
|
||||
),
|
||||
),
|
||||
content = { contentPadding, _ ->
|
||||
ExtensionScreen(
|
||||
|
||||
@@ -37,12 +37,14 @@ import eu.kanade.presentation.more.onboarding.GETTING_STARTED_URL
|
||||
import eu.kanade.presentation.util.Tab
|
||||
import eu.kanade.tachiyomi.R
|
||||
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.category.CategoryScreen
|
||||
import eu.kanade.tachiyomi.ui.home.HomeScreen
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaScreen
|
||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
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,
|
||||
onSearchQueryChange = screenModel::search,
|
||||
scrollBehavior = scrollBehavior.takeIf { !tabVisible }, // For scroll overlay when no tab
|
||||
|
||||
@@ -102,7 +102,14 @@ abstract class PagerViewer(val activity: ReaderActivity) : Viewer {
|
||||
},
|
||||
)
|
||||
pager.tapListener = { event ->
|
||||
val pos = PointF(event.x / pager.width, event.y / pager.height)
|
||||
val viewPosition = IntArray(2)
|
||||
pager.getLocationOnScreen(viewPosition)
|
||||
val viewPositionRelativeToWindow = IntArray(2)
|
||||
pager.getLocationInWindow(viewPositionRelativeToWindow)
|
||||
val pos = PointF(
|
||||
(event.rawX - viewPosition[0] + viewPositionRelativeToWindow[0]) / pager.width,
|
||||
(event.rawY - viewPosition[1] + viewPositionRelativeToWindow[1]) / pager.height,
|
||||
)
|
||||
when (config.navigator.getAction(pos)) {
|
||||
NavigationRegion.MENU -> activity.toggleMenu()
|
||||
NavigationRegion.NEXT -> moveToNext()
|
||||
|
||||
@@ -111,7 +111,14 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr
|
||||
},
|
||||
)
|
||||
recycler.tapListener = { event ->
|
||||
val pos = PointF(event.x / recycler.width, event.y / recycler.height)
|
||||
val viewPosition = IntArray(2)
|
||||
recycler.getLocationOnScreen(viewPosition)
|
||||
val viewPositionRelativeToWindow = IntArray(2)
|
||||
recycler.getLocationInWindow(viewPositionRelativeToWindow)
|
||||
val pos = PointF(
|
||||
(event.rawX - viewPosition[0] + viewPositionRelativeToWindow[0]) / recycler.width,
|
||||
(event.rawY - viewPosition[1] + viewPositionRelativeToWindow[1]) / recycler.height,
|
||||
)
|
||||
when (config.navigator.getAction(pos)) {
|
||||
NavigationRegion.MENU -> activity.toggleMenu()
|
||||
NavigationRegion.NEXT, NavigationRegion.RIGHT -> scrollDown()
|
||||
|
||||
Reference in New Issue
Block a user