Make migration changes

This commit is contained in:
Roshan Varughese 2024-10-26 22:19:24 +13:00
parent 298a1134e6
commit 17d9e47b11
9 changed files with 154 additions and 87 deletions

View file

@ -358,14 +358,14 @@ fun FailedUpdatesBottomActionMenu(
.padding(horizontal = 8.dp, vertical = 12.dp),
) {
Button(
title = stringResource(R.string.action_delete),
title = stringResource(MR.strings.action_delete),
icon = Icons.Outlined.Delete,
toConfirm = confirm[0],
onLongClick = { onLongClickItem(0) },
onClick = onDeleteClicked,
)
Button(
title = stringResource(R.string.action_dismiss),
title = stringResource(MR.strings.action_dismiss),
icon = Icons.Outlined.VisibilityOff,
toConfirm = confirm[1],
onLongClick = { onLongClickItem(1) },
@ -373,7 +373,7 @@ fun FailedUpdatesBottomActionMenu(
)
if (groupingMode == GroupByMode.NONE && selected.size <= 1) {
Button(
title = stringResource(R.string.action_info),
title = stringResource(MR.strings.action_info),
icon = Icons.Outlined.Info,
toConfirm = confirm[2],
onLongClick = { onLongClickItem(2) },

View file

@ -30,6 +30,7 @@ import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.ui.updates.UpdatesItem
import eu.kanade.tachiyomi.ui.updates.UpdatesScreenModel
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import tachiyomi.i18n.MR
@ -152,26 +153,33 @@ private fun UpdatesAppBar(
modifier = modifier,
title = stringResource(MR.strings.label_recent_updates),
actions = {
val actions = mutableListOf<AppBar.Action>()
if (hasFailedUpdates) { // only add the warning icon if it is enabled
actions += AppBar.Action(
title = stringResource(R.string.action_update_warning),
icon = Icons.Rounded.Warning,
onClick = onUpdateWarning,
iconTint = warningIconTint,
val actions = mutableListOf<AppBar.Action>().apply {
if (hasFailedUpdates) {
add(
AppBar.Action(
title = stringResource(MR.strings.action_update_warning),
icon = Icons.Rounded.Warning,
onClick = onUpdateWarning,
iconTint = warningIconTint,
),
)
}
add(
AppBar.Action(
title = stringResource(MR.strings.action_view_upcoming),
icon = Icons.Outlined.CalendarMonth,
onClick = onCalendarClicked,
),
)
add(
AppBar.Action(
title = stringResource(MR.strings.action_update_library),
icon = Icons.Outlined.Refresh,
onClick = onUpdateLibrary,
),
)
}
actions += AppBar.Action(
title = stringResource(MR.strings.action_view_upcoming),
icon = Icons.Outlined.CalendarMonth,
onClick = onCalendarClicked,
)
actions += AppBar.Action(
title = stringResource(R.string.action_update_library),
icon = Icons.Outlined.Refresh,
onClick = onUpdateLibrary,
)
AppBarActions(actions)
AppBarActions(actions.toImmutableList())
},
actionModeCounter = actionModeCounter,
onCancelActionMode = onCancelActionMode,

View file

@ -27,9 +27,9 @@ import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Dangerous
import androidx.compose.material.icons.filled.KeyboardArrowUp
import androidx.compose.material.icons.rounded.Warning
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.HorizontalDivider
@ -38,6 +38,7 @@ import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.minimumInteractiveComponentSize
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
@ -49,6 +50,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
@ -244,6 +246,15 @@ fun LazyListScope.failedUpdatesGroupUiItem(
.height(50.dp)
.aspectRatio(1f),
)
} else {
Image(
imageVector = Icons.Filled.Dangerous,
contentDescription = null,
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.error),
modifier = Modifier
.height(50.dp)
.aspectRatio(1f),
)
}
Column(
modifier = Modifier
@ -313,11 +324,13 @@ fun LazyListScope.failedUpdatesGroupUiItem(
if (expanded[errorMessageHeaderId] == null) {
onExpandedMapChange(errorMessageHeaderId, true)
} else {
onExpandedMapChange(errorMessageHeaderId, !expanded[errorMessageHeaderId]!!)
onExpandedMapChange(
errorMessageHeaderId,
!expanded[errorMessageHeaderId]!!,
)
}
},
onLongClick =
{ onGroupSelected(items) },
onLongClick = { onGroupSelected(items) },
)
.padding(
horizontal = 12.dp,
@ -390,7 +403,9 @@ fun LazyListScope.failedUpdatesGroupUiItem(
val isLastItem = index == items.lastIndex
AnimatedVisibility(
modifier = Modifier,
visible = expanded[errorMessageHeaderId] == true && expanded[GroupKey(id, Pair("", ""))] == true,
visible =
expanded[errorMessageHeaderId] == true &&
expanded[GroupKey(id, Pair("", ""))] == true,
) {
FailedUpdatesUiItem(
modifier = Modifier
@ -443,10 +458,12 @@ fun CustomIconButton(
enabled = enabled,
role = Role.Button,
interactionSource = interactionSource,
indication = rememberRipple(
bounded = false,
radius = 20.dp,
),
indication = remember {
ripple(
bounded = false,
radius = 20.dp,
)
},
),
contentAlignment = Alignment.Center,
) {

View file

@ -32,7 +32,7 @@ fun ErrorMessageDialog(
TextButton(onClick = {
onCopyClick()
onDismissRequest()
},) {
}) {
Text(text = stringResource(R.string.copy))
}
},

View file

@ -51,7 +51,9 @@ import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.manga.MangaScreen
import eu.kanade.tachiyomi.util.system.copyToClipboard
import kotlinx.collections.immutable.toImmutableList
import tachiyomi.domain.manga.model.Manga
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.FastScrollLazyColumn
import tachiyomi.presentation.core.components.Pill
import tachiyomi.presentation.core.components.SortItem
@ -59,8 +61,6 @@ import tachiyomi.presentation.core.components.material.ExtendedFloatingActionBut
import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.screens.EmptyScreen
import tachiyomi.presentation.core.screens.LoadingScreen
import tachiyomi.presentation.core.util.isScrolledToEnd
import tachiyomi.presentation.core.util.isScrollingUp
import tachiyomi.source.local.isLocal
class FailedUpdatesScreen : Screen() {
@ -120,7 +120,8 @@ class FailedUpdatesScreen : Screen() {
text = { Text(text = stringResource(R.string.label_help)) },
icon = { Icon(imageVector = Icons.Outlined.HelpOutline, contentDescription = null) },
onClick = { uriHandler.openUri("https://tachiyomi.org/help/guides/troubleshooting") },
expanded = failedUpdatesListState.isScrollingUp() || failedUpdatesListState.isScrolledToEnd(),
// Revisit
// expanded = failedUpdatesListState.isScrollingUp() || failedUpdatesListState.isScrolledToEnd(),
)
}
},
@ -129,7 +130,7 @@ class FailedUpdatesScreen : Screen() {
state.isLoading -> LoadingScreen(modifier = Modifier.padding(contentPadding))
state.items.isEmpty() -> EmptyScreen(
textResource = R.string.information_no_update_errors,
stringRes = MR.strings.information_no_update_errors,
modifier = Modifier.padding(contentPadding),
happyFace = true,
)
@ -363,7 +364,7 @@ private fun FailedUpdatesAppBar(
onClick = { onClickGroup(GroupByMode.NONE) },
)
}
AppBarActions(actions)
AppBarActions(actions.toImmutableList())
}
},
scrollBehavior = scrollBehavior,
@ -399,7 +400,7 @@ private fun FailedUpdatesActionAppBar(
icon = Icons.Outlined.FlipToBack,
onClick = onInvertSelection,
),
),
).toImmutableList(),
)
},
scrollBehavior = scrollBehavior,

View file

@ -4,11 +4,10 @@ import android.content.Context
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.coroutineScope
import cafe.adriel.voyager.core.model.screenModelScope
import eu.kanade.core.util.addOrRemove
import eu.kanade.domain.manga.interactor.UpdateManga
import eu.kanade.domain.source.interactor.GetSourcesWithFavoriteCount
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.source.online.HttpSource
@ -20,11 +19,11 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.update
import logcat.LogPriority
import tachiyomi.core.preference.PreferenceStore
import tachiyomi.core.preference.getEnum
import tachiyomi.core.util.lang.launchIO
import tachiyomi.core.util.lang.launchNonCancellable
import tachiyomi.core.util.system.logcat
import tachiyomi.core.common.preference.PreferenceStore
import tachiyomi.core.common.preference.getEnum
import tachiyomi.core.common.util.lang.launchIO
import tachiyomi.core.common.util.lang.launchNonCancellable
import tachiyomi.core.common.util.system.logcat
import tachiyomi.domain.category.interactor.GetCategories
import tachiyomi.domain.category.model.Category
import tachiyomi.domain.failed.repository.FailedUpdatesRepository
@ -56,7 +55,7 @@ class FailedUpdatesScreenModel(
val channel = _channel.receiveAsFlow()
init {
coroutineScope.launchIO {
screenModelScope.launchIO {
val sortMode = preferenceStore.getEnum("sort_mode", SortingMode.BY_ALPHABET).get()
combine(
getSourcesWithFavoriteCount.subscribe(),
@ -79,10 +78,12 @@ class FailedUpdatesScreenModel(
items = libraryManga.filter { libraryManga ->
failedUpdates.any { it.mangaId == libraryManga.manga.id }
}.map { libraryManga ->
val source = sourceManager.get(libraryManga.manga.source)!!
// Untrusted Extensions cause null crash
val source = sourceManager.getOrStub(libraryManga.manga.source)
val failedUpdate = failedUpdates.find { it.mangaId == libraryManga.manga.id }!!
val errorMessage = failedUpdate.errorMessage
val simplifiedErrorMessage = simplifyErrorMessage(errorMessage.substringBefore(":"), failedUpdate.isOnline)
val simplifiedErrorMessage =
simplifyErrorMessage(errorMessage.substringBefore(":"), failedUpdate.isOnline)
FailedUpdatesManga(
libraryManga = libraryManga,
errorMessage = errorMessage,
@ -106,42 +107,56 @@ class FailedUpdatesScreenModel(
private fun simplifyErrorMessage(exception: String, isOnline: Long): String {
return when (exception) {
// General networking exceptions
"SocketException" -> context.getString(R.string.exception_socket_error)
"BindException" -> context.getString(R.string.exception_bind_port)
"InterruptedIOException" -> context.getString(R.string.exception_io_interrupted)
"HttpRetryException" -> context.getString(R.string.exception_http_retry)
"PortUnreachableException" -> context.getString(R.string.exception_port_unreachable)
// Hold your arses this is temporary and for testing purposes only
"SocketException" -> "Socket Exception"
"BindException" -> "Bind Exception"
"InterruptedIOException" -> "Interrupted IO Exception"
"HttpRetryException" -> "HTTP Retry Exception"
"PortUnreachableException" -> "Port Unreachable Exception"
// General IO-related exceptions
"IOException" -> if (isOnline == 1L) context.getString(R.string.exception_io_error) else context.getString(R.string.exception_internet_connection)
"TimeoutException" -> context.getString(R.string.exception_timed_out)
"IOException" -> if (isOnline ==
1L
) {
"IO Exception"
} else {
"IOException: No Internet"
}
"TimeoutException" -> "Timeout Exception"
// SSL & Security-related
"SSLException" -> context.getString(R.string.exception_ssl_connection)
"CertificateExpiredException" -> context.getString(R.string.exception_ssl_certificate)
"CertificateNotYetValidException" -> context.getString(R.string.exception_ssl_not_valid)
"CertificateParsingException" -> context.getString(R.string.exception_ssl_parsing)
"CertificateEncodingException" -> context.getString(R.string.exception_ssl_encoding)
"UnrecoverableKeyException" -> context.getString(R.string.exception_unrecoverable_key)
"KeyManagementException" -> context.getString(R.string.exception_key_management)
"NoSuchAlgorithmException" -> context.getString(R.string.exception_algorithm)
"KeyStoreException" -> context.getString(R.string.exception_keystore)
"NoSuchProviderException" -> context.getString(R.string.exception_security_provider)
"SignatureException" -> context.getString(R.string.exception_signature_validation)
"InvalidKeySpecException" -> context.getString(R.string.exception_key_specification)
"SSLException" -> "SSL Exception"
"CertificateExpiredException" -> "Certificate Expired Exception"
"CertificateNotYetValidException" -> "Certificate Not Yet Valid Exception"
"CertificateParsingException" -> "Certificate Parsing Exception"
"CertificateEncodingException" -> "Certificate Encoding Exception"
"UnrecoverableKeyException" -> "Unrecoverable Key Exception"
"KeyManagementException" -> "Key Management Exception"
"NoSuchAlgorithmException" -> "No Such Algorithm Exception"
"KeyStoreException" -> "Key Store Exception"
"NoSuchProviderException" -> "No Such Provider Exception"
"SignatureException" -> "Signature Exception"
"InvalidKeySpecException" -> "Invalid Key Spec Exception"
// Host & DNS-related
"UnknownHostException" -> if (isOnline == 1L) context.getString(R.string.exception_domain) else context.getString(R.string.exception_internet_connection)
"NoRouteToHostException" -> context.getString(R.string.exception_route_to_host)
"UnknownHostException" -> if (isOnline ==
1L
) {
"Unknown Host Exception"
} else {
"Unknown Host Exception: No Internet"
}
"ConnectException" -> "Connect Exception"
"NoRouteToHostException" -> "No Route To Host Exception"
// URL & URI related
"URISyntaxException" -> context.getString(R.string.exception_uri_syntax)
"MalformedURLException" -> context.getString(R.string.exception_malformed_url)
"URISyntaxException" -> "URI Syntax Exception"
"MalformedURLException" -> "Malformed URL Exception"
// Authentication & Proxy
"ProtocolException" -> context.getString(R.string.exception_protocol_proxy_type)
"ProtocolException" -> "Protocol Exception"
// Concurrency & Operation-related
"CancellationException" -> context.getString(R.string.exception_cancelled)
"InterruptedException" -> context.getString(R.string.exception_interrupted)
"IllegalStateException" -> context.getString(R.string.exception_unexpected_state)
"UnsupportedOperationException" -> context.getString(R.string.exception_not_supported)
"IllegalArgumentException" -> context.getString(R.string.exception_invalid_argument)
else -> ""
"CancellationException" -> "Cancellation Exception"
"InterruptedException" -> "Interrupted Exception"
"IllegalStateException" -> "Illegal State Exception"
"UnsupportedOperationException" -> "Unsupported Operation Exception"
"IllegalArgumentException" -> "Illegal Argument Exception"
else -> "Unknown: $exception"
}
}
@ -163,7 +178,13 @@ class FailedUpdatesScreenModel(
val descendingOrder = if (state.sortMode == SortingMode.BY_ALPHABET) !state.descendingOrder else false
preferenceStore.getBoolean("descending_order", false).set(descendingOrder)
state.copy(
items = if (descendingOrder) state.items.sortedByDescending { it.libraryManga.manga.title } else state.items.sortedBy { it.libraryManga.manga.title },
items = if (descendingOrder) {
state.items.sortedByDescending {
it.libraryManga.manga.title
}
} else {
state.items.sortedBy { it.libraryManga.manga.title }
},
descendingOrder = descendingOrder,
sortMode = SortingMode.BY_ALPHABET,
)
@ -172,7 +193,12 @@ class FailedUpdatesScreenModel(
}
@Composable
fun categoryMap(items: List<FailedUpdatesManga>, groupMode: GroupByMode, sortMode: SortingMode, descendingOrder: Boolean): Map<String, Map<Pair<String, String>, List<FailedUpdatesManga>>> {
fun categoryMap(
items: List<FailedUpdatesManga>,
groupMode: GroupByMode,
sortMode: SortingMode,
descendingOrder: Boolean,
): Map<String, Map<Pair<String, String>, List<FailedUpdatesManga>>> {
val unsortedMap = when (groupMode) {
GroupByMode.BY_SOURCE -> items.groupBy { it.source.name }
.mapValues { entry -> entry.value.groupBy { Pair(it.errorMessage, it.simplifiedErrorMessage) } }
@ -180,7 +206,14 @@ class FailedUpdatesScreenModel(
}
return when (sortMode) {
SortingMode.BY_ALPHABET -> {
val sortedMap = TreeMap<String, Map<Pair<String, String>, List<FailedUpdatesManga>>>(if (descendingOrder) { compareByDescending { it } } else { compareBy { it } })
val sortedMap =
TreeMap<String, Map<Pair<String, String>, List<FailedUpdatesManga>>>(
if (descendingOrder) {
compareByDescending { it }
} else {
compareBy { it }
},
)
sortedMap.putAll(unsortedMap)
sortedMap
}
@ -276,7 +309,11 @@ class FailedUpdatesScreenModel(
range.forEach {
val inBetweenItem = getOrNull(it)
if (inBetweenItem != null && !inBetweenItem.selected && inBetweenItem.errorMessage == firstErrorMessage && inBetweenItem.errorMessage == lastErrorMessage) {
if (inBetweenItem != null &&
!inBetweenItem.selected &&
inBetweenItem.errorMessage == firstErrorMessage &&
inBetweenItem.errorMessage == lastErrorMessage
) {
selectedMangaIds.add(inBetweenItem.libraryManga.manga.id)
set(it, inBetweenItem.copy(selected = true))
}
@ -325,7 +362,7 @@ class FailedUpdatesScreenModel(
}
fun removeMangas(mangaList: List<Manga>, deleteFromLibrary: Boolean, deleteChapters: Boolean) {
coroutineScope.launchNonCancellable {
screenModelScope.launchNonCancellable {
val mangaToDelete = mangaList.distinctBy { it.id }
if (deleteFromLibrary) {
@ -368,7 +405,7 @@ class FailedUpdatesScreenModel(
)
}
coroutineScope.launchNonCancellable { failedUpdatesManager.removeFailedUpdatesByMangaIds(listOfMangaIds) }
screenModelScope.launchNonCancellable { failedUpdatesManager.removeFailedUpdatesByMangaIds(listOfMangaIds) }
}
fun openDeleteMangaDialog(selected: List<FailedUpdatesManga>) {

View file

@ -289,7 +289,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
is SourceNotInstalledException -> context.stringResource(
MR.strings.loader_not_implemented_error,
)
else -> e.message ?: context.getString(MR.strings.exception_unknown)
else -> e.message ?: "context.getString(MR.strings.exception_unknown)"
}
try {
failedUpdatesCount.getAndIncrement()
@ -322,7 +322,6 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
failedUpdatesCount.get(),
)
}
}
private fun downloadChapters(manga: Manga, chapters: List<Chapter>) {

View file

@ -41,7 +41,12 @@ object NotificationHandler {
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
action = Constants.SHORTCUT_FAILED
}
return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
return PendingIntent.getActivity(
context,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
)
}
/**

View file

@ -4,7 +4,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import logcat.LogPriority
import tachiyomi.core.util.system.logcat
import tachiyomi.core.common.util.system.logcat
import tachiyomi.data.DatabaseHandler
import tachiyomi.domain.failed.model.FailedUpdate
import tachiyomi.domain.failed.repository.FailedUpdatesRepository