From 2b1809b2b79dad3b0358b63712531a23cec6db04 Mon Sep 17 00:00:00 2001 From: Jobobby04 Date: Tue, 7 Feb 2023 13:04:41 -0500 Subject: [PATCH] Use global search UI for Feed --- .../kanade/presentation/browse/FeedScreen.kt | 183 ++++-------------- .../presentation/browse/SourceFeedScreen.kt | 120 ++++-------- .../components/GlobalSearchResultItems.kt | 27 ++- 3 files changed, 97 insertions(+), 233 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/browse/FeedScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/FeedScreen.kt index d75c46c2f..0a78213ca 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/FeedScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/FeedScreen.kt @@ -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), - 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, - ) } } diff --git a/app/src/main/java/eu/kanade/presentation/browse/SourceFeedScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/SourceFeedScreen.kt index 0e499d6e0..346aeb703 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/SourceFeedScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/SourceFeedScreen.kt @@ -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), - 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, + ) } } } diff --git a/app/src/main/java/eu/kanade/presentation/browse/components/GlobalSearchResultItems.kt b/app/src/main/java/eu/kanade/presentation/browse/components/GlobalSearchResultItems.kt index f1b3c9a53..ddd4ed3ac 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/components/GlobalSearchResultItems.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/components/GlobalSearchResultItems.kt @@ -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)