Move library page EmptyScreens into list/grids

It does look awkward due to the lack of filled height within those list/grids though.

Fixes #8720
Fixes #8721
This commit is contained in:
arkon 2022-12-17 12:06:02 -05:00
parent 0e2bdb7863
commit 376bbeb724
6 changed files with 206 additions and 204 deletions

View File

@ -22,12 +22,9 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastFirstOrNull
import eu.kanade.presentation.theme.TachiyomiTheme import eu.kanade.presentation.theme.TachiyomiTheme
import eu.kanade.presentation.util.ThemePreviews import eu.kanade.presentation.util.ThemePreviews
import eu.kanade.presentation.util.secondaryItemAlpha import eu.kanade.presentation.util.secondaryItemAlpha
@ -54,68 +51,45 @@ fun EmptyScreen(
actions: List<EmptyScreenAction>? = null, actions: List<EmptyScreenAction>? = null,
) { ) {
val face = remember { getRandomErrorFace() } val face = remember { getRandomErrorFace() }
Layout( Column(
content = { modifier = modifier
Column( .fillMaxSize()
modifier = Modifier .padding(horizontal = 24.dp),
.layoutId("face") horizontalAlignment = Alignment.CenterHorizontally,
.padding(horizontal = 24.dp), verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally, ) {
) { Text(
Text( text = face,
text = face, modifier = Modifier.secondaryItemAlpha(),
modifier = Modifier.secondaryItemAlpha(), style = MaterialTheme.typography.displayMedium,
style = MaterialTheme.typography.displayMedium, )
)
Text( Text(
text = message, text = message,
modifier = Modifier.paddingFromBaseline(top = 24.dp).secondaryItemAlpha(), modifier = Modifier.paddingFromBaseline(top = 24.dp).secondaryItemAlpha(),
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
) )
}
if (!actions.isNullOrEmpty()) { if (!actions.isNullOrEmpty()) {
Row( Row(
modifier = Modifier modifier = Modifier
.layoutId("actions") .padding(
.padding( top = 24.dp,
top = 24.dp, start = 24.dp,
start = horizontalPadding, end = 24.dp,
end = horizontalPadding, ),
), horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
horizontalArrangement = Arrangement.spacedBy(space = 8.dp), ) {
) { actions.forEach {
actions.forEach { ActionButton(
ActionButton( modifier = Modifier.weight(1f),
modifier = Modifier.weight(1f), title = stringResource(it.stringResId),
title = stringResource(it.stringResId), icon = it.icon,
icon = it.icon, onClick = it.onClick,
onClick = it.onClick, )
)
}
} }
} }
},
modifier = modifier.fillMaxSize(),
) { measurables, constraints ->
val looseConstraints = constraints.copy(minWidth = 0, minHeight = 0)
val facePlaceable = measurables.fastFirstOrNull { it.layoutId == "face" }!!
.measure(looseConstraints)
val actionsPlaceable = measurables.fastFirstOrNull { it.layoutId == "actions" }
?.measure(looseConstraints)
layout(constraints.maxWidth, constraints.maxHeight) {
val faceY = (constraints.maxHeight - facePlaceable.height) / 2
facePlaceable.placeRelative(
x = (constraints.maxWidth - facePlaceable.width) / 2,
y = faceY,
)
actionsPlaceable?.placeRelative(
x = (constraints.maxWidth - actionsPlaceable.width) / 2,
y = faceY + facePlaceable.height,
)
} }
} }
} }
@ -187,8 +161,6 @@ data class EmptyScreenAction(
val onClick: () -> Unit, val onClick: () -> Unit,
) )
private val horizontalPadding = 24.dp
private val ERROR_FACES = listOf( private val ERROR_FACES = listOf(
"(・o・;)", "(・o・;)",
"Σ(ಠ_ಠ)", "Σ(ಠ_ಠ)",

View File

@ -2,6 +2,7 @@ package eu.kanade.presentation.library.components
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.grid.GridItemSpan
import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.lazy.grid.items
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -22,6 +23,7 @@ fun LibraryComfortableGrid(
onClickContinueReading: ((LibraryManga) -> Unit)?, onClickContinueReading: ((LibraryManga) -> Unit)?,
searchQuery: String?, searchQuery: String?,
onGlobalSearchClicked: () -> Unit, onGlobalSearchClicked: () -> Unit,
hasActiveFilters: Boolean,
) { ) {
LazyLibraryGrid( LazyLibraryGrid(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
@ -30,39 +32,48 @@ fun LibraryComfortableGrid(
) { ) {
globalSearchItem(searchQuery, onGlobalSearchClicked) globalSearchItem(searchQuery, onGlobalSearchClicked)
items( if (items.isEmpty()) {
items = items, item(
contentType = { "library_comfortable_grid_item" }, span = { GridItemSpan(maxLineSpan) },
) { libraryItem -> contentType = "library_comfortable_grid_empty",
val manga = libraryItem.libraryManga.manga ) {
MangaComfortableGridItem( LibraryPagerEmptyScreen(searchQuery, hasActiveFilters, contentPadding)
isSelected = selection.fastAny { it.id == libraryItem.libraryManga.id }, }
title = manga.title, } else {
coverData = MangaCover( items(
mangaId = manga.id, items = items,
sourceId = manga.source, contentType = { "library_comfortable_grid_item" },
isMangaFavorite = manga.favorite, ) { libraryItem ->
url = manga.thumbnailUrl, val manga = libraryItem.libraryManga.manga
lastModified = manga.coverLastModified, MangaComfortableGridItem(
), isSelected = selection.fastAny { it.id == libraryItem.libraryManga.id },
coverBadgeStart = { title = manga.title,
DownloadsBadge(count = libraryItem.downloadCount.toInt()) coverData = MangaCover(
UnreadBadge(count = libraryItem.unreadCount.toInt()) mangaId = manga.id,
}, sourceId = manga.source,
coverBadgeEnd = { isMangaFavorite = manga.favorite,
LanguageBadge( url = manga.thumbnailUrl,
isLocal = libraryItem.isLocal, lastModified = manga.coverLastModified,
sourceLanguage = libraryItem.sourceLanguage, ),
) coverBadgeStart = {
}, DownloadsBadge(count = libraryItem.downloadCount.toInt())
onLongClick = { onLongClick(libraryItem.libraryManga) }, UnreadBadge(count = libraryItem.unreadCount.toInt())
onClick = { onClick(libraryItem.libraryManga) }, },
onClickContinueReading = if (onClickContinueReading != null) { coverBadgeEnd = {
{ onClickContinueReading(libraryItem.libraryManga) } LanguageBadge(
} else { isLocal = libraryItem.isLocal,
null sourceLanguage = libraryItem.sourceLanguage,
}, )
) },
onLongClick = { onLongClick(libraryItem.libraryManga) },
onClick = { onClick(libraryItem.libraryManga) },
onClickContinueReading = if (onClickContinueReading != null) {
{ onClickContinueReading(libraryItem.libraryManga) }
} else {
null
},
)
}
} }
} }
} }

View File

@ -2,6 +2,7 @@ package eu.kanade.presentation.library.components
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.grid.GridItemSpan
import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.lazy.grid.items
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -23,6 +24,7 @@ fun LibraryCompactGrid(
onClickContinueReading: ((LibraryManga) -> Unit)?, onClickContinueReading: ((LibraryManga) -> Unit)?,
searchQuery: String?, searchQuery: String?,
onGlobalSearchClicked: () -> Unit, onGlobalSearchClicked: () -> Unit,
hasActiveFilters: Boolean,
) { ) {
LazyLibraryGrid( LazyLibraryGrid(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
@ -31,39 +33,48 @@ fun LibraryCompactGrid(
) { ) {
globalSearchItem(searchQuery, onGlobalSearchClicked) globalSearchItem(searchQuery, onGlobalSearchClicked)
items( if (items.isEmpty()) {
items = items, item(
contentType = { "library_compact_grid_item" }, span = { GridItemSpan(maxLineSpan) },
) { libraryItem -> contentType = "library_compact_grid_empty",
val manga = libraryItem.libraryManga.manga ) {
MangaCompactGridItem( LibraryPagerEmptyScreen(searchQuery, hasActiveFilters, contentPadding)
isSelected = selection.fastAny { it.id == libraryItem.libraryManga.id }, }
title = manga.title.takeIf { showTitle }, } else {
coverData = MangaCover( items(
mangaId = manga.id, items = items,
sourceId = manga.source, contentType = { "library_compact_grid_item" },
isMangaFavorite = manga.favorite, ) { libraryItem ->
url = manga.thumbnailUrl, val manga = libraryItem.libraryManga.manga
lastModified = manga.coverLastModified, MangaCompactGridItem(
), isSelected = selection.fastAny { it.id == libraryItem.libraryManga.id },
coverBadgeStart = { title = manga.title.takeIf { showTitle },
DownloadsBadge(count = libraryItem.downloadCount.toInt()) coverData = MangaCover(
UnreadBadge(count = libraryItem.unreadCount.toInt()) mangaId = manga.id,
}, sourceId = manga.source,
coverBadgeEnd = { isMangaFavorite = manga.favorite,
LanguageBadge( url = manga.thumbnailUrl,
isLocal = libraryItem.isLocal, lastModified = manga.coverLastModified,
sourceLanguage = libraryItem.sourceLanguage, ),
) coverBadgeStart = {
}, DownloadsBadge(count = libraryItem.downloadCount.toInt())
onLongClick = { onLongClick(libraryItem.libraryManga) }, UnreadBadge(count = libraryItem.unreadCount.toInt())
onClick = { onClick(libraryItem.libraryManga) }, },
onClickContinueReading = if (onClickContinueReading != null) { coverBadgeEnd = {
{ onClickContinueReading(libraryItem.libraryManga) } LanguageBadge(
} else { isLocal = libraryItem.isLocal,
null sourceLanguage = libraryItem.sourceLanguage,
}, )
) },
onLongClick = { onLongClick(libraryItem.libraryManga) },
onClick = { onClick(libraryItem.libraryManga) },
onClickContinueReading = if (onClickContinueReading != null) {
{ onClickContinueReading(libraryItem.libraryManga) }
} else {
null
},
)
}
} }
} }
} }

View File

@ -30,6 +30,7 @@ fun LibraryList(
onClickContinueReading: ((LibraryManga) -> Unit)?, onClickContinueReading: ((LibraryManga) -> Unit)?,
searchQuery: String?, searchQuery: String?,
onGlobalSearchClicked: () -> Unit, onGlobalSearchClicked: () -> Unit,
hasActiveFilters: Boolean,
) { ) {
FastScrollLazyColumn( FastScrollLazyColumn(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
@ -49,37 +50,45 @@ fun LibraryList(
} }
} }
items( if (items.isEmpty()) {
items = items, item(
contentType = { "library_list_item" }, contentType = "library_list_empty",
) { libraryItem -> ) {
val manga = libraryItem.libraryManga.manga LibraryPagerEmptyScreen(searchQuery, hasActiveFilters, contentPadding)
MangaListItem( }
isSelected = selection.fastAny { it.id == libraryItem.libraryManga.id }, } else {
title = manga.title, items(
coverData = MangaCover( items = items,
mangaId = manga.id, contentType = { "library_list_item" },
sourceId = manga.source, ) { libraryItem ->
isMangaFavorite = manga.favorite, val manga = libraryItem.libraryManga.manga
url = manga.thumbnailUrl, MangaListItem(
lastModified = manga.coverLastModified, isSelected = selection.fastAny { it.id == libraryItem.libraryManga.id },
), title = manga.title,
badge = { coverData = MangaCover(
DownloadsBadge(count = libraryItem.downloadCount.toInt()) mangaId = manga.id,
UnreadBadge(count = libraryItem.unreadCount.toInt()) sourceId = manga.source,
LanguageBadge( isMangaFavorite = manga.favorite,
isLocal = libraryItem.isLocal, url = manga.thumbnailUrl,
sourceLanguage = libraryItem.sourceLanguage, lastModified = manga.coverLastModified,
) ),
}, badge = {
onLongClick = { onLongClick(libraryItem.libraryManga) }, DownloadsBadge(count = libraryItem.downloadCount.toInt())
onClick = { onClick(libraryItem.libraryManga) }, UnreadBadge(count = libraryItem.unreadCount.toInt())
onClickContinueReading = if (onClickContinueReading != null) { LanguageBadge(
{ onClickContinueReading(libraryItem.libraryManga) } isLocal = libraryItem.isLocal,
} else { sourceLanguage = libraryItem.sourceLanguage,
null )
}, },
) onLongClick = { onLongClick(libraryItem.libraryManga) },
onClick = { onClick(libraryItem.libraryManga) },
onClickContinueReading = if (onClickContinueReading != null) {
{ onClickContinueReading(libraryItem.libraryManga) }
} else {
null
},
)
}
} }
} }
} }

