Use global search UI for Feed
This commit is contained in:
parent
9d0560f48b
commit
2b1809b2b7
@ -1,32 +1,19 @@
|
||||
package eu.kanade.presentation.browse
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.ContentAlpha
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowForward
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
@ -39,18 +26,16 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import eu.kanade.domain.manga.model.Manga
|
||||
import eu.kanade.presentation.components.Badge
|
||||
import eu.kanade.presentation.components.BadgeGroup
|
||||
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.EmptyScreen
|
||||
import eu.kanade.presentation.components.LoadingScreen
|
||||
import eu.kanade.presentation.components.MangaCover
|
||||
import eu.kanade.presentation.components.PullRefresh
|
||||
import eu.kanade.presentation.components.ScrollbarLazyColumn
|
||||
import eu.kanade.presentation.util.plus
|
||||
@ -62,7 +47,6 @@ import exh.savedsearches.models.FeedSavedSearch
|
||||
import exh.savedsearches.models.SavedSearch
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
import eu.kanade.domain.manga.model.MangaCover as MangaCoverData
|
||||
|
||||
data class FeedItemUI(
|
||||
val feed: FeedSavedSearch,
|
||||
@ -114,40 +98,10 @@ fun FeedScreen(
|
||||
state.items.orEmpty(),
|
||||
key = { it.feed.id },
|
||||
) { item ->
|
||||
FeedItem(
|
||||
GlobalSearchResultItem(
|
||||
modifier = Modifier.animateItemPlacement(),
|
||||
item = item,
|
||||
getMangaState = { getMangaState(it, item.source) },
|
||||
onClickSavedSearch = onClickSavedSearch,
|
||||
onClickSource = onClickSource,
|
||||
onClickDelete = onClickDelete,
|
||||
onClickManga = onClickManga,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun FeedItem(
|
||||
modifier: Modifier,
|
||||
item: FeedItemUI,
|
||||
getMangaState: @Composable ((Manga) -> State<Manga>),
|
||||
onClickSavedSearch: (SavedSearch, CatalogueSource) -> Unit,
|
||||
onClickSource: (CatalogueSource) -> Unit,
|
||||
onClickDelete: (FeedSavedSearch) -> Unit,
|
||||
onClickManga: (Manga) -> Unit,
|
||||
) {
|
||||
Column(
|
||||
modifier then Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Row(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.combinedClickable(
|
||||
title = item.title,
|
||||
subtitle = item.subtitle,
|
||||
onLongClick = {
|
||||
onClickDelete(item.feed)
|
||||
},
|
||||
@ -158,44 +112,10 @@ fun FeedItem(
|
||||
onClickSource(item.source)
|
||||
}
|
||||
},
|
||||
),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Column(Modifier.padding(start = 16.dp)) {
|
||||
Text(
|
||||
text = item.title,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
)
|
||||
Text(
|
||||
text = item.subtitle,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontSize = 12.sp,
|
||||
color = LocalContentColor.current.copy(alpha = ContentAlpha.high),
|
||||
)
|
||||
}
|
||||
Icon(
|
||||
imageVector = Icons.Default.ArrowForward,
|
||||
contentDescription = stringResource(R.string.label_more),
|
||||
modifier = Modifier.padding(16.dp),
|
||||
)
|
||||
}
|
||||
when {
|
||||
item.results == null -> {
|
||||
CircularProgressIndicator()
|
||||
}
|
||||
item.results.isEmpty() -> {
|
||||
Text(stringResource(R.string.no_results_found), modifier = Modifier.padding(bottom = 16.dp))
|
||||
}
|
||||
else -> {
|
||||
LazyRow(
|
||||
Modifier.fillMaxWidth(),
|
||||
contentPadding = PaddingValues(horizontal = 12.dp),
|
||||
) {
|
||||
items(item.results) {
|
||||
val manga by getMangaState(it)
|
||||
FeedCardItem(
|
||||
manga = manga,
|
||||
FeedItem(
|
||||
item = item,
|
||||
getMangaState = { getMangaState(it, item.source) },
|
||||
onClickManga = onClickManga,
|
||||
)
|
||||
}
|
||||
@ -204,61 +124,32 @@ fun FeedItem(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun FeedCardItem(
|
||||
modifier: Modifier = Modifier,
|
||||
manga: Manga,
|
||||
fun FeedItem(
|
||||
item: FeedItemUI,
|
||||
getMangaState: @Composable ((Manga) -> State<Manga>),
|
||||
onClickManga: (Manga) -> Unit,
|
||||
) {
|
||||
Column(
|
||||
modifier
|
||||
.padding(vertical = 4.dp)
|
||||
.width(112.dp)
|
||||
.clip(RoundedCornerShape(4.dp))
|
||||
.clickable(onClick = { onClickManga(manga) })
|
||||
.padding(4.dp),
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(MangaCover.Book.ratio),
|
||||
) {
|
||||
MangaCover.Book(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.alpha(
|
||||
if (manga.favorite) 0.3f else 1.0f,
|
||||
),
|
||||
data = MangaCoverData(
|
||||
manga.id,
|
||||
manga.source,
|
||||
manga.favorite,
|
||||
manga.thumbnailUrl,
|
||||
manga.coverLastModified,
|
||||
),
|
||||
when {
|
||||
item.results == null -> {
|
||||
GlobalSearchLoadingResultItem()
|
||||
}
|
||||
item.results.isEmpty() -> {
|
||||
GlobalSearchErrorResultItem(message = stringResource(R.string.no_results_found))
|
||||
}
|
||||
else -> {
|
||||
GlobalSearchCardRow(
|
||||
titles = item.results,
|
||||
getManga = getMangaState,
|
||||
onClick = onClickManga,
|
||||
onLongClick = onClickManga,
|
||||
)
|
||||
BadgeGroup(
|
||||
modifier = Modifier
|
||||
.padding(4.dp)
|
||||
.align(Alignment.TopStart),
|
||||
) {
|
||||
if (manga.favorite) {
|
||||
Badge(text = stringResource(R.string.in_library))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text(
|
||||
modifier = Modifier.padding(4.dp),
|
||||
text = manga.title,
|
||||
fontSize = 12.sp,
|
||||
maxLines = 2,
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun FeedAddDialog(
|
||||
sources: List<CatalogueSource>,
|
||||
|
@ -1,33 +1,20 @@
|
||||
package eu.kanade.presentation.browse
|
||||
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowForward
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import eu.kanade.domain.manga.model.Manga
|
||||
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.LoadingScreen
|
||||
import eu.kanade.presentation.components.Scaffold
|
||||
@ -160,95 +147,62 @@ fun SourceFeedList(
|
||||
contentPadding = paddingValues + topSmallPaddingValues,
|
||||
) {
|
||||
items(
|
||||
items.orEmpty(),
|
||||
items,
|
||||
key = { it.id },
|
||||
) { item ->
|
||||
SourceFeedItem(
|
||||
GlobalSearchResultItem(
|
||||
modifier = Modifier.animateItemPlacement(),
|
||||
item = item,
|
||||
getMangaState = getMangaState,
|
||||
onClickTitle = when (item) {
|
||||
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) }
|
||||
}
|
||||
},
|
||||
onClickDelete = onClickDelete,
|
||||
) {
|
||||
SourceFeedItem(
|
||||
item = item,
|
||||
getMangaState = { getMangaState(it) },
|
||||
onClickManga = onClickManga,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SourceFeedItem(
|
||||
modifier: Modifier,
|
||||
item: SourceFeedUI,
|
||||
getMangaState: @Composable ((Manga) -> State<Manga>),
|
||||
onClickTitle: () -> Unit,
|
||||
onClickDelete: (FeedSavedSearch) -> Unit,
|
||||
onClickManga: (Manga) -> Unit,
|
||||
) {
|
||||
Column(
|
||||
modifier then Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Row(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.let {
|
||||
if (item is SourceFeedUI.SourceSavedSearch) {
|
||||
it.combinedClickable(
|
||||
onLongClick = {
|
||||
onClickDelete(item.feed)
|
||||
},
|
||||
onClick = onClickTitle,
|
||||
)
|
||||
} else {
|
||||
it.clickable(onClick = onClickTitle)
|
||||
}
|
||||
},
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Column(Modifier.padding(start = 16.dp)) {
|
||||
Text(
|
||||
text = item.title,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
)
|
||||
}
|
||||
Icon(
|
||||
imageVector = Icons.Default.ArrowForward,
|
||||
contentDescription = stringResource(R.string.label_more),
|
||||
modifier = Modifier.padding(16.dp),
|
||||
)
|
||||
}
|
||||
val results = item.results
|
||||
when {
|
||||
results == null -> {
|
||||
CircularProgressIndicator()
|
||||
GlobalSearchLoadingResultItem()
|
||||
}
|
||||
results.isEmpty() -> {
|
||||
Text(stringResource(R.string.no_results_found), modifier = Modifier.padding(bottom = 16.dp))
|
||||
GlobalSearchErrorResultItem(message = stringResource(R.string.no_results_found))
|
||||
}
|
||||
else -> {
|
||||
LazyRow(
|
||||
Modifier.fillMaxWidth(),
|
||||
contentPadding = PaddingValues(horizontal = 12.dp),
|
||||
) {
|
||||
items(results) {
|
||||
val manga by getMangaState(it)
|
||||
FeedCardItem(
|
||||
manga = manga,
|
||||
onClickManga = onClickManga,
|
||||
GlobalSearchCardRow(
|
||||
titles = item.results.orEmpty(),
|
||||
getManga = getMangaState,
|
||||
onClick = onClickManga,
|
||||
onLongClick = onClickManga,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SourceFeedToolbar(
|
||||
|
@ -1,6 +1,7 @@
|
||||
package eu.kanade.presentation.browse.components
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@ -29,12 +30,20 @@ import eu.kanade.tachiyomi.R
|
||||
|
||||
@Composable
|
||||
fun GlobalSearchResultItem(
|
||||
// SY -->
|
||||
modifier: Modifier = Modifier,
|
||||
// SY <--
|
||||
title: String,
|
||||
subtitle: String,
|
||||
// SY -->
|
||||
subtitle: String?,
|
||||
// SY <--
|
||||
onClick: () -> Unit,
|
||||
// SY -->
|
||||
onLongClick: (() -> Unit)? = null,
|
||||
// SY <--
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
Column {
|
||||
Column(modifier) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
@ -42,7 +51,15 @@ fun GlobalSearchResultItem(
|
||||
end = MaterialTheme.padding.tiny,
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = onClick),
|
||||
// SY -->
|
||||
.let {
|
||||
if (onLongClick == null) {
|
||||
it.clickable(onClick = onClick)
|
||||
} else {
|
||||
it.combinedClickable(onClick = onClick, onLongClick = onLongClick)
|
||||
}
|
||||
},
|
||||
// SY <--
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
@ -51,8 +68,10 @@ fun GlobalSearchResultItem(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
)
|
||||
if (subtitle != null) {
|
||||
Text(text = subtitle)
|
||||
}
|
||||
}
|
||||
IconButton(onClick = onClick) {
|
||||
Icon(imageVector = Icons.Outlined.ArrowForward, contentDescription = null)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user