Add source search, icon & flag on Feed adding dialog (#324)

* add language flag

* add source icon

* source search
This commit is contained in:
Cuong-Tran 2024-08-31 01:23:20 +07:00 committed by GitHub
parent e94a6d3d80
commit eb62927c1b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 82 additions and 9 deletions

View file

@ -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,

View file

@ -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>()

View file

@ -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.

View file

@ -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

View file

@ -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) ||

View file

@ -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 <--
) )