2022-05-08 05:34:55 +02:00
|
|
|
package eu.kanade.presentation.browse
|
2022-04-27 14:36:16 +02:00
|
|
|
|
2024-08-05 22:38:09 +02:00
|
|
|
import androidx.activity.compose.BackHandler
|
2022-09-18 22:08:50 +02:00
|
|
|
import androidx.compose.foundation.background
|
2022-06-14 15:10:40 +02:00
|
|
|
import androidx.compose.foundation.layout.Arrangement
|
2025-11-28 08:21:41 +01:00
|
|
|
import androidx.compose.foundation.layout.Box
|
2022-06-14 15:10:40 +02:00
|
|
|
import androidx.compose.foundation.layout.Column
|
2022-10-09 21:52:56 +02:00
|
|
|
import androidx.compose.foundation.layout.PaddingValues
|
2022-06-14 15:10:40 +02:00
|
|
|
import androidx.compose.foundation.layout.Row
|
2022-04-27 14:36:16 +02:00
|
|
|
import androidx.compose.foundation.layout.padding
|
|
|
|
|
import androidx.compose.foundation.lazy.items
|
2024-08-05 22:38:09 +02:00
|
|
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
2022-08-30 22:31:28 +02:00
|
|
|
import androidx.compose.material.icons.Icons
|
|
|
|
|
import androidx.compose.material.icons.outlined.ArrowDownward
|
|
|
|
|
import androidx.compose.material.icons.outlined.ArrowUpward
|
2024-11-10 07:42:57 +01:00
|
|
|
import androidx.compose.material.icons.outlined.NewReleases
|
2022-08-30 22:31:28 +02:00
|
|
|
import androidx.compose.material.icons.outlined.Numbers
|
|
|
|
|
import androidx.compose.material.icons.outlined.SortByAlpha
|
|
|
|
|
import androidx.compose.material3.Icon
|
|
|
|
|
import androidx.compose.material3.IconButton
|
2024-11-10 07:42:57 +01:00
|
|
|
import androidx.compose.material3.LocalContentColor
|
2022-04-27 14:36:16 +02:00
|
|
|
import androidx.compose.material3.MaterialTheme
|
|
|
|
|
import androidx.compose.material3.Text
|
|
|
|
|
import androidx.compose.runtime.Composable
|
2024-11-10 07:42:57 +01:00
|
|
|
import androidx.compose.runtime.getValue
|
|
|
|
|
import androidx.compose.runtime.mutableStateOf
|
2025-02-22 17:45:04 +01:00
|
|
|
import androidx.compose.runtime.remember
|
2024-11-24 03:23:00 +01:00
|
|
|
import androidx.compose.runtime.saveable.rememberSaveable
|
2024-11-10 07:42:57 +01:00
|
|
|
import androidx.compose.runtime.setValue
|
2022-06-14 15:10:40 +02:00
|
|
|
import androidx.compose.ui.Alignment
|
2022-04-27 14:36:16 +02:00
|
|
|
import androidx.compose.ui.Modifier
|
2022-07-16 20:44:37 +02:00
|
|
|
import androidx.compose.ui.platform.LocalContext
|
2025-12-09 07:19:54 +01:00
|
|
|
import androidx.compose.ui.platform.LocalDensity
|
2022-06-14 15:10:40 +02:00
|
|
|
import androidx.compose.ui.text.style.TextOverflow
|
2022-08-29 23:18:06 +02:00
|
|
|
import eu.kanade.domain.source.interactor.SetMigrateSorting
|
2024-10-28 16:20:52 +01:00
|
|
|
import eu.kanade.domain.source.model.installedExtension
|
2022-05-08 05:34:55 +02:00
|
|
|
import eu.kanade.presentation.browse.components.BaseSourceItem
|
2022-06-14 15:10:40 +02:00
|
|
|
import eu.kanade.presentation.browse.components.SourceIcon
|
2024-10-18 18:16:32 +02:00
|
|
|
import eu.kanade.presentation.components.AnimatedFloatingSearchBox
|
2025-12-09 07:19:54 +01:00
|
|
|
import eu.kanade.presentation.components.SOURCE_SEARCH_BOX_HEIGHT
|
2024-08-05 22:38:09 +02:00
|
|
|
import eu.kanade.presentation.util.animateItemFastScroll
|
2023-07-24 02:03:37 +02:00
|
|
|
import eu.kanade.tachiyomi.ui.browse.migration.sources.MigrateSourceScreenModel
|
2022-07-16 20:44:37 +02:00
|
|
|
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
2025-05-15 19:38:03 +02:00
|
|
|
import exh.source.ExhPreferences
|
2025-12-09 09:21:33 +01:00
|
|
|
import exh.source.hentaiSourceIds
|
2024-01-08 04:22:32 +01:00
|
|
|
import kotlinx.collections.immutable.ImmutableList
|
2023-01-22 17:04:50 +01:00
|
|
|
import tachiyomi.domain.source.model.Source
|
2023-11-18 19:54:56 +01:00
|
|
|
import tachiyomi.i18n.MR
|
2024-08-05 22:38:09 +02:00
|
|
|
import tachiyomi.i18n.kmk.KMR
|
2023-02-18 21:52:52 +01:00
|
|
|
import tachiyomi.presentation.core.components.Badge
|
|
|
|
|
import tachiyomi.presentation.core.components.BadgeGroup
|
2024-08-05 22:38:09 +02:00
|
|
|
import tachiyomi.presentation.core.components.FastScrollLazyColumn
|
2023-02-18 22:03:01 +01:00
|
|
|
import tachiyomi.presentation.core.components.material.padding
|
2023-11-19 01:41:33 +01:00
|
|
|
import tachiyomi.presentation.core.i18n.stringResource
|
2024-08-29 11:21:08 +02:00
|
|
|
import tachiyomi.presentation.core.icons.FlagEmoji
|
2023-02-20 16:12:41 +01:00
|
|
|
import tachiyomi.presentation.core.screens.EmptyScreen
|
|
|
|
|
import tachiyomi.presentation.core.screens.LoadingScreen
|
2023-02-18 23:04:32 +01:00
|
|
|
import tachiyomi.presentation.core.theme.header
|
2023-02-18 22:33:03 +01:00
|
|
|
import tachiyomi.presentation.core.util.secondaryItemAlpha
|
2025-02-22 17:45:04 +01:00
|
|
|
import uy.kohesive.injekt.Injekt
|
|
|
|
|
import uy.kohesive.injekt.api.get
|
2022-04-27 14:36:16 +02:00
|
|
|
|
|
|
|
|
@Composable
|
|
|
|
|
fun MigrateSourceScreen(
|
2023-07-24 02:03:37 +02:00
|
|
|
state: MigrateSourceScreenModel.State,
|
2022-10-09 21:52:56 +02:00
|
|
|
contentPadding: PaddingValues,
|
2022-04-27 14:36:16 +02:00
|
|
|
onClickItem: (Source) -> Unit,
|
2022-11-24 04:28:25 +01:00
|
|
|
onToggleSortingDirection: () -> Unit,
|
|
|
|
|
onToggleSortingMode: () -> Unit,
|
2024-08-05 22:38:09 +02:00
|
|
|
// KMK -->
|
|
|
|
|
onChangeSearchQuery: (String?) -> Unit,
|
|
|
|
|
// KMK <--
|
2022-04-27 14:36:16 +02:00
|
|
|
) {
|
2022-07-16 20:44:37 +02:00
|
|
|
val context = LocalContext.current
|
|
|
|
|
when {
|
2023-08-26 04:25:00 +02:00
|
|
|
state.isLoading -> LoadingScreen(Modifier.padding(contentPadding))
|
2024-08-05 22:38:09 +02:00
|
|
|
// KMK -->
|
2024-11-08 09:43:38 +01:00
|
|
|
state.searchQuery == null &&
|
2024-08-05 22:38:09 +02:00
|
|
|
// KMK <--
|
|
|
|
|
state.isEmpty -> EmptyScreen(
|
2023-11-18 19:54:56 +01:00
|
|
|
stringRes = MR.strings.information_empty_library,
|
2022-10-09 21:52:56 +02:00
|
|
|
modifier = Modifier.padding(contentPadding),
|
|
|
|
|
)
|
2022-07-16 20:44:37 +02:00
|
|
|
else ->
|
2022-04-27 14:36:16 +02:00
|
|
|
MigrateSourceList(
|
2022-11-24 04:28:25 +01:00
|
|
|
list = state.items,
|
2022-10-09 21:52:56 +02:00
|
|
|
contentPadding = contentPadding,
|
2022-04-27 14:36:16 +02:00
|
|
|
onClickItem = onClickItem,
|
2022-07-16 20:44:37 +02:00
|
|
|
onLongClickItem = { source ->
|
|
|
|
|
val sourceId = source.id.toString()
|
|
|
|
|
context.copyToClipboard(sourceId, sourceId)
|
|
|
|
|
},
|
2022-11-24 04:28:25 +01:00
|
|
|
sortingMode = state.sortingMode,
|
|
|
|
|
onToggleSortingMode = onToggleSortingMode,
|
|
|
|
|
sortingDirection = state.sortingDirection,
|
|
|
|
|
onToggleSortingDirection = onToggleSortingDirection,
|
2024-08-05 22:38:09 +02:00
|
|
|
// KMK -->
|
|
|
|
|
state = state,
|
|
|
|
|
onChangeSearchQuery = onChangeSearchQuery,
|
|
|
|
|
// KMK <--
|
2022-04-27 14:36:16 +02:00
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Composable
|
2022-09-19 04:38:44 +02:00
|
|
|
private fun MigrateSourceList(
|
2024-01-08 04:22:32 +01:00
|
|
|
list: ImmutableList<Pair<Source, Long>>,
|
2022-10-09 21:52:56 +02:00
|
|
|
contentPadding: PaddingValues,
|
2022-04-27 14:36:16 +02:00
|
|
|
onClickItem: (Source) -> Unit,
|
|
|
|
|
onLongClickItem: (Source) -> Unit,
|
2022-08-29 23:18:06 +02:00
|
|
|
sortingMode: SetMigrateSorting.Mode,
|
|
|
|
|
onToggleSortingMode: () -> Unit,
|
|
|
|
|
sortingDirection: SetMigrateSorting.Direction,
|
|
|
|
|
onToggleSortingDirection: () -> Unit,
|
2024-08-05 22:38:09 +02:00
|
|
|
// KMK -->
|
|
|
|
|
state: MigrateSourceScreenModel.State,
|
|
|
|
|
onChangeSearchQuery: (String?) -> Unit,
|
|
|
|
|
// KMK <--
|
2022-04-27 14:36:16 +02:00
|
|
|
) {
|
2024-08-05 22:38:09 +02:00
|
|
|
// KMK -->
|
|
|
|
|
val lazyListState = rememberLazyListState()
|
2024-11-24 03:23:00 +01:00
|
|
|
var filterObsoleteSource by rememberSaveable { mutableStateOf(false) }
|
2025-05-15 19:38:03 +02:00
|
|
|
val isHentaiEnabled = remember { Injekt.get<ExhPreferences>().isHentaiEnabled().get() }
|
2024-08-05 22:38:09 +02:00
|
|
|
|
|
|
|
|
BackHandler(enabled = !state.searchQuery.isNullOrBlank()) {
|
2024-11-08 09:43:38 +01:00
|
|
|
onChangeSearchQuery("")
|
2024-08-05 22:38:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Column(
|
2024-11-08 09:43:38 +01:00
|
|
|
modifier = Modifier.padding(contentPadding),
|
2022-04-27 14:36:16 +02:00
|
|
|
) {
|
2025-11-28 08:21:41 +01:00
|
|
|
// KMK <--
|
|
|
|
|
Row(
|
2024-08-30 20:59:39 +02:00
|
|
|
modifier = Modifier
|
2025-11-28 08:21:41 +01:00
|
|
|
.background(MaterialTheme.colorScheme.background)
|
|
|
|
|
.padding(start = MaterialTheme.padding.medium),
|
|
|
|
|
verticalAlignment = Alignment.CenterVertically,
|
|
|
|
|
) {
|
|
|
|
|
Text(
|
|
|
|
|
text = stringResource(MR.strings.migration_selection_prompt),
|
|
|
|
|
modifier = Modifier.weight(1f),
|
|
|
|
|
style = MaterialTheme.typography.header,
|
|
|
|
|
)
|
2024-08-05 22:38:09 +02:00
|
|
|
|
2025-11-28 08:21:41 +01:00
|
|
|
// KMK -->
|
|
|
|
|
IconButton(onClick = { filterObsoleteSource = !filterObsoleteSource }) {
|
|
|
|
|
Icon(
|
|
|
|
|
Icons.Outlined.NewReleases,
|
|
|
|
|
contentDescription = stringResource(MR.strings.ext_obsolete),
|
|
|
|
|
tint = MaterialTheme.colorScheme.error
|
|
|
|
|
.takeIf { filterObsoleteSource } ?: LocalContentColor.current,
|
|
|
|
|
)
|
|
|
|
|
}
|
2024-08-05 22:38:09 +02:00
|
|
|
// KMK <--
|
2025-11-28 08:21:41 +01:00
|
|
|
IconButton(onClick = onToggleSortingMode) {
|
|
|
|
|
when (sortingMode) {
|
|
|
|
|
SetMigrateSorting.Mode.ALPHABETICAL -> Icon(
|
|
|
|
|
Icons.Outlined.SortByAlpha,
|
|
|
|
|
contentDescription = stringResource(MR.strings.action_sort_alpha),
|
|
|
|
|
)
|
|
|
|
|
SetMigrateSorting.Mode.TOTAL -> Icon(
|
|
|
|
|
Icons.Outlined.Numbers,
|
|
|
|
|
contentDescription = stringResource(MR.strings.action_sort_count),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
IconButton(onClick = onToggleSortingDirection) {
|
|
|
|
|
when (sortingDirection) {
|
|
|
|
|
SetMigrateSorting.Direction.ASCENDING -> Icon(
|
|
|
|
|
Icons.Outlined.ArrowUpward,
|
|
|
|
|
contentDescription = stringResource(MR.strings.action_asc),
|
|
|
|
|
)
|
|
|
|
|
SetMigrateSorting.Direction.DESCENDING -> Icon(
|
|
|
|
|
Icons.Outlined.ArrowDownward,
|
|
|
|
|
contentDescription = stringResource(MR.strings.action_desc),
|
2024-08-05 22:38:09 +02:00
|
|
|
)
|
2022-08-29 23:18:06 +02:00
|
|
|
}
|
|
|
|
|
}
|
2025-11-28 08:21:41 +01:00
|
|
|
}
|
2022-08-29 23:18:06 +02:00
|
|
|
|
2025-11-28 08:21:41 +01:00
|
|
|
// KMK -->
|
|
|
|
|
Box {
|
2025-12-09 07:19:54 +01:00
|
|
|
val density = LocalDensity.current
|
|
|
|
|
var searchBoxHeight by remember { mutableStateOf(SOURCE_SEARCH_BOX_HEIGHT) }
|
|
|
|
|
|
2025-11-28 08:21:41 +01:00
|
|
|
FastScrollLazyColumn(
|
|
|
|
|
state = lazyListState,
|
2025-12-09 07:19:54 +01:00
|
|
|
contentPadding = PaddingValues(top = searchBoxHeight),
|
2025-02-22 17:45:04 +01:00
|
|
|
// KMK <--
|
2025-11-28 08:21:41 +01:00
|
|
|
) {
|
|
|
|
|
items(
|
|
|
|
|
items = list
|
|
|
|
|
// KMK -->
|
|
|
|
|
.filter {
|
|
|
|
|
!filterObsoleteSource ||
|
|
|
|
|
(
|
|
|
|
|
it.first.installedExtension?.isObsolete != false &&
|
2025-12-09 09:21:33 +01:00
|
|
|
(!isHentaiEnabled || it.first.id !in hentaiSourceIds)
|
2025-11-28 08:21:41 +01:00
|
|
|
)
|
|
|
|
|
},
|
2024-08-05 22:38:09 +02:00
|
|
|
// KMK <--
|
2025-11-28 08:21:41 +01:00
|
|
|
key = { (source, _) -> "migrate-${source.id}" },
|
|
|
|
|
) { (source, count) ->
|
|
|
|
|
MigrateSourceItem(
|
|
|
|
|
// KMK -->
|
|
|
|
|
// modifier = Modifier.animateItem(),
|
2025-12-09 07:19:54 +01:00
|
|
|
modifier = Modifier.animateItemFastScroll()
|
|
|
|
|
.padding(end = MaterialTheme.padding.small),
|
2025-11-28 08:21:41 +01:00
|
|
|
// KMK <--
|
|
|
|
|
source = source,
|
|
|
|
|
count = count,
|
|
|
|
|
onClickItem = { onClickItem(source) },
|
|
|
|
|
onLongClickItem = { onLongClickItem(source) },
|
|
|
|
|
)
|
|
|
|
|
}
|
2024-08-05 22:38:09 +02:00
|
|
|
}
|
2025-11-28 08:21:41 +01:00
|
|
|
|
|
|
|
|
// KMK -->
|
|
|
|
|
AnimatedFloatingSearchBox(
|
|
|
|
|
listState = lazyListState,
|
|
|
|
|
searchQuery = state.searchQuery,
|
|
|
|
|
onChangeSearchQuery = onChangeSearchQuery,
|
|
|
|
|
placeholderText = stringResource(KMR.strings.action_search_for_source),
|
|
|
|
|
modifier = Modifier
|
|
|
|
|
.background(MaterialTheme.colorScheme.background)
|
|
|
|
|
.padding(
|
|
|
|
|
horizontal = MaterialTheme.padding.medium,
|
|
|
|
|
)
|
|
|
|
|
.align(Alignment.TopCenter),
|
2025-12-09 07:19:54 +01:00
|
|
|
onGloballyPositioned = { layoutCoordinates ->
|
|
|
|
|
searchBoxHeight = with(density) { layoutCoordinates.size.height.toDp() }
|
|
|
|
|
},
|
2025-11-28 08:21:41 +01:00
|
|
|
)
|
|
|
|
|
// KMK <--
|
2022-04-27 14:36:16 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Composable
|
2022-09-19 04:38:44 +02:00
|
|
|
private fun MigrateSourceItem(
|
2022-04-27 14:36:16 +02:00
|
|
|
source: Source,
|
|
|
|
|
count: Long,
|
|
|
|
|
onClickItem: () -> Unit,
|
|
|
|
|
onLongClickItem: () -> Unit,
|
2023-11-17 15:46:13 +01:00
|
|
|
modifier: Modifier = Modifier,
|
2022-04-27 14:36:16 +02:00
|
|
|
) {
|
|
|
|
|
BaseSourceItem(
|
|
|
|
|
modifier = modifier,
|
|
|
|
|
source = source,
|
2022-04-30 14:33:00 +02:00
|
|
|
showLanguageInContent = source.lang != "",
|
2022-04-27 14:36:16 +02:00
|
|
|
onClickItem = onClickItem,
|
|
|
|
|
onLongClickItem = onLongClickItem,
|
2022-06-19 16:16:55 +02:00
|
|
|
icon = { SourceIcon(source = source) },
|
2022-04-27 14:36:16 +02:00
|
|
|
action = {
|
2022-07-16 23:40:40 +02:00
|
|
|
BadgeGroup {
|
|
|
|
|
Badge(text = "$count")
|
|
|
|
|
}
|
2022-06-14 15:10:40 +02:00
|
|
|
},
|
2024-08-20 21:39:11 +02:00
|
|
|
content = { _, sourceLangString, /* KMK --> */ lang /* KMK <-- */ ->
|
2022-06-14 15:10:40 +02:00
|
|
|
Column(
|
|
|
|
|
modifier = Modifier
|
2022-11-13 18:11:51 +01:00
|
|
|
.padding(horizontal = MaterialTheme.padding.medium)
|
2022-06-14 15:10:40 +02:00
|
|
|
.weight(1f),
|
|
|
|
|
) {
|
|
|
|
|
Text(
|
|
|
|
|
text = source.name.ifBlank { source.id.toString() },
|
|
|
|
|
maxLines = 1,
|
|
|
|
|
overflow = TextOverflow.Ellipsis,
|
|
|
|
|
style = MaterialTheme.typography.bodyMedium,
|
|
|
|
|
)
|
|
|
|
|
Row(
|
2024-08-29 11:21:08 +02:00
|
|
|
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
|
2022-06-14 15:10:40 +02:00
|
|
|
verticalAlignment = Alignment.CenterVertically,
|
|
|
|
|
) {
|
2022-10-04 05:05:37 +02:00
|
|
|
if (sourceLangString != null) {
|
2022-06-14 15:10:40 +02:00
|
|
|
Text(
|
2022-10-09 21:49:32 +02:00
|
|
|
modifier = Modifier.secondaryItemAlpha(),
|
2024-08-29 11:21:08 +02:00
|
|
|
text = /* KMK --> */ FlagEmoji.getEmojiLangFlag(lang) + " " + /* KMK <-- */
|
|
|
|
|
sourceLangString,
|
2022-06-14 15:10:40 +02:00
|
|
|
maxLines = 1,
|
|
|
|
|
overflow = TextOverflow.Ellipsis,
|
|
|
|
|
style = MaterialTheme.typography.bodySmall,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
if (source.isStub) {
|
|
|
|
|
Text(
|
2022-10-09 21:49:32 +02:00
|
|
|
modifier = Modifier.secondaryItemAlpha(),
|
2023-11-19 01:41:33 +01:00
|
|
|
text = stringResource(MR.strings.not_installed),
|
2022-06-14 15:10:40 +02:00
|
|
|
maxLines = 1,
|
|
|
|
|
overflow = TextOverflow.Ellipsis,
|
|
|
|
|
style = MaterialTheme.typography.bodySmall,
|
|
|
|
|
color = MaterialTheme.colorScheme.error,
|
|
|
|
|
)
|
2024-10-28 16:20:52 +01:00
|
|
|
// KMK -->
|
|
|
|
|
} else if (source.installedExtension?.isObsolete == true) {
|
|
|
|
|
Text(
|
|
|
|
|
modifier = Modifier.secondaryItemAlpha(),
|
|
|
|
|
text = stringResource(MR.strings.ext_obsolete),
|
|
|
|
|
maxLines = 1,
|
|
|
|
|
overflow = TextOverflow.Ellipsis,
|
|
|
|
|
style = MaterialTheme.typography.bodySmall,
|
|
|
|
|
color = MaterialTheme.colorScheme.error,
|
|
|
|
|
)
|
|
|
|
|
// KMK <--
|
2022-06-14 15:10:40 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-05-10 23:54:52 +02:00
|
|
|
},
|
2022-04-27 14:36:16 +02:00
|
|
|
)
|
|
|
|
|
}
|