Replicate global search filters to migrate screen
Still needs better refactoring to dedupe all of this stuff though...
This commit is contained in:
parent
8b46e8edad
commit
dd3ca0c131
@ -1,45 +1,21 @@
|
|||||||
package eu.kanade.presentation.browse
|
package eu.kanade.presentation.browse
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.horizontalScroll
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.layout.size
|
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.rememberScrollState
|
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.outlined.DoneAll
|
|
||||||
import androidx.compose.material.icons.outlined.FilterList
|
|
||||||
import androidx.compose.material.icons.outlined.PushPin
|
|
||||||
import androidx.compose.material3.FilterChip
|
|
||||||
import androidx.compose.material3.FilterChipDefaults
|
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import eu.kanade.presentation.browse.components.GlobalSearchCardRow
|
import eu.kanade.presentation.browse.components.GlobalSearchCardRow
|
||||||
import eu.kanade.presentation.browse.components.GlobalSearchEmptyResultItem
|
|
||||||
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.GlobalSearchToolbar
|
import eu.kanade.presentation.browse.components.GlobalSearchToolbar
|
||||||
import eu.kanade.tachiyomi.R
|
|
||||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||||
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchScreenModel
|
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchScreenModel
|
||||||
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchItemResult
|
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchItemResult
|
||||||
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SourceFilter
|
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SourceFilter
|
||||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import tachiyomi.presentation.core.components.material.Divider
|
|
||||||
import tachiyomi.presentation.core.components.material.Scaffold
|
import tachiyomi.presentation.core.components.material.Scaffold
|
||||||
import tachiyomi.presentation.core.components.material.VerticalDivider
|
|
||||||
import tachiyomi.presentation.core.components.material.padding
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun GlobalSearchScreen(
|
fun GlobalSearchScreen(
|
||||||
@ -56,76 +32,19 @@ fun GlobalSearchScreen(
|
|||||||
) {
|
) {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = { scrollBehavior ->
|
topBar = { scrollBehavior ->
|
||||||
Column(modifier = Modifier.background(MaterialTheme.colorScheme.surface)) {
|
GlobalSearchToolbar(
|
||||||
GlobalSearchToolbar(
|
searchQuery = state.searchQuery,
|
||||||
searchQuery = state.searchQuery,
|
progress = state.progress,
|
||||||
progress = state.progress,
|
total = state.total,
|
||||||
total = state.total,
|
navigateUp = navigateUp,
|
||||||
navigateUp = navigateUp,
|
onChangeSearchQuery = onChangeSearchQuery,
|
||||||
onChangeSearchQuery = onChangeSearchQuery,
|
onSearch = onSearch,
|
||||||
onSearch = onSearch,
|
sourceFilter = state.sourceFilter,
|
||||||
scrollBehavior = scrollBehavior,
|
onChangeSearchFilter = onChangeSearchFilter,
|
||||||
)
|
onlyShowHasResults = state.onlyShowHasResults,
|
||||||
|
onToggleResults = onToggleResults,
|
||||||
Row(
|
scrollBehavior = scrollBehavior,
|
||||||
modifier = Modifier
|
)
|
||||||
.horizontalScroll(rememberScrollState())
|
|
||||||
.padding(horizontal = MaterialTheme.padding.small),
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
|
|
||||||
) {
|
|
||||||
// TODO: make this UX better; it only applies when triggering a new search
|
|
||||||
FilterChip(
|
|
||||||
selected = state.sourceFilter == SourceFilter.PinnedOnly,
|
|
||||||
onClick = { onChangeSearchFilter(SourceFilter.PinnedOnly) },
|
|
||||||
leadingIcon = {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Outlined.PushPin,
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier
|
|
||||||
.size(FilterChipDefaults.IconSize),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
label = {
|
|
||||||
Text(text = stringResource(id = R.string.pinned_sources))
|
|
||||||
},
|
|
||||||
)
|
|
||||||
FilterChip(
|
|
||||||
selected = state.sourceFilter == SourceFilter.All,
|
|
||||||
onClick = { onChangeSearchFilter(SourceFilter.All) },
|
|
||||||
leadingIcon = {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Outlined.DoneAll,
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier
|
|
||||||
.size(FilterChipDefaults.IconSize),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
label = {
|
|
||||||
Text(text = stringResource(id = R.string.all))
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
VerticalDivider()
|
|
||||||
|
|
||||||
FilterChip(
|
|
||||||
selected = state.onlyShowHasResults,
|
|
||||||
onClick = { onToggleResults() },
|
|
||||||
leadingIcon = {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Outlined.FilterList,
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier
|
|
||||||
.size(FilterChipDefaults.IconSize),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
label = {
|
|
||||||
Text(text = stringResource(id = R.string.has_results))
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Divider()
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
GlobalSearchContent(
|
GlobalSearchContent(
|
||||||
@ -141,7 +60,7 @@ fun GlobalSearchScreen(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun GlobalSearchContent(
|
internal fun GlobalSearchContent(
|
||||||
sourceId: Long? = null,
|
fromSourceId: Long? = null,
|
||||||
items: Map<CatalogueSource, SearchItemResult>,
|
items: Map<CatalogueSource, SearchItemResult>,
|
||||||
contentPadding: PaddingValues,
|
contentPadding: PaddingValues,
|
||||||
getManga: @Composable (Manga) -> State<Manga>,
|
getManga: @Composable (Manga) -> State<Manga>,
|
||||||
@ -155,7 +74,7 @@ internal fun GlobalSearchContent(
|
|||||||
items.forEach { (source, result) ->
|
items.forEach { (source, result) ->
|
||||||
item(key = source.id) {
|
item(key = source.id) {
|
||||||
GlobalSearchResultItem(
|
GlobalSearchResultItem(
|
||||||
title = sourceId?.let { "▶ ${source.name}".takeIf { source.id == sourceId } } ?: source.name,
|
title = fromSourceId?.let { "▶ ${source.name}".takeIf { source.id == fromSourceId } } ?: source.name,
|
||||||
subtitle = LocaleHelper.getDisplayName(source.lang),
|
subtitle = LocaleHelper.getDisplayName(source.lang),
|
||||||
onClick = { onClickSource(source) },
|
onClick = { onClickSource(source) },
|
||||||
) {
|
) {
|
||||||
@ -164,11 +83,6 @@ internal fun GlobalSearchContent(
|
|||||||
GlobalSearchLoadingResultItem()
|
GlobalSearchLoadingResultItem()
|
||||||
}
|
}
|
||||||
is SearchItemResult.Success -> {
|
is SearchItemResult.Success -> {
|
||||||
if (result.isEmpty) {
|
|
||||||
GlobalSearchEmptyResultItem()
|
|
||||||
return@GlobalSearchResultItem
|
|
||||||
}
|
|
||||||
|
|
||||||
GlobalSearchCardRow(
|
GlobalSearchCardRow(
|
||||||
titles = result.result,
|
titles = result.result,
|
||||||
getManga = getManga,
|
getManga = getManga,
|
||||||
|
@ -5,16 +5,19 @@ import androidx.compose.runtime.State
|
|||||||
import eu.kanade.presentation.browse.components.GlobalSearchToolbar
|
import eu.kanade.presentation.browse.components.GlobalSearchToolbar
|
||||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||||
import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateSearchScreenModel
|
import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateSearchScreenModel
|
||||||
|
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SourceFilter
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import tachiyomi.presentation.core.components.material.Scaffold
|
import tachiyomi.presentation.core.components.material.Scaffold
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MigrateSearchScreen(
|
fun MigrateSearchScreen(
|
||||||
navigateUp: () -> Unit,
|
|
||||||
state: MigrateSearchScreenModel.State,
|
state: MigrateSearchScreenModel.State,
|
||||||
getManga: @Composable (Manga) -> State<Manga>,
|
navigateUp: () -> Unit,
|
||||||
onChangeSearchQuery: (String?) -> Unit,
|
onChangeSearchQuery: (String?) -> Unit,
|
||||||
onSearch: (String) -> Unit,
|
onSearch: (String) -> Unit,
|
||||||
|
onChangeSearchFilter: (SourceFilter) -> Unit,
|
||||||
|
onToggleResults: () -> Unit,
|
||||||
|
getManga: @Composable (Manga) -> State<Manga>,
|
||||||
onClickSource: (CatalogueSource) -> Unit,
|
onClickSource: (CatalogueSource) -> Unit,
|
||||||
onClickItem: (Manga) -> Unit,
|
onClickItem: (Manga) -> Unit,
|
||||||
onLongClickItem: (Manga) -> Unit,
|
onLongClickItem: (Manga) -> Unit,
|
||||||
@ -28,13 +31,17 @@ fun MigrateSearchScreen(
|
|||||||
navigateUp = navigateUp,
|
navigateUp = navigateUp,
|
||||||
onChangeSearchQuery = onChangeSearchQuery,
|
onChangeSearchQuery = onChangeSearchQuery,
|
||||||
onSearch = onSearch,
|
onSearch = onSearch,
|
||||||
|
sourceFilter = state.sourceFilter,
|
||||||
|
onChangeSearchFilter = onChangeSearchFilter,
|
||||||
|
onlyShowHasResults = state.onlyShowHasResults,
|
||||||
|
onToggleResults = onToggleResults,
|
||||||
scrollBehavior = scrollBehavior,
|
scrollBehavior = scrollBehavior,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
GlobalSearchContent(
|
GlobalSearchContent(
|
||||||
sourceId = state.manga?.source,
|
fromSourceId = state.manga?.source,
|
||||||
items = state.items,
|
items = state.filteredItems,
|
||||||
contentPadding = paddingValues,
|
contentPadding = paddingValues,
|
||||||
getManga = getManga,
|
getManga = getManga,
|
||||||
onClickSource = onClickSource,
|
onClickSource = onClickSource,
|
||||||
|
@ -3,17 +3,21 @@ package eu.kanade.presentation.browse.components
|
|||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.LazyRow
|
import androidx.compose.foundation.lazy.LazyRow
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import eu.kanade.presentation.library.components.CommonMangaItemDefaults
|
import eu.kanade.presentation.library.components.CommonMangaItemDefaults
|
||||||
import eu.kanade.presentation.library.components.MangaComfortableGridItem
|
import eu.kanade.presentation.library.components.MangaComfortableGridItem
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import tachiyomi.domain.manga.model.MangaCover
|
import tachiyomi.domain.manga.model.MangaCover
|
||||||
import tachiyomi.domain.manga.model.asMangaCover
|
import tachiyomi.domain.manga.model.asMangaCover
|
||||||
@ -26,13 +30,18 @@ fun GlobalSearchCardRow(
|
|||||||
onClick: (Manga) -> Unit,
|
onClick: (Manga) -> Unit,
|
||||||
onLongClick: (Manga) -> Unit,
|
onLongClick: (Manga) -> Unit,
|
||||||
) {
|
) {
|
||||||
|
if (titles.isEmpty()) {
|
||||||
|
EmptyResultItem()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
LazyRow(
|
LazyRow(
|
||||||
contentPadding = PaddingValues(MaterialTheme.padding.small),
|
contentPadding = PaddingValues(MaterialTheme.padding.small),
|
||||||
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.tiny),
|
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.tiny),
|
||||||
) {
|
) {
|
||||||
items(titles) {
|
items(titles) {
|
||||||
val title by getManga(it)
|
val title by getManga(it)
|
||||||
GlobalSearchCard(
|
MangaItem(
|
||||||
title = title.title,
|
title = title.title,
|
||||||
cover = title.asMangaCover(),
|
cover = title.asMangaCover(),
|
||||||
isFavorite = title.favorite,
|
isFavorite = title.favorite,
|
||||||
@ -44,7 +53,7 @@ fun GlobalSearchCardRow(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun GlobalSearchCard(
|
private fun MangaItem(
|
||||||
title: String,
|
title: String,
|
||||||
cover: MangaCover,
|
cover: MangaCover,
|
||||||
isFavorite: Boolean,
|
isFavorite: Boolean,
|
||||||
@ -64,3 +73,15 @@ private fun GlobalSearchCard(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun EmptyResultItem() {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.no_results_found),
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(
|
||||||
|
horizontal = MaterialTheme.padding.medium,
|
||||||
|
vertical = MaterialTheme.padding.small,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@ -61,18 +61,6 @@ fun GlobalSearchResultItem(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun GlobalSearchEmptyResultItem() {
|
|
||||||
Text(
|
|
||||||
text = stringResource(R.string.no_results_found),
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(
|
|
||||||
horizontal = MaterialTheme.padding.medium,
|
|
||||||
vertical = MaterialTheme.padding.small,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun GlobalSearchLoadingResultItem() {
|
fun GlobalSearchLoadingResultItem() {
|
||||||
Box(
|
Box(
|
||||||
|
@ -1,13 +1,36 @@
|
|||||||
package eu.kanade.presentation.browse.components
|
package eu.kanade.presentation.browse.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.horizontalScroll
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.DoneAll
|
||||||
|
import androidx.compose.material.icons.outlined.FilterList
|
||||||
|
import androidx.compose.material.icons.outlined.PushPin
|
||||||
|
import androidx.compose.material3.FilterChip
|
||||||
|
import androidx.compose.material3.FilterChipDefaults
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.LinearProgressIndicator
|
import androidx.compose.material3.LinearProgressIndicator
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
import eu.kanade.presentation.components.SearchToolbar
|
import eu.kanade.presentation.components.SearchToolbar
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
|
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SourceFilter
|
||||||
|
import tachiyomi.presentation.core.components.material.Divider
|
||||||
|
import tachiyomi.presentation.core.components.material.VerticalDivider
|
||||||
|
import tachiyomi.presentation.core.components.material.padding
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun GlobalSearchToolbar(
|
fun GlobalSearchToolbar(
|
||||||
@ -17,24 +40,89 @@ fun GlobalSearchToolbar(
|
|||||||
navigateUp: () -> Unit,
|
navigateUp: () -> Unit,
|
||||||
onChangeSearchQuery: (String?) -> Unit,
|
onChangeSearchQuery: (String?) -> Unit,
|
||||||
onSearch: (String) -> Unit,
|
onSearch: (String) -> Unit,
|
||||||
|
sourceFilter: SourceFilter,
|
||||||
|
onChangeSearchFilter: (SourceFilter) -> Unit,
|
||||||
|
onlyShowHasResults: Boolean,
|
||||||
|
onToggleResults: () -> Unit,
|
||||||
scrollBehavior: TopAppBarScrollBehavior,
|
scrollBehavior: TopAppBarScrollBehavior,
|
||||||
) {
|
) {
|
||||||
Box {
|
Column(modifier = Modifier.background(MaterialTheme.colorScheme.surface)) {
|
||||||
SearchToolbar(
|
Box {
|
||||||
searchQuery = searchQuery,
|
SearchToolbar(
|
||||||
onChangeSearchQuery = onChangeSearchQuery,
|
searchQuery = searchQuery,
|
||||||
onSearch = onSearch,
|
onChangeSearchQuery = onChangeSearchQuery,
|
||||||
onClickCloseSearch = navigateUp,
|
onSearch = onSearch,
|
||||||
navigateUp = navigateUp,
|
onClickCloseSearch = navigateUp,
|
||||||
scrollBehavior = scrollBehavior,
|
navigateUp = navigateUp,
|
||||||
)
|
scrollBehavior = scrollBehavior,
|
||||||
if (progress in 1 until total) {
|
)
|
||||||
LinearProgressIndicator(
|
if (progress in 1 until total) {
|
||||||
progress = progress / total.toFloat(),
|
LinearProgressIndicator(
|
||||||
modifier = Modifier
|
progress = progress / total.toFloat(),
|
||||||
.align(Alignment.BottomStart)
|
modifier = Modifier
|
||||||
.fillMaxWidth(),
|
.align(Alignment.BottomStart)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.horizontalScroll(rememberScrollState())
|
||||||
|
.padding(horizontal = MaterialTheme.padding.small),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
|
||||||
|
) {
|
||||||
|
// TODO: make this UX better; it only applies when triggering a new search
|
||||||
|
FilterChip(
|
||||||
|
selected = sourceFilter == SourceFilter.PinnedOnly,
|
||||||
|
onClick = { onChangeSearchFilter(SourceFilter.PinnedOnly) },
|
||||||
|
leadingIcon = {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.PushPin,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier
|
||||||
|
.size(FilterChipDefaults.IconSize),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
label = {
|
||||||
|
Text(text = stringResource(id = R.string.pinned_sources))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
FilterChip(
|
||||||
|
selected = sourceFilter == SourceFilter.All,
|
||||||
|
onClick = { onChangeSearchFilter(SourceFilter.All) },
|
||||||
|
leadingIcon = {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.DoneAll,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier
|
||||||
|
.size(FilterChipDefaults.IconSize),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
label = {
|
||||||
|
Text(text = stringResource(id = R.string.all))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
VerticalDivider()
|
||||||
|
|
||||||
|
FilterChip(
|
||||||
|
selected = onlyShowHasResults,
|
||||||
|
onClick = { onToggleResults() },
|
||||||
|
leadingIcon = {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.FilterList,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier
|
||||||
|
.size(FilterChipDefaults.IconSize),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
label = {
|
||||||
|
Text(text = stringResource(id = R.string.has_results))
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Divider()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,11 +21,13 @@ class MigrateSearchScreen(private val mangaId: Long) : Screen() {
|
|||||||
val state by screenModel.state.collectAsState()
|
val state by screenModel.state.collectAsState()
|
||||||
|
|
||||||
MigrateSearchScreen(
|
MigrateSearchScreen(
|
||||||
navigateUp = navigator::pop,
|
|
||||||
state = state,
|
state = state,
|
||||||
getManga = { screenModel.getManga(it) },
|
navigateUp = navigator::pop,
|
||||||
onChangeSearchQuery = screenModel::updateSearchQuery,
|
onChangeSearchQuery = screenModel::updateSearchQuery,
|
||||||
onSearch = screenModel::search,
|
onSearch = screenModel::search,
|
||||||
|
getManga = { screenModel.getManga(it) },
|
||||||
|
onChangeSearchFilter = screenModel::setSourceFilter,
|
||||||
|
onToggleResults = screenModel::toggleFilterResults,
|
||||||
onClickSource = {
|
onClickSource = {
|
||||||
if (!screenModel.incognitoMode.get()) {
|
if (!screenModel.incognitoMode.get()) {
|
||||||
screenModel.lastUsedSourceId.set(it.id)
|
screenModel.lastUsedSourceId.set(it.id)
|
||||||
|
@ -7,6 +7,7 @@ import eu.kanade.domain.source.service.SourcePreferences
|
|||||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||||
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchItemResult
|
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchItemResult
|
||||||
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchScreenModel
|
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchScreenModel
|
||||||
|
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SourceFilter
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import tachiyomi.domain.manga.interactor.GetManga
|
import tachiyomi.domain.manga.interactor.GetManga
|
||||||
@ -23,6 +24,9 @@ class MigrateSearchScreenModel(
|
|||||||
private val getManga: GetManga = Injekt.get(),
|
private val getManga: GetManga = Injekt.get(),
|
||||||
) : SearchScreenModel<MigrateSearchScreenModel.State>(State()) {
|
) : SearchScreenModel<MigrateSearchScreenModel.State>(State()) {
|
||||||
|
|
||||||
|
val incognitoMode = preferences.incognitoMode()
|
||||||
|
val lastUsedSourceId = sourcePreferences.lastUsedSource()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
val manga = getManga.await(mangaId)!!
|
val manga = getManga.await(mangaId)!!
|
||||||
@ -35,15 +39,13 @@ class MigrateSearchScreenModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val incognitoMode = preferences.incognitoMode()
|
|
||||||
val lastUsedSourceId = sourcePreferences.lastUsedSource()
|
|
||||||
|
|
||||||
override fun getEnabledSources(): List<CatalogueSource> {
|
override fun getEnabledSources(): List<CatalogueSource> {
|
||||||
val enabledLanguages = sourcePreferences.enabledLanguages().get()
|
val enabledLanguages = sourcePreferences.enabledLanguages().get()
|
||||||
val disabledSources = sourcePreferences.disabledSources().get()
|
val disabledSources = sourcePreferences.disabledSources().get()
|
||||||
val pinnedSources = sourcePreferences.pinnedSources().get()
|
val pinnedSources = sourcePreferences.pinnedSources().get()
|
||||||
|
|
||||||
return sourceManager.getCatalogueSources()
|
return sourceManager.getCatalogueSources()
|
||||||
|
.filter { mutableState.value.sourceFilter != SourceFilter.PinnedOnly || "${it.id}" in pinnedSources }
|
||||||
.filter { it.lang in enabledLanguages }
|
.filter { it.lang in enabledLanguages }
|
||||||
.filterNot { "${it.id}" in disabledSources }
|
.filterNot { "${it.id}" in disabledSources }
|
||||||
.sortedWith(compareBy({ "${it.id}" !in pinnedSources }, { "${it.name.lowercase()} (${it.lang})" }))
|
.sortedWith(compareBy({ "${it.id}" !in pinnedSources }, { "${it.name.lowercase()} (${it.lang})" }))
|
||||||
@ -66,6 +68,16 @@ class MigrateSearchScreenModel(
|
|||||||
return mutableState.value.items
|
return mutableState.value.items
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setSourceFilter(filter: SourceFilter) {
|
||||||
|
mutableState.update { it.copy(sourceFilter = filter) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toggleFilterResults() {
|
||||||
|
mutableState.update {
|
||||||
|
it.copy(onlyShowHasResults = !it.onlyShowHasResults)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun setDialog(dialog: MigrateSearchDialog?) {
|
fun setDialog(dialog: MigrateSearchDialog?) {
|
||||||
mutableState.update {
|
mutableState.update {
|
||||||
it.copy(dialog = dialog)
|
it.copy(dialog = dialog)
|
||||||
@ -75,12 +87,16 @@ class MigrateSearchScreenModel(
|
|||||||
@Immutable
|
@Immutable
|
||||||
data class State(
|
data class State(
|
||||||
val manga: Manga? = null,
|
val manga: Manga? = null,
|
||||||
val searchQuery: String? = null,
|
|
||||||
val items: Map<CatalogueSource, SearchItemResult> = emptyMap(),
|
|
||||||
val dialog: MigrateSearchDialog? = null,
|
val dialog: MigrateSearchDialog? = null,
|
||||||
|
|
||||||
|
val searchQuery: String? = null,
|
||||||
|
val sourceFilter: SourceFilter = SourceFilter.PinnedOnly,
|
||||||
|
val onlyShowHasResults: Boolean = false,
|
||||||
|
val items: Map<CatalogueSource, SearchItemResult> = emptyMap(),
|
||||||
) {
|
) {
|
||||||
val progress: Int = items.count { it.value !is SearchItemResult.Loading }
|
val progress: Int = items.count { it.value !is SearchItemResult.Loading }
|
||||||
val total: Int = items.size
|
val total: Int = items.size
|
||||||
|
val filteredItems = items.filter { (_, result) -> result.isVisible(onlyShowHasResults) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ class GlobalSearchScreenModel(
|
|||||||
|
|
||||||
val incognitoMode = preferences.incognitoMode()
|
val incognitoMode = preferences.incognitoMode()
|
||||||
val lastUsedSourceId = sourcePreferences.lastUsedSource()
|
val lastUsedSourceId = sourcePreferences.lastUsedSource()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
extensionFilter = initialExtensionFilter
|
extensionFilter = initialExtensionFilter
|
||||||
if (initialQuery.isNotBlank() || !initialExtensionFilter.isNullOrBlank()) {
|
if (initialQuery.isNotBlank() || !initialExtensionFilter.isNullOrBlank()) {
|
||||||
@ -76,7 +77,3 @@ class GlobalSearchScreenModel(
|
|||||||
val filteredItems = items.filter { (_, result) -> result.isVisible(onlyShowHasResults) }
|
val filteredItems = items.filter { (_, result) -> result.isVisible(onlyShowHasResults) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun SearchItemResult.isVisible(onlyShowHasResults: Boolean): Boolean {
|
|
||||||
return !onlyShowHasResults || (this is SearchItemResult.Success && !this.isEmpty)
|
|
||||||
}
|
|
||||||
|
@ -145,4 +145,8 @@ sealed class SearchItemResult {
|
|||||||
val isEmpty: Boolean
|
val isEmpty: Boolean
|
||||||
get() = result.isEmpty()
|
get() = result.isEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isVisible(onlyShowHasResults: Boolean): Boolean {
|
||||||
|
return !onlyShowHasResults || (this is Success && !this.isEmpty)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user