mirror of
https://github.com/mihonapp/mihon.git
synced 2025-01-30 20:04:56 +01:00
Added library update errors screen
(cherry picked from commit 7cf37d52f959ac65f53cf7657563fb4428bd9188)
This commit is contained in:
parent
f7752a98b2
commit
ee78212c75
@ -37,6 +37,9 @@ import mihon.domain.upcoming.interactor.GetUpcomingManga
|
||||
import tachiyomi.data.category.CategoryRepositoryImpl
|
||||
import tachiyomi.data.chapter.ChapterRepositoryImpl
|
||||
import tachiyomi.data.history.HistoryRepositoryImpl
|
||||
import tachiyomi.data.libraryUpdateError.LibraryUpdateErrorRepositoryImpl
|
||||
import tachiyomi.data.libraryUpdateError.LibraryUpdateErrorWithRelationsRepositoryImpl
|
||||
import tachiyomi.data.libraryUpdateErrorMessage.LibraryUpdateErrorMessageRepositoryImpl
|
||||
import tachiyomi.data.manga.MangaRepositoryImpl
|
||||
import tachiyomi.data.release.ReleaseServiceImpl
|
||||
import tachiyomi.data.source.SourceRepositoryImpl
|
||||
@ -67,6 +70,16 @@ import tachiyomi.domain.history.interactor.GetTotalReadDuration
|
||||
import tachiyomi.domain.history.interactor.RemoveHistory
|
||||
import tachiyomi.domain.history.interactor.UpsertHistory
|
||||
import tachiyomi.domain.history.repository.HistoryRepository
|
||||
import tachiyomi.domain.libraryUpdateError.interactor.DeleteLibraryUpdateErrors
|
||||
import tachiyomi.domain.libraryUpdateError.interactor.GetLibraryUpdateErrorWithRelations
|
||||
import tachiyomi.domain.libraryUpdateError.interactor.GetLibraryUpdateErrors
|
||||
import tachiyomi.domain.libraryUpdateError.interactor.InsertLibraryUpdateErrors
|
||||
import tachiyomi.domain.libraryUpdateError.repository.LibraryUpdateErrorRepository
|
||||
import tachiyomi.domain.libraryUpdateError.repository.LibraryUpdateErrorWithRelationsRepository
|
||||
import tachiyomi.domain.libraryUpdateErrorMessage.interactor.DeleteLibraryUpdateErrorMessages
|
||||
import tachiyomi.domain.libraryUpdateErrorMessage.interactor.GetLibraryUpdateErrorMessages
|
||||
import tachiyomi.domain.libraryUpdateErrorMessage.interactor.InsertLibraryUpdateErrorMessages
|
||||
import tachiyomi.domain.libraryUpdateErrorMessage.repository.LibraryUpdateErrorMessageRepository
|
||||
import tachiyomi.domain.manga.interactor.FetchInterval
|
||||
import tachiyomi.domain.manga.interactor.GetDuplicateLibraryManga
|
||||
import tachiyomi.domain.manga.interactor.GetFavorites
|
||||
@ -191,5 +204,20 @@ class DomainModule : InjektModule {
|
||||
addFactory { DeleteExtensionRepo(get()) }
|
||||
addFactory { ReplaceExtensionRepo(get()) }
|
||||
addFactory { UpdateExtensionRepo(get(), get()) }
|
||||
|
||||
addSingletonFactory<LibraryUpdateErrorWithRelationsRepository> {
|
||||
LibraryUpdateErrorWithRelationsRepositoryImpl(get())
|
||||
}
|
||||
addFactory { GetLibraryUpdateErrorWithRelations(get()) }
|
||||
|
||||
addSingletonFactory<LibraryUpdateErrorMessageRepository> { LibraryUpdateErrorMessageRepositoryImpl(get()) }
|
||||
addFactory { GetLibraryUpdateErrorMessages(get()) }
|
||||
addFactory { DeleteLibraryUpdateErrorMessages(get()) }
|
||||
addFactory { InsertLibraryUpdateErrorMessages(get()) }
|
||||
|
||||
addSingletonFactory<LibraryUpdateErrorRepository> { LibraryUpdateErrorRepositoryImpl(get()) }
|
||||
addFactory { GetLibraryUpdateErrors(get()) }
|
||||
addFactory { DeleteLibraryUpdateErrors(get()) }
|
||||
addFactory { InsertLibraryUpdateErrors(get()) }
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,240 @@
|
||||
package eu.kanade.presentation.libraryUpdateError
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.expandVertically
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.shrinkVertically
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||
import androidx.compose.foundation.layout.asPaddingValues
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.only
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.ZeroCornerSize
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.FindReplace
|
||||
import androidx.compose.material.icons.outlined.FlipToBack
|
||||
import androidx.compose.material.icons.outlined.SelectAll
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
import androidx.compose.material3.ripple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import eu.kanade.presentation.components.AppBar
|
||||
import eu.kanade.presentation.libraryUpdateError.components.libraryUpdateErrorUiItems
|
||||
import eu.kanade.tachiyomi.ui.libraryUpdateError.LibraryUpdateErrorItem
|
||||
import eu.kanade.tachiyomi.ui.libraryUpdateError.LibraryUpdateErrorScreenState
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.components.FastScrollLazyColumn
|
||||
import tachiyomi.presentation.core.components.material.Scaffold
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
import tachiyomi.presentation.core.screens.EmptyScreen
|
||||
import tachiyomi.presentation.core.screens.LoadingScreen
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
@Composable
|
||||
fun LibraryUpdateErrorScreen(
|
||||
state: LibraryUpdateErrorScreenState,
|
||||
modifier: Modifier = Modifier,
|
||||
onClick: (LibraryUpdateErrorItem) -> Unit,
|
||||
onClickCover: (LibraryUpdateErrorItem) -> Unit,
|
||||
onMultiMigrateClicked: (() -> Unit),
|
||||
onSelectAll: (Boolean) -> Unit,
|
||||
onInvertSelection: () -> Unit,
|
||||
onErrorSelected: (LibraryUpdateErrorItem, Boolean, Boolean, Boolean) -> Unit,
|
||||
navigateUp: () -> Unit,
|
||||
) {
|
||||
BackHandler(enabled = state.selectionMode, onBack = { onSelectAll(false) })
|
||||
|
||||
Scaffold(
|
||||
topBar = { scrollBehavior ->
|
||||
LibraryUpdateErrorsAppBar(
|
||||
title = stringResource(
|
||||
MR.strings.label_library_update_errors,
|
||||
state.items.size,
|
||||
),
|
||||
actionModeCounter = state.selected.size,
|
||||
onSelectAll = { onSelectAll(true) },
|
||||
onInvertSelection = onInvertSelection,
|
||||
onCancelActionMode = { onSelectAll(false) },
|
||||
scrollBehavior = scrollBehavior,
|
||||
navigateUp = navigateUp,
|
||||
)
|
||||
},
|
||||
bottomBar = {
|
||||
AnimatedVisibility(
|
||||
visible = state.selected.isNotEmpty(),
|
||||
enter = expandVertically(expandFrom = Alignment.Bottom),
|
||||
exit = shrinkVertically(shrinkTowards = Alignment.Bottom),
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
Surface(
|
||||
modifier = modifier,
|
||||
shape = MaterialTheme.shapes.large.copy(
|
||||
bottomEnd = ZeroCornerSize,
|
||||
bottomStart = ZeroCornerSize,
|
||||
),
|
||||
tonalElevation = 3.dp,
|
||||
) {
|
||||
val haptic = LocalHapticFeedback.current
|
||||
val confirm = remember { mutableStateListOf(false) }
|
||||
var resetJob: Job? = remember { null }
|
||||
val onLongClickItem: (Int) -> Unit = { toConfirmIndex ->
|
||||
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||
(0 until 1).forEach { i -> confirm[i] = i == toConfirmIndex }
|
||||
resetJob?.cancel()
|
||||
resetJob = scope.launch {
|
||||
delay(1.seconds)
|
||||
if (isActive) confirm[toConfirmIndex] = false
|
||||
}
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
WindowInsets.navigationBars
|
||||
.only(WindowInsetsSides.Bottom)
|
||||
.asPaddingValues(),
|
||||
)
|
||||
.padding(horizontal = 8.dp, vertical = 12.dp),
|
||||
) {
|
||||
Button(
|
||||
title = stringResource(MR.strings.migrate),
|
||||
icon = Icons.Outlined.FindReplace,
|
||||
toConfirm = confirm[0],
|
||||
onLongClick = { onLongClickItem(0) },
|
||||
onClick = onMultiMigrateClicked,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
) { paddingValues ->
|
||||
when {
|
||||
state.isLoading -> LoadingScreen(modifier = Modifier.padding(paddingValues))
|
||||
state.items.isEmpty() -> EmptyScreen(
|
||||
message = stringResource(MR.strings.info_empty_library_update_errors),
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
)
|
||||
|
||||
else -> {
|
||||
FastScrollLazyColumn(
|
||||
contentPadding = paddingValues,
|
||||
) {
|
||||
libraryUpdateErrorUiItems(
|
||||
uiModels = state.getUiModel(),
|
||||
selectionMode = state.selectionMode,
|
||||
onErrorSelected = onErrorSelected,
|
||||
onClick = onClick,
|
||||
onClickCover = onClickCover,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RowScope.Button(
|
||||
title: String,
|
||||
icon: ImageVector,
|
||||
toConfirm: Boolean,
|
||||
onLongClick: () -> Unit,
|
||||
onClick: (() -> Unit),
|
||||
content: (@Composable () -> Unit)? = null,
|
||||
) {
|
||||
val animatedWeight by animateFloatAsState(if (toConfirm) 2f else 1f)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.weight(animatedWeight)
|
||||
.combinedClickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = ripple(bounded = false),
|
||||
onLongClick = onLongClick,
|
||||
onClick = onClick,
|
||||
),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = title,
|
||||
)
|
||||
AnimatedVisibility(
|
||||
visible = toConfirm,
|
||||
enter = expandVertically(expandFrom = Alignment.Top) + fadeIn(),
|
||||
exit = shrinkVertically(shrinkTowards = Alignment.Top) + fadeOut(),
|
||||
) {
|
||||
Text(
|
||||
text = title,
|
||||
overflow = TextOverflow.Visible,
|
||||
maxLines = 1,
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
)
|
||||
}
|
||||
content?.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LibraryUpdateErrorsAppBar(
|
||||
modifier: Modifier = Modifier,
|
||||
title: String,
|
||||
actionModeCounter: Int,
|
||||
onSelectAll: () -> Unit,
|
||||
onInvertSelection: () -> Unit,
|
||||
onCancelActionMode: () -> Unit,
|
||||
scrollBehavior: TopAppBarScrollBehavior,
|
||||
navigateUp: () -> Unit,
|
||||
) {
|
||||
AppBar(
|
||||
modifier = modifier,
|
||||
title = title,
|
||||
scrollBehavior = scrollBehavior,
|
||||
actionModeCounter = actionModeCounter,
|
||||
onCancelActionMode = onCancelActionMode,
|
||||
actionModeActions = {
|
||||
IconButton(onClick = onSelectAll) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.SelectAll,
|
||||
contentDescription = stringResource(MR.strings.action_select_all),
|
||||
)
|
||||
}
|
||||
IconButton(onClick = onInvertSelection) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.FlipToBack,
|
||||
contentDescription = stringResource(MR.strings.action_select_inverse),
|
||||
)
|
||||
}
|
||||
},
|
||||
navigateUp = navigateUp,
|
||||
)
|
||||
}
|
@ -0,0 +1,156 @@
|
||||
package eu.kanade.presentation.libraryUpdateError.components
|
||||
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import eu.kanade.presentation.manga.components.MangaCover
|
||||
import eu.kanade.tachiyomi.ui.libraryUpdateError.LibraryUpdateErrorItem
|
||||
import tachiyomi.domain.libraryUpdateError.model.LibraryUpdateErrorWithRelations
|
||||
import tachiyomi.domain.source.service.SourceManager
|
||||
import tachiyomi.presentation.core.components.ListGroupHeader
|
||||
import tachiyomi.presentation.core.components.material.padding
|
||||
import tachiyomi.presentation.core.util.secondaryItemAlpha
|
||||
import tachiyomi.presentation.core.util.selectedBackground
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
internal fun LazyListScope.libraryUpdateErrorUiItems(
|
||||
uiModels: List<LibraryUpdateErrorUiModel>,
|
||||
selectionMode: Boolean,
|
||||
onErrorSelected: (LibraryUpdateErrorItem, Boolean, Boolean, Boolean) -> Unit,
|
||||
onClick: (LibraryUpdateErrorItem) -> Unit,
|
||||
onClickCover: (LibraryUpdateErrorItem) -> Unit,
|
||||
) {
|
||||
items(
|
||||
items = uiModels,
|
||||
contentType = {
|
||||
when (it) {
|
||||
is LibraryUpdateErrorUiModel.Header -> "header"
|
||||
is LibraryUpdateErrorUiModel.Item -> "item"
|
||||
}
|
||||
},
|
||||
key = {
|
||||
when (it) {
|
||||
is LibraryUpdateErrorUiModel.Header -> "sticky:errorHeader-${it.hashCode()}"
|
||||
is LibraryUpdateErrorUiModel.Item -> "error-${it.item.error.errorId}-${it.item.error.mangaId}"
|
||||
}
|
||||
},
|
||||
) { item ->
|
||||
when (item) {
|
||||
is LibraryUpdateErrorUiModel.Header -> {
|
||||
ListGroupHeader(
|
||||
modifier = Modifier.animateItemPlacement(),
|
||||
text = item.errorMessage,
|
||||
)
|
||||
}
|
||||
|
||||
is LibraryUpdateErrorUiModel.Item -> {
|
||||
val libraryUpdateErrorItem = item.item
|
||||
LibraryUpdateErrorUiItem(
|
||||
modifier = Modifier.animateItemPlacement(),
|
||||
error = libraryUpdateErrorItem.error,
|
||||
selected = libraryUpdateErrorItem.selected,
|
||||
onClick = {
|
||||
when {
|
||||
selectionMode -> onErrorSelected(
|
||||
libraryUpdateErrorItem,
|
||||
!libraryUpdateErrorItem.selected,
|
||||
true,
|
||||
false,
|
||||
)
|
||||
|
||||
else -> onClick(libraryUpdateErrorItem)
|
||||
}
|
||||
},
|
||||
onLongClick = {
|
||||
onErrorSelected(
|
||||
libraryUpdateErrorItem,
|
||||
!libraryUpdateErrorItem.selected,
|
||||
true,
|
||||
true,
|
||||
)
|
||||
},
|
||||
onClickCover = { onClickCover(libraryUpdateErrorItem) }.takeIf { !selectionMode },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LibraryUpdateErrorUiItem(
|
||||
modifier: Modifier,
|
||||
error: LibraryUpdateErrorWithRelations,
|
||||
selected: Boolean,
|
||||
onClick: () -> Unit,
|
||||
onLongClick: () -> Unit,
|
||||
onClickCover: (() -> Unit)?,
|
||||
) {
|
||||
val haptic = LocalHapticFeedback.current
|
||||
|
||||
Row(
|
||||
modifier = modifier
|
||||
.selectedBackground(selected)
|
||||
.combinedClickable(
|
||||
onClick = onClick,
|
||||
onLongClick = {
|
||||
onLongClick()
|
||||
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||
},
|
||||
)
|
||||
.padding(horizontal = MaterialTheme.padding.medium),
|
||||
verticalAlignment = Alignment.Top,
|
||||
) {
|
||||
MangaCover.Square(
|
||||
modifier = Modifier
|
||||
.padding(vertical = 6.dp)
|
||||
.height(48.dp),
|
||||
data = error.mangaCover,
|
||||
onClick = onClickCover,
|
||||
)
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = MaterialTheme.padding.medium, vertical = 5.dp)
|
||||
.weight(1f),
|
||||
) {
|
||||
Text(
|
||||
text = error.mangaTitle,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
overflow = TextOverflow.Visible,
|
||||
)
|
||||
|
||||
Row(modifier = Modifier.padding(vertical = 4.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(
|
||||
text = Injekt.get<SourceManager>().getOrStub(error.mangaSource).name,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
overflow = TextOverflow.Visible,
|
||||
maxLines = 1,
|
||||
modifier = Modifier
|
||||
.secondaryItemAlpha()
|
||||
.weight(weight = 1f, fill = false),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class LibraryUpdateErrorUiModel {
|
||||
|
||||
data class Header(val errorMessage: String) : LibraryUpdateErrorUiModel()
|
||||
|
||||
data class Item(val item: LibraryUpdateErrorItem) : LibraryUpdateErrorUiModel()
|
||||
}
|
@ -13,6 +13,7 @@ import androidx.compose.material.icons.automirrored.outlined.Label
|
||||
import androidx.compose.material.icons.outlined.CloudOff
|
||||
import androidx.compose.material.icons.outlined.GetApp
|
||||
import androidx.compose.material.icons.outlined.Info
|
||||
import androidx.compose.material.icons.outlined.NewReleases
|
||||
import androidx.compose.material.icons.outlined.QueryStats
|
||||
import androidx.compose.material.icons.outlined.Settings
|
||||
import androidx.compose.material.icons.outlined.Storage
|
||||
@ -47,6 +48,7 @@ fun MoreScreen(
|
||||
onClickDataAndStorage: () -> Unit,
|
||||
onClickSettings: () -> Unit,
|
||||
onClickAbout: () -> Unit,
|
||||
onClickLibraryUpdateErrors: () -> Unit,
|
||||
) {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
|
||||
@ -133,6 +135,13 @@ fun MoreScreen(
|
||||
onPreferenceClick = onClickStats,
|
||||
)
|
||||
}
|
||||
item {
|
||||
TextPreferenceWidget(
|
||||
title = stringResource(MR.strings.option_label_library_update_errors),
|
||||
icon = Icons.Outlined.NewReleases,
|
||||
onPreferenceClick = onClickLibraryUpdateErrors,
|
||||
)
|
||||
}
|
||||
item {
|
||||
TextPreferenceWidget(
|
||||
title = stringResource(MR.strings.label_data_storage),
|
||||
|
@ -55,6 +55,12 @@ import tachiyomi.domain.library.service.LibraryPreferences.Companion.MANGA_HAS_U
|
||||
import tachiyomi.domain.library.service.LibraryPreferences.Companion.MANGA_NON_COMPLETED
|
||||
import tachiyomi.domain.library.service.LibraryPreferences.Companion.MANGA_NON_READ
|
||||
import tachiyomi.domain.library.service.LibraryPreferences.Companion.MANGA_OUTSIDE_RELEASE_PERIOD
|
||||
import tachiyomi.domain.libraryUpdateError.interactor.DeleteLibraryUpdateErrors
|
||||
import tachiyomi.domain.libraryUpdateError.interactor.InsertLibraryUpdateErrors
|
||||
import tachiyomi.domain.libraryUpdateError.model.LibraryUpdateError
|
||||
import tachiyomi.domain.libraryUpdateErrorMessage.interactor.DeleteLibraryUpdateErrorMessages
|
||||
import tachiyomi.domain.libraryUpdateErrorMessage.interactor.InsertLibraryUpdateErrorMessages
|
||||
import tachiyomi.domain.libraryUpdateErrorMessage.model.LibraryUpdateErrorMessage
|
||||
import tachiyomi.domain.manga.interactor.FetchInterval
|
||||
import tachiyomi.domain.manga.interactor.GetLibraryManga
|
||||
import tachiyomi.domain.manga.interactor.GetManga
|
||||
@ -86,6 +92,11 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
||||
private val fetchInterval: FetchInterval = Injekt.get()
|
||||
private val filterChaptersForDownload: FilterChaptersForDownload = Injekt.get()
|
||||
|
||||
private val deleteLibraryUpdateErrorMessages: DeleteLibraryUpdateErrorMessages = Injekt.get()
|
||||
private val deleteLibraryUpdateErrors: DeleteLibraryUpdateErrors = Injekt.get()
|
||||
private val insertLibraryUpdateErrors: InsertLibraryUpdateErrors = Injekt.get()
|
||||
private val insertLibraryUpdateErrorMessages: InsertLibraryUpdateErrorMessages = Injekt.get()
|
||||
|
||||
private val notifier = LibraryUpdateNotifier(context)
|
||||
|
||||
private var mangaToUpdate: List<LibraryManga> = mutableListOf()
|
||||
@ -310,6 +321,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
||||
}
|
||||
|
||||
if (failedUpdates.isNotEmpty()) {
|
||||
writeErrorsToDB(failedUpdates)
|
||||
val errorFile = writeErrorFile(failedUpdates)
|
||||
notifier.showUpdateErrorNotification(
|
||||
failedUpdates.size,
|
||||
@ -406,6 +418,24 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
||||
return File("")
|
||||
}
|
||||
|
||||
private suspend fun writeErrorsToDB(errors: List<Pair<Manga, String?>>) {
|
||||
deleteLibraryUpdateErrorMessages.await()
|
||||
deleteLibraryUpdateErrors.await()
|
||||
val libraryErrors = errors.groupBy({ it.second }, { it.first })
|
||||
val errorMessages = insertLibraryUpdateErrorMessages.insertAll(
|
||||
libraryUpdateErrorMessages = libraryErrors.keys.map { errorMessage ->
|
||||
LibraryUpdateErrorMessage(-1L, errorMessage.orEmpty())
|
||||
},
|
||||
)
|
||||
val errorList = mutableListOf<LibraryUpdateError>()
|
||||
errorMessages.forEach {
|
||||
libraryErrors[it.second]?.forEach { manga ->
|
||||
errorList.add(LibraryUpdateError(id = -1L, mangaId = manga.id, messageId = it.first))
|
||||
}
|
||||
}
|
||||
insertLibraryUpdateErrors.insertAll(errorList)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "LibraryUpdate"
|
||||
private const val WORK_NAME_AUTO = "LibraryUpdate-auto"
|
||||
|
@ -0,0 +1,48 @@
|
||||
package eu.kanade.tachiyomi.ui.libraryUpdateError
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import cafe.adriel.voyager.core.model.rememberScreenModel
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import eu.kanade.presentation.libraryUpdateError.LibraryUpdateErrorScreen
|
||||
import eu.kanade.presentation.util.Screen
|
||||
import eu.kanade.tachiyomi.ui.browse.migration.advanced.design.PreMigrationScreen
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaScreen
|
||||
import tachiyomi.domain.UnsortedPreferences
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
class LibraryUpdateErrorScreen : Screen() {
|
||||
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
val screenModel = rememberScreenModel { LibraryUpdateErrorScreenModel() }
|
||||
val state by screenModel.state.collectAsState()
|
||||
|
||||
LibraryUpdateErrorScreen(
|
||||
state = state,
|
||||
onClick = { item ->
|
||||
PreMigrationScreen.navigateToMigration(
|
||||
Injekt.get<UnsortedPreferences>().skipPreMigration().get(),
|
||||
navigator,
|
||||
listOf(item.error.mangaId),
|
||||
)
|
||||
},
|
||||
onClickCover = { item -> navigator.push(MangaScreen(item.error.mangaId)) },
|
||||
onMultiMigrateClicked = {
|
||||
PreMigrationScreen.navigateToMigration(
|
||||
Injekt.get<UnsortedPreferences>().skipPreMigration().get(),
|
||||
navigator,
|
||||
state.selected.map { it.error.mangaId },
|
||||
)
|
||||
},
|
||||
onSelectAll = screenModel::toggleAllSelection,
|
||||
onInvertSelection = screenModel::invertSelection,
|
||||
onErrorSelected = screenModel::toggleSelection,
|
||||
navigateUp = navigator::pop,
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,168 @@
|
||||
package eu.kanade.tachiyomi.ui.libraryUpdateError
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import cafe.adriel.voyager.core.model.StateScreenModel
|
||||
import cafe.adriel.voyager.core.model.screenModelScope
|
||||
import eu.kanade.core.util.addOrRemove
|
||||
import eu.kanade.presentation.libraryUpdateError.components.LibraryUpdateErrorUiModel
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.update
|
||||
import tachiyomi.core.common.util.lang.launchIO
|
||||
import tachiyomi.domain.libraryUpdateError.interactor.GetLibraryUpdateErrorWithRelations
|
||||
import tachiyomi.domain.libraryUpdateError.model.LibraryUpdateErrorWithRelations
|
||||
import tachiyomi.domain.libraryUpdateErrorMessage.interactor.GetLibraryUpdateErrorMessages
|
||||
import tachiyomi.domain.libraryUpdateErrorMessage.model.LibraryUpdateErrorMessage
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
class LibraryUpdateErrorScreenModel(
|
||||
private val getLibraryUpdateErrorWithRelations: GetLibraryUpdateErrorWithRelations = Injekt.get(),
|
||||
private val getLibraryUpdateErrorMessages: GetLibraryUpdateErrorMessages = Injekt.get(),
|
||||
) : StateScreenModel<LibraryUpdateErrorScreenState>(LibraryUpdateErrorScreenState()) {
|
||||
|
||||
// First and last selected index in list
|
||||
private val selectedPositions: Array<Int> = arrayOf(-1, -1)
|
||||
private val selectedErrorIds: HashSet<Long> = HashSet()
|
||||
|
||||
init {
|
||||
screenModelScope.launchIO {
|
||||
getLibraryUpdateErrorWithRelations.subscribeAll()
|
||||
.collectLatest { errors ->
|
||||
val errorMessages = getLibraryUpdateErrorMessages.await()
|
||||
mutableState.update {
|
||||
it.copy(
|
||||
isLoading = false,
|
||||
items = toLibraryUpdateErrorItems(errors),
|
||||
messages = errorMessages,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun toLibraryUpdateErrorItems(errors: List<LibraryUpdateErrorWithRelations>): List<LibraryUpdateErrorItem> {
|
||||
return errors.map { error ->
|
||||
LibraryUpdateErrorItem(
|
||||
error = error,
|
||||
selected = error.errorId in selectedErrorIds,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun toggleSelection(
|
||||
item: LibraryUpdateErrorItem,
|
||||
selected: Boolean,
|
||||
userSelected: Boolean = false,
|
||||
fromLongPress: Boolean = false,
|
||||
) {
|
||||
mutableState.update { state ->
|
||||
val newItems = state.items.toMutableList().apply {
|
||||
val selectedIndex = indexOfFirst { it.error.errorId == item.error.errorId }
|
||||
if (selectedIndex < 0) return@apply
|
||||
|
||||
val selectedItem = get(selectedIndex)
|
||||
if (selectedItem.selected == selected) return@apply
|
||||
|
||||
val firstSelection = none { it.selected }
|
||||
set(selectedIndex, selectedItem.copy(selected = selected))
|
||||
selectedErrorIds.addOrRemove(item.error.errorId, selected)
|
||||
|
||||
if (selected && userSelected && fromLongPress) {
|
||||
if (firstSelection) {
|
||||
selectedPositions[0] = selectedIndex
|
||||
selectedPositions[1] = selectedIndex
|
||||
} else {
|
||||
// Try to select the items in-between when possible
|
||||
val range: IntRange
|
||||
if (selectedIndex < selectedPositions[0]) {
|
||||
range = selectedIndex + 1 until selectedPositions[0]
|
||||
selectedPositions[0] = selectedIndex
|
||||
} else if (selectedIndex > selectedPositions[1]) {
|
||||
range = (selectedPositions[1] + 1) until selectedIndex
|
||||
selectedPositions[1] = selectedIndex
|
||||
} else {
|
||||
// Just select itself
|
||||
range = IntRange.EMPTY
|
||||
}
|
||||
|
||||
range.forEach {
|
||||
val inbetweenItem = get(it)
|
||||
if (!inbetweenItem.selected) {
|
||||
selectedErrorIds.add(inbetweenItem.error.errorId)
|
||||
set(it, inbetweenItem.copy(selected = true))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (userSelected && !fromLongPress) {
|
||||
if (!selected) {
|
||||
if (selectedIndex == selectedPositions[0]) {
|
||||
selectedPositions[0] = indexOfFirst { it.selected }
|
||||
} else if (selectedIndex == selectedPositions[1]) {
|
||||
selectedPositions[1] = indexOfLast { it.selected }
|
||||
}
|
||||
} else {
|
||||
if (selectedIndex < selectedPositions[0]) {
|
||||
selectedPositions[0] = selectedIndex
|
||||
} else if (selectedIndex > selectedPositions[1]) {
|
||||
selectedPositions[1] = selectedIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
state.copy(items = newItems)
|
||||
}
|
||||
}
|
||||
|
||||
fun toggleAllSelection(selected: Boolean) {
|
||||
mutableState.update { state ->
|
||||
val newItems = state.items.map {
|
||||
selectedErrorIds.addOrRemove(it.error.errorId, selected)
|
||||
it.copy(selected = selected)
|
||||
}
|
||||
state.copy(items = newItems)
|
||||
}
|
||||
|
||||
selectedPositions[0] = -1
|
||||
selectedPositions[1] = -1
|
||||
}
|
||||
|
||||
fun invertSelection() {
|
||||
mutableState.update { state ->
|
||||
val newItems = state.items.map {
|
||||
selectedErrorIds.addOrRemove(it.error.errorId, !it.selected)
|
||||
it.copy(selected = !it.selected)
|
||||
}
|
||||
state.copy(items = newItems)
|
||||
}
|
||||
selectedPositions[0] = -1
|
||||
selectedPositions[1] = -1
|
||||
}
|
||||
}
|
||||
|
||||
@Immutable
|
||||
data class LibraryUpdateErrorScreenState(
|
||||
val isLoading: Boolean = true,
|
||||
val items: List<LibraryUpdateErrorItem> = emptyList(),
|
||||
val messages: List<LibraryUpdateErrorMessage> = emptyList(),
|
||||
) {
|
||||
|
||||
val selected = items.filter { it.selected }
|
||||
val selectionMode = selected.isNotEmpty()
|
||||
|
||||
fun getUiModel(): List<LibraryUpdateErrorUiModel> {
|
||||
val uiModels = mutableListOf<LibraryUpdateErrorUiModel>()
|
||||
val errorMap = items.groupBy { it.error.messageId }
|
||||
errorMap.forEach { (messageId, errors) ->
|
||||
val message = messages.find { it.id == messageId }
|
||||
uiModels.add(LibraryUpdateErrorUiModel.Header(message!!.message))
|
||||
uiModels.addAll(errors.map { LibraryUpdateErrorUiModel.Item(it) })
|
||||
}
|
||||
return uiModels
|
||||
}
|
||||
}
|
||||
|
||||
@Immutable
|
||||
data class LibraryUpdateErrorItem(
|
||||
val error: LibraryUpdateErrorWithRelations,
|
||||
val selected: Boolean,
|
||||
)
|
@ -24,6 +24,7 @@ import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||
import eu.kanade.tachiyomi.ui.category.CategoryScreen
|
||||
import eu.kanade.tachiyomi.ui.download.DownloadQueueScreen
|
||||
import eu.kanade.tachiyomi.ui.libraryUpdateError.LibraryUpdateErrorScreen
|
||||
import eu.kanade.tachiyomi.ui.setting.SettingsScreen
|
||||
import eu.kanade.tachiyomi.ui.stats.StatsScreen
|
||||
import eu.kanade.tachiyomi.util.system.isInstalledFromFDroid
|
||||
@ -75,6 +76,7 @@ object MoreTab : Tab {
|
||||
onClickDataAndStorage = { navigator.push(SettingsScreen(SettingsScreen.Destination.DataAndStorage)) },
|
||||
onClickSettings = { navigator.push(SettingsScreen()) },
|
||||
onClickAbout = { navigator.push(SettingsScreen(SettingsScreen.Destination.About)) },
|
||||
onClickLibraryUpdateErrors = { navigator.push(LibraryUpdateErrorScreen()) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,11 @@
|
||||
package tachiyomi.data.libraryUpdateError
|
||||
|
||||
import tachiyomi.domain.libraryUpdateError.model.LibraryUpdateError
|
||||
|
||||
val libraryUpdateErrorMapper: (Long, Long, Long) -> LibraryUpdateError = { id, mangaId, messageId ->
|
||||
LibraryUpdateError(
|
||||
id = id,
|
||||
mangaId = mangaId,
|
||||
messageId = messageId,
|
||||
)
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package tachiyomi.data.libraryUpdateError
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import tachiyomi.data.DatabaseHandler
|
||||
import tachiyomi.domain.libraryUpdateError.model.LibraryUpdateError
|
||||
import tachiyomi.domain.libraryUpdateError.repository.LibraryUpdateErrorRepository
|
||||
|
||||
class LibraryUpdateErrorRepositoryImpl(
|
||||
private val handler: DatabaseHandler,
|
||||
) : LibraryUpdateErrorRepository {
|
||||
|
||||
override suspend fun getAll(): List<LibraryUpdateError> {
|
||||
return handler.awaitList {
|
||||
libraryUpdateErrorQueries.getAllErrors(
|
||||
libraryUpdateErrorMapper,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAllAsFlow(): Flow<List<LibraryUpdateError>> {
|
||||
return handler.subscribeToList {
|
||||
libraryUpdateErrorQueries.getAllErrors(
|
||||
libraryUpdateErrorMapper,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun deleteAll() {
|
||||
return handler.await { libraryUpdateErrorQueries.deleteAllErrors() }
|
||||
}
|
||||
|
||||
override suspend fun delete(errorId: Long) {
|
||||
return handler.await {
|
||||
libraryUpdateErrorQueries.deleteError(
|
||||
_id = errorId,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun insert(libraryUpdateError: LibraryUpdateError) {
|
||||
return handler.await(inTransaction = true) {
|
||||
libraryUpdateErrorQueries.insert(
|
||||
mangaId = libraryUpdateError.mangaId,
|
||||
messageId = libraryUpdateError.messageId,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun insertAll(libraryUpdateErrors: List<LibraryUpdateError>) {
|
||||
return handler.await(inTransaction = true) {
|
||||
libraryUpdateErrors.forEach {
|
||||
libraryUpdateErrorQueries.insert(
|
||||
mangaId = it.mangaId,
|
||||
messageId = it.messageId,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package tachiyomi.data.libraryUpdateError
|
||||
|
||||
import tachiyomi.domain.libraryUpdateError.model.LibraryUpdateErrorWithRelations
|
||||
import tachiyomi.domain.manga.model.MangaCover
|
||||
|
||||
val libraryUpdateErrorWithRelationsMapper:
|
||||
(Long, String, Long, Boolean, String?, Long, Long, Long) -> LibraryUpdateErrorWithRelations =
|
||||
{ mangaId, mangaTitle, mangaSource, favorite, mangaThumbnail, coverLastModified, errorId, messageId ->
|
||||
LibraryUpdateErrorWithRelations(
|
||||
mangaId = mangaId,
|
||||
mangaTitle = mangaTitle,
|
||||
mangaSource = mangaSource,
|
||||
mangaCover = MangaCover(
|
||||
mangaId = mangaId,
|
||||
sourceId = mangaSource,
|
||||
isMangaFavorite = favorite,
|
||||
url = mangaThumbnail,
|
||||
lastModified = coverLastModified,
|
||||
),
|
||||
errorId = errorId,
|
||||
messageId = messageId,
|
||||
)
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package tachiyomi.data.libraryUpdateError
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import tachiyomi.data.DatabaseHandler
|
||||
import tachiyomi.domain.libraryUpdateError.model.LibraryUpdateErrorWithRelations
|
||||
import tachiyomi.domain.libraryUpdateError.repository.LibraryUpdateErrorWithRelationsRepository
|
||||
|
||||
class LibraryUpdateErrorWithRelationsRepositoryImpl(
|
||||
private val handler: DatabaseHandler,
|
||||
) : LibraryUpdateErrorWithRelationsRepository {
|
||||
|
||||
override fun subscribeAll(): Flow<List<LibraryUpdateErrorWithRelations>> {
|
||||
return handler.subscribeToList {
|
||||
libraryUpdateErrorViewQueries.errors(
|
||||
libraryUpdateErrorWithRelationsMapper,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package tachiyomi.data.libraryUpdateErrorMessage
|
||||
|
||||
import tachiyomi.domain.libraryUpdateErrorMessage.model.LibraryUpdateErrorMessage
|
||||
|
||||
val LibraryUpdateErrorMessageMapper: (Long, String) -> LibraryUpdateErrorMessage = { id, message ->
|
||||
LibraryUpdateErrorMessage(
|
||||
id = id,
|
||||
message = message,
|
||||
)
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package tachiyomi.data.libraryUpdateErrorMessage
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import tachiyomi.data.DatabaseHandler
|
||||
import tachiyomi.domain.libraryUpdateErrorMessage.model.LibraryUpdateErrorMessage
|
||||
import tachiyomi.domain.libraryUpdateErrorMessage.repository.LibraryUpdateErrorMessageRepository
|
||||
|
||||
class LibraryUpdateErrorMessageRepositoryImpl(
|
||||
private val handler: DatabaseHandler,
|
||||
) : LibraryUpdateErrorMessageRepository {
|
||||
|
||||
override suspend fun getAll(): List<LibraryUpdateErrorMessage> {
|
||||
return handler.awaitList {
|
||||
libraryUpdateErrorMessageQueries.getAllErrorMessages(
|
||||
LibraryUpdateErrorMessageMapper,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAllAsFlow(): Flow<List<LibraryUpdateErrorMessage>> {
|
||||
return handler.subscribeToList {
|
||||
libraryUpdateErrorMessageQueries.getAllErrorMessages(
|
||||
LibraryUpdateErrorMessageMapper,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun deleteAll() {
|
||||
return handler.await { libraryUpdateErrorMessageQueries.deleteAllErrorMessages() }
|
||||
}
|
||||
|
||||
override suspend fun insert(libraryUpdateErrorMessage: LibraryUpdateErrorMessage): Long? {
|
||||
return handler.awaitOneOrNullExecutable(inTransaction = true) {
|
||||
libraryUpdateErrorMessageQueries.insert(libraryUpdateErrorMessage.message)
|
||||
libraryUpdateErrorMessageQueries.selectLastInsertedRowId()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun insertAll(libraryUpdateErrorMessages: List<LibraryUpdateErrorMessage>): List<Pair<Long, String>> {
|
||||
return handler.await(inTransaction = true) {
|
||||
libraryUpdateErrorMessages.map {
|
||||
libraryUpdateErrorMessageQueries.insert(it.message)
|
||||
libraryUpdateErrorMessageQueries.selectLastInsertedRowId().executeAsOne() to it.message
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
CREATE TABLE libraryUpdateError (
|
||||
_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
manga_id INTEGER NOT NULL,
|
||||
message_id INTEGER NOT NULL
|
||||
);
|
||||
|
||||
getAllErrors:
|
||||
SELECT *
|
||||
FROM libraryUpdateError;
|
||||
|
||||
insert:
|
||||
INSERT INTO libraryUpdateError(manga_id, message_id) VALUES (:mangaId, :messageId);
|
||||
|
||||
deleteAllErrors:
|
||||
DELETE FROM libraryUpdateError;
|
||||
|
||||
deleteError:
|
||||
DELETE FROM libraryUpdateError
|
||||
WHERE _id = :_id;
|
@ -0,0 +1,17 @@
|
||||
CREATE TABLE libraryUpdateErrorMessage (
|
||||
_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
message TEXT NOT NULL UNIQUE
|
||||
);
|
||||
|
||||
getAllErrorMessages:
|
||||
SELECT *
|
||||
FROM libraryUpdateErrorMessage;
|
||||
|
||||
insert:
|
||||
INSERT INTO libraryUpdateErrorMessage(message) VALUES (:message);
|
||||
|
||||
deleteAllErrorMessages:
|
||||
DELETE FROM libraryUpdateErrorMessage;
|
||||
|
||||
selectLastInsertedRowId:
|
||||
SELECT last_insert_rowid();
|
26
data/src/main/sqldelight/tachiyomi/migrations/4.sqm
Normal file
26
data/src/main/sqldelight/tachiyomi/migrations/4.sqm
Normal file
@ -0,0 +1,26 @@
|
||||
DROP VIEW IF EXISTS libraryUpdateErrorView;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS libraryUpdateError (
|
||||
_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
manga_id INTEGER NOT NULL,
|
||||
message_id INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS libraryUpdateErrorMessage (
|
||||
_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
message TEXT NOT NULL UNIQUE
|
||||
);
|
||||
|
||||
CREATE VIEW libraryUpdateErrorView AS
|
||||
SELECT
|
||||
mangas._id AS mangaId,
|
||||
mangas.title AS mangaTitle,
|
||||
mangas.source,
|
||||
mangas.favorite,
|
||||
mangas.thumbnail_url AS thumbnailUrl,
|
||||
mangas.cover_last_modified AS coverLastModified,
|
||||
libraryUpdateError._id AS errorId,
|
||||
libraryUpdateError.message_id AS messageId
|
||||
FROM mangas JOIN libraryUpdateError
|
||||
ON mangas._id = libraryUpdateError.manga_id
|
||||
WHERE favorite = 1;
|
@ -0,0 +1,17 @@
|
||||
CREATE VIEW libraryUpdateErrorView AS
|
||||
SELECT
|
||||
mangas._id AS mangaId,
|
||||
mangas.title AS mangaTitle,
|
||||
mangas.source,
|
||||
mangas.favorite,
|
||||
mangas.thumbnail_url AS thumbnailUrl,
|
||||
mangas.cover_last_modified AS coverLastModified,
|
||||
libraryUpdateError._id AS errorId,
|
||||
libraryUpdateError.message_id AS messageId
|
||||
FROM mangas JOIN libraryUpdateError
|
||||
ON mangas._id = libraryUpdateError.manga_id
|
||||
WHERE favorite = 1;
|
||||
|
||||
errors:
|
||||
SELECT *
|
||||
FROM libraryUpdateErrorView;
|
@ -0,0 +1,36 @@
|
||||
package tachiyomi.domain.libraryUpdateError.interactor
|
||||
|
||||
import logcat.LogPriority
|
||||
import tachiyomi.core.common.util.lang.withNonCancellableContext
|
||||
import tachiyomi.core.common.util.system.logcat
|
||||
import tachiyomi.domain.libraryUpdateError.repository.LibraryUpdateErrorRepository
|
||||
|
||||
class DeleteLibraryUpdateErrors(
|
||||
private val libraryUpdateErrorRepository: LibraryUpdateErrorRepository,
|
||||
) {
|
||||
|
||||
suspend fun await() = withNonCancellableContext {
|
||||
try {
|
||||
libraryUpdateErrorRepository.deleteAll()
|
||||
Result.Success
|
||||
} catch (e: Exception) {
|
||||
logcat(LogPriority.ERROR, e)
|
||||
return@withNonCancellableContext Result.InternalError(e)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun await(errorId: Long) = withNonCancellableContext {
|
||||
try {
|
||||
libraryUpdateErrorRepository.delete(errorId)
|
||||
Result.Success
|
||||
} catch (e: Exception) {
|
||||
logcat(LogPriority.ERROR, e)
|
||||
return@withNonCancellableContext Result.InternalError(e)
|
||||
}
|
||||
}
|
||||
|
||||
sealed class Result {
|
||||
object Success : Result()
|
||||
data class InternalError(val error: Throwable) : Result()
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package tachiyomi.domain.libraryUpdateError.interactor
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import tachiyomi.domain.libraryUpdateError.model.LibraryUpdateErrorWithRelations
|
||||
import tachiyomi.domain.libraryUpdateError.repository.LibraryUpdateErrorWithRelationsRepository
|
||||
|
||||
class GetLibraryUpdateErrorWithRelations(
|
||||
private val libraryUpdateErrorWithRelationsRepository: LibraryUpdateErrorWithRelationsRepository,
|
||||
) {
|
||||
|
||||
fun subscribeAll(): Flow<List<LibraryUpdateErrorWithRelations>> {
|
||||
return libraryUpdateErrorWithRelationsRepository.subscribeAll()
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package tachiyomi.domain.libraryUpdateError.interactor
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import tachiyomi.domain.libraryUpdateError.model.LibraryUpdateError
|
||||
import tachiyomi.domain.libraryUpdateError.repository.LibraryUpdateErrorRepository
|
||||
|
||||
class GetLibraryUpdateErrors(
|
||||
private val libraryUpdateErrorRepository: LibraryUpdateErrorRepository,
|
||||
) {
|
||||
|
||||
fun subscribe(): Flow<List<LibraryUpdateError>> {
|
||||
return libraryUpdateErrorRepository.getAllAsFlow()
|
||||
}
|
||||
|
||||
suspend fun await(): List<LibraryUpdateError> {
|
||||
return libraryUpdateErrorRepository.getAll()
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package tachiyomi.domain.libraryUpdateError.interactor
|
||||
|
||||
import tachiyomi.domain.libraryUpdateError.model.LibraryUpdateError
|
||||
import tachiyomi.domain.libraryUpdateError.repository.LibraryUpdateErrorRepository
|
||||
|
||||
class InsertLibraryUpdateErrors(
|
||||
private val libraryUpdateErrorRepository: LibraryUpdateErrorRepository,
|
||||
) {
|
||||
suspend fun insert(libraryUpdateError: LibraryUpdateError) {
|
||||
return libraryUpdateErrorRepository.insert(libraryUpdateError)
|
||||
}
|
||||
|
||||
suspend fun insertAll(libraryUpdateErrors: List<LibraryUpdateError>) {
|
||||
return libraryUpdateErrorRepository.insertAll(libraryUpdateErrors)
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package tachiyomi.domain.libraryUpdateError.model
|
||||
|
||||
import java.io.Serializable
|
||||
|
||||
data class LibraryUpdateError(
|
||||
val id: Long,
|
||||
val mangaId: Long,
|
||||
val messageId: Long,
|
||||
) : Serializable
|
@ -0,0 +1,12 @@
|
||||
package tachiyomi.domain.libraryUpdateError.model
|
||||
|
||||
import tachiyomi.domain.manga.model.MangaCover
|
||||
|
||||
data class LibraryUpdateErrorWithRelations(
|
||||
val mangaId: Long,
|
||||
val mangaTitle: String,
|
||||
val mangaSource: Long,
|
||||
val mangaCover: MangaCover,
|
||||
val errorId: Long,
|
||||
val messageId: Long,
|
||||
)
|
@ -0,0 +1,19 @@
|
||||
package tachiyomi.domain.libraryUpdateError.repository
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import tachiyomi.domain.libraryUpdateError.model.LibraryUpdateError
|
||||
|
||||
interface LibraryUpdateErrorRepository {
|
||||
|
||||
suspend fun getAll(): List<LibraryUpdateError>
|
||||
|
||||
fun getAllAsFlow(): Flow<List<LibraryUpdateError>>
|
||||
|
||||
suspend fun deleteAll()
|
||||
|
||||
suspend fun delete(errorId: Long)
|
||||
|
||||
suspend fun insert(libraryUpdateError: LibraryUpdateError)
|
||||
|
||||
suspend fun insertAll(libraryUpdateErrors: List<LibraryUpdateError>)
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package tachiyomi.domain.libraryUpdateError.repository
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import tachiyomi.domain.libraryUpdateError.model.LibraryUpdateErrorWithRelations
|
||||
|
||||
interface LibraryUpdateErrorWithRelationsRepository {
|
||||
|
||||
fun subscribeAll(): Flow<List<LibraryUpdateErrorWithRelations>>
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package tachiyomi.domain.libraryUpdateErrorMessage.interactor
|
||||
|
||||
import logcat.LogPriority
|
||||
import tachiyomi.core.common.util.lang.withNonCancellableContext
|
||||
import tachiyomi.core.common.util.system.logcat
|
||||
import tachiyomi.domain.libraryUpdateErrorMessage.repository.LibraryUpdateErrorMessageRepository
|
||||
import kotlin.Exception
|
||||
|
||||
class DeleteLibraryUpdateErrorMessages(
|
||||
private val libraryUpdateErrorMessageRepository: LibraryUpdateErrorMessageRepository,
|
||||
) {
|
||||
|
||||
suspend fun await() = withNonCancellableContext {
|
||||
try {
|
||||
libraryUpdateErrorMessageRepository.deleteAll()
|
||||
Result.Success
|
||||
} catch (e: Exception) {
|
||||
logcat(LogPriority.ERROR, e)
|
||||
return@withNonCancellableContext Result.InternalError(e)
|
||||
}
|
||||
}
|
||||
|
||||
sealed class Result {
|
||||
object Success : Result()
|
||||
data class InternalError(val error: Throwable) : Result()
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package tachiyomi.domain.libraryUpdateErrorMessage.interactor
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import tachiyomi.domain.libraryUpdateErrorMessage.model.LibraryUpdateErrorMessage
|
||||
import tachiyomi.domain.libraryUpdateErrorMessage.repository.LibraryUpdateErrorMessageRepository
|
||||
|
||||
class GetLibraryUpdateErrorMessages(
|
||||
private val libraryUpdateErrorMessageRepository: LibraryUpdateErrorMessageRepository,
|
||||
) {
|
||||
|
||||
fun subscribe(): Flow<List<LibraryUpdateErrorMessage>> {
|
||||
return libraryUpdateErrorMessageRepository.getAllAsFlow()
|
||||
}
|
||||
|
||||
suspend fun await(): List<LibraryUpdateErrorMessage> {
|
||||
return libraryUpdateErrorMessageRepository.getAll()
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package tachiyomi.domain.libraryUpdateErrorMessage.interactor
|
||||
|
||||
import tachiyomi.domain.libraryUpdateErrorMessage.model.LibraryUpdateErrorMessage
|
||||
import tachiyomi.domain.libraryUpdateErrorMessage.repository.LibraryUpdateErrorMessageRepository
|
||||
|
||||
class InsertLibraryUpdateErrorMessages(
|
||||
private val libraryUpdateErrorMessageRepository: LibraryUpdateErrorMessageRepository,
|
||||
) {
|
||||
|
||||
suspend fun insert(libraryUpdateErrorMessage: LibraryUpdateErrorMessage): Long? {
|
||||
return libraryUpdateErrorMessageRepository.insert(libraryUpdateErrorMessage)
|
||||
}
|
||||
|
||||
suspend fun insertAll(libraryUpdateErrorMessages: List<LibraryUpdateErrorMessage>): List<Pair<Long, String>> {
|
||||
return libraryUpdateErrorMessageRepository.insertAll(libraryUpdateErrorMessages)
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package tachiyomi.domain.libraryUpdateErrorMessage.model
|
||||
|
||||
import java.io.Serializable
|
||||
|
||||
data class LibraryUpdateErrorMessage(
|
||||
val id: Long,
|
||||
val message: String,
|
||||
) : Serializable
|
@ -0,0 +1,17 @@
|
||||
package tachiyomi.domain.libraryUpdateErrorMessage.repository
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import tachiyomi.domain.libraryUpdateErrorMessage.model.LibraryUpdateErrorMessage
|
||||
|
||||
interface LibraryUpdateErrorMessageRepository {
|
||||
|
||||
suspend fun getAll(): List<LibraryUpdateErrorMessage>
|
||||
|
||||
fun getAllAsFlow(): Flow<List<LibraryUpdateErrorMessage>>
|
||||
|
||||
suspend fun deleteAll()
|
||||
|
||||
suspend fun insert(libraryUpdateErrorMessage: LibraryUpdateErrorMessage): Long?
|
||||
|
||||
suspend fun insertAll(libraryUpdateErrorMessages: List<LibraryUpdateErrorMessage>): List<Pair<Long, String>>
|
||||
}
|
@ -573,6 +573,11 @@
|
||||
<string name="syncing_library">Syncing library</string>
|
||||
<string name="library_sync_complete">Library sync complete</string>
|
||||
|
||||
<!-- Error section -->
|
||||
<string name="option_label_library_update_errors">Library update errors</string>
|
||||
<string name="label_library_update_errors">Library update errors (%d)</string>
|
||||
<string name="info_empty_library_update_errors">You have no library update errors.</string>
|
||||
|
||||
<!-- Advanced section -->
|
||||
<string name="label_network">Networking</string>
|
||||
<string name="pref_clear_cookies">Clear cookies</string>
|
||||
|
Loading…
x
Reference in New Issue
Block a user