Add source search, icon & flag on Feed adding dialog (#324)
* add language flag * add source icon * source search
This commit is contained in:
parent
e94a6d3d80
commit
eb62927c1b
6 changed files with 82 additions and 9 deletions
|
|
@ -16,6 +16,7 @@ import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.RadioButton
|
import androidx.compose.material3.RadioButton
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
|
|
@ -34,7 +35,9 @@ import eu.kanade.presentation.browse.components.GlobalSearchCardRow
|
||||||
import eu.kanade.presentation.browse.components.GlobalSearchErrorResultItem
|
import eu.kanade.presentation.browse.components.GlobalSearchErrorResultItem
|
||||||
import eu.kanade.presentation.browse.components.GlobalSearchLoadingResultItem
|
import eu.kanade.presentation.browse.components.GlobalSearchLoadingResultItem
|
||||||
import eu.kanade.presentation.browse.components.GlobalSearchResultItem
|
import eu.kanade.presentation.browse.components.GlobalSearchResultItem
|
||||||
|
import eu.kanade.presentation.browse.components.SourceIcon
|
||||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||||
|
import eu.kanade.tachiyomi.source.getNameForMangaInfo
|
||||||
import eu.kanade.tachiyomi.ui.browse.feed.FeedScreenState
|
import eu.kanade.tachiyomi.ui.browse.feed.FeedScreenState
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
|
|
@ -43,11 +46,13 @@ import tachiyomi.core.common.i18n.stringResource
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import tachiyomi.domain.source.model.FeedSavedSearch
|
import tachiyomi.domain.source.model.FeedSavedSearch
|
||||||
import tachiyomi.domain.source.model.SavedSearch
|
import tachiyomi.domain.source.model.SavedSearch
|
||||||
|
import tachiyomi.domain.source.model.Source
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.i18n.kmk.KMR
|
import tachiyomi.i18n.kmk.KMR
|
||||||
import tachiyomi.i18n.sy.SYMR
|
import tachiyomi.i18n.sy.SYMR
|
||||||
import tachiyomi.presentation.core.components.ScrollbarLazyColumn
|
import tachiyomi.presentation.core.components.ScrollbarLazyColumn
|
||||||
import tachiyomi.presentation.core.components.material.PullRefresh
|
import tachiyomi.presentation.core.components.material.PullRefresh
|
||||||
|
import tachiyomi.presentation.core.components.material.padding
|
||||||
import tachiyomi.presentation.core.components.material.topSmallPaddingValues
|
import tachiyomi.presentation.core.components.material.topSmallPaddingValues
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
import tachiyomi.presentation.core.screens.EmptyScreen
|
import tachiyomi.presentation.core.screens.EmptyScreen
|
||||||
|
|
@ -190,13 +195,49 @@ fun FeedAddDialog(
|
||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
onClickAdd: (CatalogueSource?) -> Unit,
|
onClickAdd: (CatalogueSource?) -> Unit,
|
||||||
) {
|
) {
|
||||||
|
// KMK -->
|
||||||
|
var query by remember { mutableStateOf("") }
|
||||||
|
val sourceComposes: List<@Composable () -> Unit> = sources
|
||||||
|
.filter { source ->
|
||||||
|
if (query.isBlank()) return@filter true
|
||||||
|
query.split(",").any {
|
||||||
|
val input = it.trim()
|
||||||
|
if (input.isEmpty()) return@any false
|
||||||
|
source.name.contains(input, ignoreCase = true) ||
|
||||||
|
source.id == input.toLongOrNull()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.map {
|
||||||
|
{
|
||||||
|
val source = Source(
|
||||||
|
id = it.id,
|
||||||
|
lang = it.lang,
|
||||||
|
name = it.name,
|
||||||
|
supportsLatest = it.supportsLatest,
|
||||||
|
isStub = false,
|
||||||
|
)
|
||||||
|
SourceIcon(source = source)
|
||||||
|
Spacer(modifier = Modifier.width(MaterialTheme.padding.extraSmall))
|
||||||
|
Text(text = it.getNameForMangaInfo())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// KMK <--
|
||||||
var selected by remember { mutableStateOf<Int?>(null) }
|
var selected by remember { mutableStateOf<Int?>(null) }
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
title = {
|
title = {
|
||||||
Text(text = stringResource(SYMR.strings.feed))
|
Text(text = stringResource(SYMR.strings.feed))
|
||||||
},
|
},
|
||||||
text = {
|
text = {
|
||||||
RadioSelector(options = sources, selected = selected) {
|
// KMK -->
|
||||||
|
RadioSelectorSearchable(
|
||||||
|
options = sourceComposes,
|
||||||
|
queryString = query,
|
||||||
|
onChangeSearchQuery = {
|
||||||
|
query = it ?: ""
|
||||||
|
},
|
||||||
|
// KMK <--
|
||||||
|
selected = selected,
|
||||||
|
) {
|
||||||
selected = it
|
selected = it
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -237,7 +278,7 @@ fun FeedAddSearchDialog(
|
||||||
// KMK <--
|
// KMK <--
|
||||||
}.toImmutableList()
|
}.toImmutableList()
|
||||||
}
|
}
|
||||||
RadioSelector(
|
RadioSelectorSearchable(
|
||||||
options = savedSearches,
|
options = savedSearches,
|
||||||
optionStrings = savedSearchStrings,
|
optionStrings = savedSearchStrings,
|
||||||
selected = selected,
|
selected = selected,
|
||||||
|
|
@ -260,7 +301,7 @@ fun FeedAddSearchDialog(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun <T> RadioSelector(
|
fun <T> RadioSelectorSearchable(
|
||||||
options: ImmutableList<T>,
|
options: ImmutableList<T>,
|
||||||
optionStrings: ImmutableList<String> = remember { options.map { it.toString() }.toImmutableList() },
|
optionStrings: ImmutableList<String> = remember { options.map { it.toString() }.toImmutableList() },
|
||||||
selected: Int?,
|
selected: Int?,
|
||||||
|
|
@ -276,13 +317,45 @@ fun <T> RadioSelector(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
RadioButton(selected == index, onClick = null)
|
RadioButton(selected == index, onClick = null)
|
||||||
Spacer(Modifier.width(4.dp))
|
Spacer(Modifier.width(MaterialTheme.padding.extraSmall))
|
||||||
Text(option, maxLines = 1)
|
Text(option, maxLines = 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// KMK -->
|
||||||
|
@Composable
|
||||||
|
fun RadioSelectorSearchable(
|
||||||
|
options: List<@Composable () -> Unit>,
|
||||||
|
queryString: String? = null,
|
||||||
|
onChangeSearchQuery: ((String?) -> Unit)? = null,
|
||||||
|
selected: Int?,
|
||||||
|
onSelectOption: (Int) -> Unit = {},
|
||||||
|
) {
|
||||||
|
Column(Modifier.verticalScroll(rememberScrollState())) {
|
||||||
|
SourcesSearch(
|
||||||
|
searchQuery = queryString,
|
||||||
|
onChangeSearchQuery = onChangeSearchQuery ?: {},
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
).takeIf { onChangeSearchQuery != null }
|
||||||
|
options.forEachIndexed { index, option ->
|
||||||
|
Row(
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(48.dp)
|
||||||
|
.clickable { onSelectOption(index) },
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
RadioButton(selected == index, onClick = null)
|
||||||
|
Spacer(Modifier.width(MaterialTheme.padding.extraSmall))
|
||||||
|
option()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// KMK <--
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun FeedDeleteConfirmDialog(
|
fun FeedDeleteConfirmDialog(
|
||||||
feed: FeedSavedSearch,
|
feed: FeedSavedSearch,
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
fun Source.getNameForMangaInfo(
|
fun Source.getNameForMangaInfo(
|
||||||
// SY -->
|
// SY -->
|
||||||
mergeSources: List<Source>?,
|
mergeSources: List<Source>? = null,
|
||||||
// SY <--
|
// SY <--
|
||||||
): String {
|
): String {
|
||||||
val preferences = Injekt.get<SourcePreferences>()
|
val preferences = Injekt.get<SourcePreferences>()
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ class MigrationSourceHolder(view: View, val adapter: MigrationSourceAdapter) :
|
||||||
// Set capitalized title.
|
// Set capitalized title.
|
||||||
val sourceName =
|
val sourceName =
|
||||||
// KMK -->
|
// KMK -->
|
||||||
source.getNameForMangaInfo(null)
|
source.getNameForMangaInfo()
|
||||||
// KMK <--
|
// KMK <--
|
||||||
binding.title.text = sourceName
|
binding.title.text = sourceName
|
||||||
// Update circle letter image.
|
// Update circle letter image.
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,7 @@ class MigrationListScreenModel(
|
||||||
chapterCount = chapters.size,
|
chapterCount = chapters.size,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
fun getSourceName(manga: Manga) = sourceManager.getOrStub(manga.source).getNameForMangaInfo(null)
|
fun getSourceName(manga: Manga) = sourceManager.getOrStub(manga.source).getNameForMangaInfo()
|
||||||
|
|
||||||
fun getMigrationSources() = preferences.migrationSources().get().split("/").mapNotNull {
|
fun getMigrationSources() = preferences.migrationSources().get().split("/").mapNotNull {
|
||||||
val value = it.toLongOrNull() ?: return@mapNotNull null
|
val value = it.toLongOrNull() ?: return@mapNotNull null
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ data class LibraryItem(
|
||||||
* @return true if the manga matches the query, false otherwise.
|
* @return true if the manga matches the query, false otherwise.
|
||||||
*/
|
*/
|
||||||
fun matches(constraint: String): Boolean {
|
fun matches(constraint: String): Boolean {
|
||||||
val sourceName by lazy { sourceManager.getOrStub(libraryManga.manga.source).getNameForMangaInfo(null) }
|
val sourceName by lazy { sourceManager.getOrStub(libraryManga.manga.source).getNameForMangaInfo() }
|
||||||
return libraryManga.manga.title.contains(constraint, true) ||
|
return libraryManga.manga.title.contains(constraint, true) ||
|
||||||
(libraryManga.manga.author?.contains(constraint, true) ?: false) ||
|
(libraryManga.manga.author?.contains(constraint, true) ?: false) ||
|
||||||
(libraryManga.manga.artist?.contains(constraint, true) ?: false) ||
|
(libraryManga.manga.artist?.contains(constraint, true) ?: false) ||
|
||||||
|
|
|
||||||
|
|
@ -1032,7 +1032,7 @@ class MangaScreenModel(
|
||||||
downloadProgress = activeDownload?.progress ?: 0,
|
downloadProgress = activeDownload?.progress ?: 0,
|
||||||
selected = chapter.id in selectedChapterIds,
|
selected = chapter.id in selectedChapterIds,
|
||||||
// SY -->
|
// SY -->
|
||||||
sourceName = source?.getNameForMangaInfo(null),
|
sourceName = source?.getNameForMangaInfo(),
|
||||||
showScanlator = !isExhManga,
|
showScanlator = !isExhManga,
|
||||||
// SY <--
|
// SY <--
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue