Use global search UI for Feed

This commit is contained in:
Jobobby04 2023-02-07 13:04:41 -05:00
parent 9d0560f48b
commit 2b1809b2b7
3 changed files with 97 additions and 233 deletions

View File

@ -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,15 +98,27 @@ 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,
)
title = item.title,
subtitle = item.subtitle,
onLongClick = {
onClickDelete(item.feed)
},
onClick = {
if (item.savedSearch != null && item.source != null) {
onClickSavedSearch(item.savedSearch, item.source)
} else if (item.source != null) {
onClickSource(item.source)
}
},
) {
FeedItem(
item = item,
getMangaState = { getMangaState(it, item.source) },
onClickManga = onClickManga,
)
}
}
}
}
@ -132,130 +128,25 @@ fun FeedScreen(
@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(
onLongClick = {
onClickDelete(item.feed)
},
onClick = {
if (item.savedSearch != null && item.source != null) {
onClickSavedSearch(item.savedSearch, item.source)
} else if (item.source != null) {
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 -> {
GlobalSearchLoadingResultItem()
}
item.results.isEmpty() -> {
GlobalSearchErrorResultItem(message = stringResource(R.string.no_results_found))
}
else -> {
GlobalSearchCardRow(
titles = item.results,
getManga = getMangaState,
onClick = onClickManga,
onLongClick = onClickManga,
)
}
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,
onClickManga = onClickManga,
)
}
}
}
}
}
}
@Composable
fun FeedCardItem(
modifier: Modifier = Modifier,
manga: 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,
),
)
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,
)
}
}

View File

@ -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,92 +147,59 @@ 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,
onClickManga = onClickManga,
)
) {
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 -> {
GlobalSearchLoadingResultItem()
}
val results = item.results
when {
results == null -> {
CircularProgressIndicator()
}
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(results) {
val manga by getMangaState(it)
FeedCardItem(
manga = manga,
onClickManga = onClickManga,
)
}
}
}
results.isEmpty() -> {
GlobalSearchErrorResultItem(message = stringResource(R.string.no_results_found))
}
else -> {
GlobalSearchCardRow(
titles = item.results.orEmpty(),
getManga = getMangaState,
onClick = onClickManga,
onLongClick = onClickManga,
)
}
}
}

View File

@ -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,7 +68,9 @@ fun GlobalSearchResultItem(
text = title,
style = MaterialTheme.typography.titleMedium,
)
Text(text = subtitle)
if (subtitle != null) {
Text(text = subtitle)
}
}
IconButton(onClick = onClick) {
Icon(imageVector = Icons.Outlined.ArrowForward, contentDescription = null)