(cherry picked from commit 92132c59f5417ef81a7bbba6849be849282fc25e) # Conflicts: # app/src/main/java/eu/kanade/presentation/components/SettingsItems.kt # app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterSheet.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/GroupItem.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/SectionItems.kt # app/src/main/java/eu/kanade/tachiyomi/widget/SimpleNavigationView.kt # app/src/main/java/eu/kanade/tachiyomi/widget/listener/IgnoreFirstSpinnerListener.kt # app/src/main/res/drawable/empty_drawable_32dp.xml # app/src/main/res/drawable/ic_check_box_24dp.xml # app/src/main/res/drawable/ic_check_box_outline_blank_24dp.xml # app/src/main/res/drawable/ic_check_box_x_24dp.xml # app/src/main/res/layout/navigation_view_checkbox.xml # app/src/main/res/layout/navigation_view_checkedtext.xml # app/src/main/res/layout/navigation_view_group.xml # app/src/main/res/layout/navigation_view_radio.xml # app/src/main/res/layout/navigation_view_spinner.xml # app/src/main/res/layout/navigation_view_text.xml # app/src/main/res/layout/source_filter_sheet.xml
226 lines
7.3 KiB
Kotlin
226 lines
7.3 KiB
Kotlin
package eu.kanade.presentation.browse
|
|
|
|
import androidx.compose.animation.Crossfade
|
|
import androidx.compose.foundation.layout.PaddingValues
|
|
import androidx.compose.foundation.lazy.items
|
|
import androidx.compose.material3.TopAppBarScrollBehavior
|
|
import androidx.compose.runtime.Composable
|
|
import androidx.compose.runtime.ReadOnlyComposable
|
|
import androidx.compose.runtime.State
|
|
import androidx.compose.ui.Modifier
|
|
import androidx.compose.ui.res.stringResource
|
|
import eu.kanade.presentation.browse.components.BrowseSourceFloatingActionButton
|
|
import eu.kanade.presentation.browse.components.GlobalSearchCardRow
|
|
import eu.kanade.presentation.browse.components.GlobalSearchErrorResultItem
|
|
import eu.kanade.presentation.browse.components.GlobalSearchLoadingResultItem
|
|
import eu.kanade.presentation.browse.components.GlobalSearchResultItem
|
|
import eu.kanade.presentation.components.AppBarTitle
|
|
import eu.kanade.presentation.components.SearchToolbar
|
|
import eu.kanade.tachiyomi.R
|
|
import tachiyomi.domain.manga.model.Manga
|
|
import tachiyomi.domain.source.model.FeedSavedSearch
|
|
import tachiyomi.domain.source.model.SavedSearch
|
|
import tachiyomi.presentation.core.components.ScrollbarLazyColumn
|
|
import tachiyomi.presentation.core.components.material.Scaffold
|
|
import tachiyomi.presentation.core.components.material.topSmallPaddingValues
|
|
import tachiyomi.presentation.core.screens.LoadingScreen
|
|
import tachiyomi.presentation.core.util.plus
|
|
|
|
sealed class SourceFeedUI {
|
|
abstract val id: Long
|
|
|
|
abstract val title: String
|
|
@Composable
|
|
@ReadOnlyComposable
|
|
get
|
|
|
|
abstract val results: List<Manga>?
|
|
|
|
abstract fun withResults(results: List<Manga>?): SourceFeedUI
|
|
|
|
data class Latest(override val results: List<Manga>?) : SourceFeedUI() {
|
|
override val id: Long = -1
|
|
override val title: String
|
|
@Composable
|
|
@ReadOnlyComposable
|
|
get() = stringResource(R.string.latest)
|
|
|
|
override fun withResults(results: List<Manga>?): SourceFeedUI {
|
|
return copy(results = results)
|
|
}
|
|
}
|
|
data class Browse(override val results: List<Manga>?) : SourceFeedUI() {
|
|
override val id: Long = -2
|
|
override val title: String
|
|
@Composable
|
|
@ReadOnlyComposable
|
|
get() = stringResource(R.string.browse)
|
|
|
|
override fun withResults(results: List<Manga>?): SourceFeedUI {
|
|
return copy(results = results)
|
|
}
|
|
}
|
|
data class SourceSavedSearch(
|
|
val feed: FeedSavedSearch,
|
|
val savedSearch: SavedSearch,
|
|
override val results: List<Manga>?,
|
|
) : SourceFeedUI() {
|
|
override val id: Long
|
|
get() = feed.id
|
|
|
|
override val title: String
|
|
@Composable
|
|
@ReadOnlyComposable
|
|
get() = savedSearch.name
|
|
|
|
override fun withResults(results: List<Manga>?): SourceFeedUI {
|
|
return copy(results = results)
|
|
}
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
fun SourceFeedScreen(
|
|
name: String,
|
|
isLoading: Boolean,
|
|
items: List<SourceFeedUI>,
|
|
hasFilters: Boolean,
|
|
onFabClick: () -> Unit,
|
|
onClickBrowse: () -> Unit,
|
|
onClickLatest: () -> Unit,
|
|
onClickSavedSearch: (SavedSearch) -> Unit,
|
|
onClickDelete: (FeedSavedSearch) -> Unit,
|
|
onClickManga: (Manga) -> Unit,
|
|
onClickSearch: (String) -> Unit,
|
|
searchQuery: String?,
|
|
onSearchQueryChange: (String?) -> Unit,
|
|
getMangaState: @Composable (Manga) -> State<Manga>,
|
|
) {
|
|
Scaffold(
|
|
topBar = { scrollBehavior ->
|
|
SourceFeedToolbar(
|
|
title = name,
|
|
searchQuery = searchQuery,
|
|
onSearchQueryChange = onSearchQueryChange,
|
|
scrollBehavior = scrollBehavior,
|
|
onClickSearch = onClickSearch,
|
|
)
|
|
},
|
|
floatingActionButton = {
|
|
BrowseSourceFloatingActionButton(
|
|
isVisible = hasFilters,
|
|
onFabClick = onFabClick,
|
|
)
|
|
},
|
|
) { paddingValues ->
|
|
Crossfade(targetState = isLoading) { state ->
|
|
when (state) {
|
|
true -> LoadingScreen()
|
|
false -> {
|
|
SourceFeedList(
|
|
items = items,
|
|
paddingValues = paddingValues,
|
|
getMangaState = getMangaState,
|
|
onClickBrowse = onClickBrowse,
|
|
onClickLatest = onClickLatest,
|
|
onClickSavedSearch = onClickSavedSearch,
|
|
onClickDelete = onClickDelete,
|
|
onClickManga = onClickManga,
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
fun SourceFeedList(
|
|
items: List<SourceFeedUI>,
|
|
paddingValues: PaddingValues,
|
|
getMangaState: @Composable ((Manga) -> State<Manga>),
|
|
onClickBrowse: () -> Unit,
|
|
onClickLatest: () -> Unit,
|
|
onClickSavedSearch: (SavedSearch) -> Unit,
|
|
onClickDelete: (FeedSavedSearch) -> Unit,
|
|
onClickManga: (Manga) -> Unit,
|
|
) {
|
|
ScrollbarLazyColumn(
|
|
contentPadding = paddingValues + topSmallPaddingValues,
|
|
) {
|
|
items(
|
|
items,
|
|
key = { it.id },
|
|
) { item ->
|
|
GlobalSearchResultItem(
|
|
modifier = Modifier.animateItemPlacement(),
|
|
title = item.title,
|
|
subtitle = null,
|
|
onLongClick = if (item is SourceFeedUI.SourceSavedSearch) {
|
|
{
|
|
onClickDelete(item.feed)
|
|
}
|
|
} else {
|
|
null
|
|
},
|
|
onClick = when (item) {
|
|
is SourceFeedUI.Browse -> onClickBrowse
|
|
is SourceFeedUI.Latest -> onClickLatest
|
|
is SourceFeedUI.SourceSavedSearch -> {
|
|
{ onClickSavedSearch(item.savedSearch) }
|
|
}
|
|
},
|
|
) {
|
|
SourceFeedItem(
|
|
item = item,
|
|
getMangaState = { getMangaState(it) },
|
|
onClickManga = onClickManga,
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
fun SourceFeedItem(
|
|
item: SourceFeedUI,
|
|
getMangaState: @Composable ((Manga) -> State<Manga>),
|
|
onClickManga: (Manga) -> Unit,
|
|
) {
|
|
val results = item.results
|
|
when {
|
|
results == null -> {
|
|
GlobalSearchLoadingResultItem()
|
|
}
|
|
results.isEmpty() -> {
|
|
GlobalSearchErrorResultItem(message = stringResource(R.string.no_results_found))
|
|
}
|
|
else -> {
|
|
GlobalSearchCardRow(
|
|
titles = item.results.orEmpty(),
|
|
getManga = getMangaState,
|
|
onClick = onClickManga,
|
|
onLongClick = onClickManga,
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
fun SourceFeedToolbar(
|
|
title: String,
|
|
searchQuery: String?,
|
|
onSearchQueryChange: (String?) -> Unit,
|
|
scrollBehavior: TopAppBarScrollBehavior,
|
|
onClickSearch: (String) -> Unit,
|
|
) {
|
|
SearchToolbar(
|
|
titleContent = { AppBarTitle(title) },
|
|
searchQuery = searchQuery,
|
|
onChangeSearchQuery = onSearchQueryChange,
|
|
onSearch = onClickSearch,
|
|
onClickCloseSearch = { onSearchQueryChange(null) },
|
|
scrollBehavior = scrollBehavior,
|
|
placeholderText = stringResource(R.string.action_search_hint),
|
|
)
|
|
}
|