View File

@ -48,11 +48,6 @@ fun LibraryPager(
} }
val library = getLibraryForPage(page) val library = getLibraryForPage(page)
if (library.isEmpty()) {
LibraryPagerEmptyScreen(searchQuery, hasActiveFilters, contentPadding)
return@HorizontalPager
}
val displayMode = getDisplayModeForPage(page) val displayMode = getDisplayModeForPage(page)
val columns by if (displayMode != LibraryDisplayMode.List) { val columns by if (displayMode != LibraryDisplayMode.List) {
val configuration = LocalConfiguration.current val configuration = LocalConfiguration.current
@ -74,6 +69,7 @@ fun LibraryPager(
onClickContinueReading = onClickContinueReading, onClickContinueReading = onClickContinueReading,
searchQuery = searchQuery, searchQuery = searchQuery,
onGlobalSearchClicked = onGlobalSearchClicked, onGlobalSearchClicked = onGlobalSearchClicked,
hasActiveFilters = hasActiveFilters,
) )
} }
LibraryDisplayMode.CompactGrid, LibraryDisplayMode.CoverOnlyGrid -> { LibraryDisplayMode.CompactGrid, LibraryDisplayMode.CoverOnlyGrid -> {
@ -88,6 +84,7 @@ fun LibraryPager(
onClickContinueReading = onClickContinueReading, onClickContinueReading = onClickContinueReading,
searchQuery = searchQuery, searchQuery = searchQuery,
onGlobalSearchClicked = onGlobalSearchClicked, onGlobalSearchClicked = onGlobalSearchClicked,
hasActiveFilters = hasActiveFilters,
) )
} }
LibraryDisplayMode.ComfortableGrid -> { LibraryDisplayMode.ComfortableGrid -> {
@ -101,6 +98,7 @@ fun LibraryPager(
onClickContinueReading = onClickContinueReading, onClickContinueReading = onClickContinueReading,
searchQuery = searchQuery, searchQuery = searchQuery,
onGlobalSearchClicked = onGlobalSearchClicked, onGlobalSearchClicked = onGlobalSearchClicked,
hasActiveFilters = hasActiveFilters,
) )
} }
} }
@ -108,7 +106,7 @@ fun LibraryPager(
} }
@Composable @Composable
private fun LibraryPagerEmptyScreen( internal fun LibraryPagerEmptyScreen(
searchQuery: String?, searchQuery: String?,
hasActiveFilters: Boolean, hasActiveFilters: Boolean,
contentPadding: PaddingValues, contentPadding: PaddingValues,
@ -119,6 +117,7 @@ private fun LibraryPagerEmptyScreen(
else -> R.string.information_no_manga_category else -> R.string.information_no_manga_category
} }
// TODO: vertically center this better
EmptyScreen( EmptyScreen(
textResource = msg, textResource = msg,
modifier = Modifier.padding(contentPadding), modifier = Modifier.padding(contentPadding),

View File

@ -212,43 +212,43 @@ private fun SearchResult(
} }
Crossfade(targetState = result) { Crossfade(targetState = result) {
LazyColumn( when {
modifier = modifier.fillMaxSize(), it == null -> {}
state = listState, it.isEmpty() -> {
contentPadding = contentPadding, EmptyScreen(stringResource(R.string.no_results_found))
horizontalAlignment = Alignment.CenterHorizontally, }
) { else -> {
when { LazyColumn(
it == null -> { modifier = modifier.fillMaxSize(),
/* Don't show anything just yet */ state = listState,
} contentPadding = contentPadding,
// No result horizontalAlignment = Alignment.CenterHorizontally,
it.isEmpty() -> item { EmptyScreen(stringResource(R.string.no_results_found)) } ) {
// Show result list items(
else -> items( items = it,
items = it, key = { i -> i.hashCode() },
key = { i -> i.hashCode() }, ) { item ->
) { item -> Column(
Column( modifier = Modifier
modifier = Modifier .fillMaxWidth()
.fillMaxWidth() .clickable { onItemClick(item) }
.clickable { onItemClick(item) } .padding(horizontal = 24.dp, vertical = 14.dp),
.padding(horizontal = 24.dp, vertical = 14.dp), ) {
) { Text(
Text( text = item.title,
text = item.title, overflow = TextOverflow.Ellipsis,
overflow = TextOverflow.Ellipsis, maxLines = 1,
maxLines = 1, fontWeight = FontWeight.Normal,
fontWeight = FontWeight.Normal, style = MaterialTheme.typography.titleMedium,
style = MaterialTheme.typography.titleMedium, )
) Text(
Text( text = item.breadcrumbs,
text = item.breadcrumbs, modifier = Modifier.paddingFromBaseline(top = 16.dp),
modifier = Modifier.paddingFromBaseline(top = 16.dp), maxLines = 1,
maxLines = 1, color = MaterialTheme.colorScheme.onSurfaceVariant,
color = MaterialTheme.colorScheme.onSurfaceVariant, style = MaterialTheme.typography.bodySmall,
style = MaterialTheme.typography.bodySmall, )
) }
} }
} }
} }