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:
parent
0e2bdb7863
commit
376bbeb724
@ -22,12 +22,9 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
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.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.fastFirstOrNull
|
||||
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||
import eu.kanade.presentation.util.ThemePreviews
|
||||
import eu.kanade.presentation.util.secondaryItemAlpha
|
||||
@ -54,68 +51,45 @@ fun EmptyScreen(
|
||||
actions: List<EmptyScreenAction>? = null,
|
||||
) {
|
||||
val face = remember { getRandomErrorFace() }
|
||||
Layout(
|
||||
content = {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.layoutId("face")
|
||||
.padding(horizontal = 24.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
text = face,
|
||||
modifier = Modifier.secondaryItemAlpha(),
|
||||
style = MaterialTheme.typography.displayMedium,
|
||||
)
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = 24.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
) {
|
||||
Text(
|
||||
text = face,
|
||||
modifier = Modifier.secondaryItemAlpha(),
|
||||
style = MaterialTheme.typography.displayMedium,
|
||||
)
|
||||
|
||||
Text(
|
||||
text = message,
|
||||
modifier = Modifier.paddingFromBaseline(top = 24.dp).secondaryItemAlpha(),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
if (!actions.isNullOrEmpty()) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.layoutId("actions")
|
||||
.padding(
|
||||
top = 24.dp,
|
||||
start = horizontalPadding,
|
||||
end = horizontalPadding,
|
||||
),
|
||||
horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
|
||||
) {
|
||||
actions.forEach {
|
||||
ActionButton(
|
||||
modifier = Modifier.weight(1f),
|
||||
title = stringResource(it.stringResId),
|
||||
icon = it.icon,
|
||||
onClick = it.onClick,
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = message,
|
||||
modifier = Modifier.paddingFromBaseline(top = 24.dp).secondaryItemAlpha(),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
|
||||
if (!actions.isNullOrEmpty()) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
top = 24.dp,
|
||||
start = 24.dp,
|
||||
end = 24.dp,
|
||||
),
|
||||
horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
|
||||
) {
|
||||
actions.forEach {
|
||||
ActionButton(
|
||||
modifier = Modifier.weight(1f),
|
||||
title = stringResource(it.stringResId),
|
||||
icon = it.icon,
|
||||
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,
|
||||
)
|
||||
|
||||
private val horizontalPadding = 24.dp
|
||||
|
||||
private val ERROR_FACES = listOf(
|
||||
"(・o・;)",
|
||||
"Σ(ಠ_ಠ)",
|
||||
|
@ -2,6 +2,7 @@ package eu.kanade.presentation.library.components
|
||||
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.lazy.grid.GridItemSpan
|
||||
import androidx.compose.foundation.lazy.grid.items
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
@ -22,6 +23,7 @@ fun LibraryComfortableGrid(
|
||||
onClickContinueReading: ((LibraryManga) -> Unit)?,
|
||||
searchQuery: String?,
|
||||
onGlobalSearchClicked: () -> Unit,
|
||||
hasActiveFilters: Boolean,
|
||||
) {
|
||||
LazyLibraryGrid(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
@ -30,39 +32,48 @@ fun LibraryComfortableGrid(
|
||||
) {
|
||||
globalSearchItem(searchQuery, onGlobalSearchClicked)
|
||||
|
||||
items(
|
||||
items = items,
|
||||
contentType = { "library_comfortable_grid_item" },
|
||||
) { libraryItem ->
|
||||
val manga = libraryItem.libraryManga.manga
|
||||
MangaComfortableGridItem(
|
||||
isSelected = selection.fastAny { it.id == libraryItem.libraryManga.id },
|
||||
title = manga.title,
|
||||
coverData = MangaCover(
|
||||
mangaId = manga.id,
|
||||
sourceId = manga.source,
|
||||
isMangaFavorite = manga.favorite,
|
||||
url = manga.thumbnailUrl,
|
||||
lastModified = manga.coverLastModified,
|
||||
),
|
||||
coverBadgeStart = {
|
||||
DownloadsBadge(count = libraryItem.downloadCount.toInt())
|
||||
UnreadBadge(count = libraryItem.unreadCount.toInt())
|
||||
},
|
||||
coverBadgeEnd = {
|
||||
LanguageBadge(
|
||||
isLocal = libraryItem.isLocal,
|
||||
sourceLanguage = libraryItem.sourceLanguage,
|
||||
)
|
||||
},
|
||||
onLongClick = { onLongClick(libraryItem.libraryManga) },
|
||||
onClick = { onClick(libraryItem.libraryManga) },
|
||||
onClickContinueReading = if (onClickContinueReading != null) {
|
||||
{ onClickContinueReading(libraryItem.libraryManga) }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
)
|
||||
if (items.isEmpty()) {
|
||||
item(
|
||||
span = { GridItemSpan(maxLineSpan) },
|
||||
contentType = "library_comfortable_grid_empty",
|
||||
) {
|
||||
LibraryPagerEmptyScreen(searchQuery, hasActiveFilters, contentPadding)
|
||||
}
|
||||
} else {
|
||||
items(
|
||||
items = items,
|
||||
contentType = { "library_comfortable_grid_item" },
|
||||
) { libraryItem ->
|
||||
val manga = libraryItem.libraryManga.manga
|
||||
MangaComfortableGridItem(
|
||||
isSelected = selection.fastAny { it.id == libraryItem.libraryManga.id },
|
||||
title = manga.title,
|
||||
coverData = MangaCover(
|
||||
mangaId = manga.id,
|
||||
sourceId = manga.source,
|
||||
isMangaFavorite = manga.favorite,
|
||||
url = manga.thumbnailUrl,
|
||||
lastModified = manga.coverLastModified,
|
||||
),
|
||||
coverBadgeStart = {
|
||||
DownloadsBadge(count = libraryItem.downloadCount.toInt())
|
||||
UnreadBadge(count = libraryItem.unreadCount.toInt())
|
||||
},
|
||||
coverBadgeEnd = {
|
||||
LanguageBadge(
|
||||
isLocal = libraryItem.isLocal,
|
||||
sourceLanguage = libraryItem.sourceLanguage,
|
||||
)
|
||||
},
|
||||
onLongClick = { onLongClick(libraryItem.libraryManga) },
|
||||
onClick = { onClick(libraryItem.libraryManga) },
|
||||
onClickContinueReading = if (onClickContinueReading != null) {
|
||||
{ onClickContinueReading(libraryItem.libraryManga) }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package eu.kanade.presentation.library.components
|
||||
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.lazy.grid.GridItemSpan
|
||||
import androidx.compose.foundation.lazy.grid.items
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
@ -23,6 +24,7 @@ fun LibraryCompactGrid(
|
||||
onClickContinueReading: ((LibraryManga) -> Unit)?,
|
||||
searchQuery: String?,
|
||||
onGlobalSearchClicked: () -> Unit,
|
||||
hasActiveFilters: Boolean,
|
||||
) {
|
||||
LazyLibraryGrid(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
@ -31,39 +33,48 @@ fun LibraryCompactGrid(
|
||||
) {
|
||||
globalSearchItem(searchQuery, onGlobalSearchClicked)
|
||||
|
||||
items(
|
||||
items = items,
|
||||
contentType = { "library_compact_grid_item" },
|
||||
) { libraryItem ->
|
||||
val manga = libraryItem.libraryManga.manga
|
||||
MangaCompactGridItem(
|
||||
isSelected = selection.fastAny { it.id == libraryItem.libraryManga.id },
|
||||
title = manga.title.takeIf { showTitle },
|
||||
coverData = MangaCover(
|
||||
mangaId = manga.id,
|
||||
sourceId = manga.source,
|
||||
isMangaFavorite = manga.favorite,
|
||||
url = manga.thumbnailUrl,
|
||||
lastModified = manga.coverLastModified,
|
||||
),
|
||||
coverBadgeStart = {
|
||||
DownloadsBadge(count = libraryItem.downloadCount.toInt())
|
||||
UnreadBadge(count = libraryItem.unreadCount.toInt())
|
||||
},
|
||||
coverBadgeEnd = {
|
||||
LanguageBadge(
|
||||
isLocal = libraryItem.isLocal,
|
||||
sourceLanguage = libraryItem.sourceLanguage,
|
||||
)
|
||||
},
|
||||
onLongClick = { onLongClick(libraryItem.libraryManga) },
|
||||
onClick = { onClick(libraryItem.libraryManga) },
|
||||
onClickContinueReading = if (onClickContinueReading != null) {
|
||||
{ onClickContinueReading(libraryItem.libraryManga) }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
)
|
||||
if (items.isEmpty()) {
|
||||
item(
|
||||
span = { GridItemSpan(maxLineSpan) },
|
||||
contentType = "library_compact_grid_empty",
|
||||
) {
|
||||
LibraryPagerEmptyScreen(searchQuery, hasActiveFilters, contentPadding)
|
||||
}
|
||||
} else {
|
||||
items(
|
||||
items = items,
|
||||
contentType = { "library_compact_grid_item" },
|
||||
) { libraryItem ->
|
||||
val manga = libraryItem.libraryManga.manga
|
||||
MangaCompactGridItem(
|
||||
isSelected = selection.fastAny { it.id == libraryItem.libraryManga.id },
|
||||
title = manga.title.takeIf { showTitle },
|
||||
coverData = MangaCover(
|
||||
mangaId = manga.id,
|
||||
sourceId = manga.source,
|
||||
isMangaFavorite = manga.favorite,
|
||||
url = manga.thumbnailUrl,
|
||||
lastModified = manga.coverLastModified,
|
||||
),
|
||||
coverBadgeStart = {
|
||||
DownloadsBadge(count = libraryItem.downloadCount.toInt())
|
||||
UnreadBadge(count = libraryItem.unreadCount.toInt())
|
||||
},
|
||||
coverBadgeEnd = {
|
||||
LanguageBadge(
|
||||
isLocal = libraryItem.isLocal,
|
||||
sourceLanguage = libraryItem.sourceLanguage,
|
||||
)
|
||||
},
|
||||
onLongClick = { onLongClick(libraryItem.libraryManga) },
|
||||
onClick = { onClick(libraryItem.libraryManga) },
|
||||
onClickContinueReading = if (onClickContinueReading != null) {
|
||||
{ onClickContinueReading(libraryItem.libraryManga) }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ fun LibraryList(
|
||||
onClickContinueReading: ((LibraryManga) -> Unit)?,
|
||||
searchQuery: String?,
|
||||
onGlobalSearchClicked: () -> Unit,
|
||||
hasActiveFilters: Boolean,
|
||||
) {
|
||||
FastScrollLazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
@ -49,37 +50,45 @@ fun LibraryList(
|
||||
}
|
||||
}
|
||||
|
||||
items(
|
||||
items = items,
|
||||
contentType = { "library_list_item" },
|
||||
) { libraryItem ->
|
||||
val manga = libraryItem.libraryManga.manga
|
||||
MangaListItem(
|
||||
isSelected = selection.fastAny { it.id == libraryItem.libraryManga.id },
|
||||
title = manga.title,
|
||||
coverData = MangaCover(
|
||||
mangaId = manga.id,
|
||||
sourceId = manga.source,
|
||||
isMangaFavorite = manga.favorite,
|
||||
url = manga.thumbnailUrl,
|
||||
lastModified = manga.coverLastModified,
|
||||
),
|
||||
badge = {
|
||||
DownloadsBadge(count = libraryItem.downloadCount.toInt())
|
||||
UnreadBadge(count = libraryItem.unreadCount.toInt())
|
||||
LanguageBadge(
|
||||
isLocal = libraryItem.isLocal,
|
||||
sourceLanguage = libraryItem.sourceLanguage,
|
||||
)
|
||||
},
|
||||
onLongClick = { onLongClick(libraryItem.libraryManga) },
|
||||
onClick = { onClick(libraryItem.libraryManga) },
|
||||
onClickContinueReading = if (onClickContinueReading != null) {
|
||||
{ onClickContinueReading(libraryItem.libraryManga) }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
)
|
||||
if (items.isEmpty()) {
|
||||
item(
|
||||
contentType = "library_list_empty",
|
||||
) {
|
||||
LibraryPagerEmptyScreen(searchQuery, hasActiveFilters, contentPadding)
|
||||
}
|
||||
} else {
|
||||
items(
|
||||
items = items,
|
||||
contentType = { "library_list_item" },
|
||||
) { libraryItem ->
|
||||
val manga = libraryItem.libraryManga.manga
|
||||
MangaListItem(
|
||||
isSelected = selection.fastAny { it.id == libraryItem.libraryManga.id },
|
||||
title = manga.title,
|
||||
coverData = MangaCover(
|
||||
mangaId = manga.id,
|
||||
sourceId = manga.source,
|
||||
isMangaFavorite = manga.favorite,
|
||||
url = manga.thumbnailUrl,
|
||||
lastModified = manga.coverLastModified,
|
||||
),
|
||||
badge = {
|
||||
DownloadsBadge(count = libraryItem.downloadCount.toInt())
|
||||
UnreadBadge(count = libraryItem.unreadCount.toInt())
|
||||
LanguageBadge(
|
||||
isLocal = libraryItem.isLocal,
|
||||
sourceLanguage = libraryItem.sourceLanguage,
|
||||
)
|
||||
},
|
||||
onLongClick = { onLongClick(libraryItem.libraryManga) },
|
||||
onClick = { onClick(libraryItem.libraryManga) },
|
||||
onClickContinueReading = if (onClickContinueReading != null) {
|
||||
{ onClickContinueReading(libraryItem.libraryManga) }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -48,11 +48,6 @@ fun LibraryPager(
|
||||
}
|
||||
val library = getLibraryForPage(page)
|
||||
|
||||
if (library.isEmpty()) {
|
||||
LibraryPagerEmptyScreen(searchQuery, hasActiveFilters, contentPadding)
|
||||
return@HorizontalPager
|
||||
}
|
||||
|
||||
val displayMode = getDisplayModeForPage(page)
|
||||
val columns by if (displayMode != LibraryDisplayMode.List) {
|
||||
val configuration = LocalConfiguration.current
|
||||
@ -74,6 +69,7 @@ fun LibraryPager(
|
||||
onClickContinueReading = onClickContinueReading,
|
||||
searchQuery = searchQuery,
|
||||
onGlobalSearchClicked = onGlobalSearchClicked,
|
||||
hasActiveFilters = hasActiveFilters,
|
||||
)
|
||||
}
|
||||
LibraryDisplayMode.CompactGrid, LibraryDisplayMode.CoverOnlyGrid -> {
|
||||
@ -88,6 +84,7 @@ fun LibraryPager(
|
||||
onClickContinueReading = onClickContinueReading,
|
||||
searchQuery = searchQuery,
|
||||
onGlobalSearchClicked = onGlobalSearchClicked,
|
||||
hasActiveFilters = hasActiveFilters,
|
||||
)
|
||||
}
|
||||
LibraryDisplayMode.ComfortableGrid -> {
|
||||
@ -101,6 +98,7 @@ fun LibraryPager(
|
||||
onClickContinueReading = onClickContinueReading,
|
||||
searchQuery = searchQuery,
|
||||
onGlobalSearchClicked = onGlobalSearchClicked,
|
||||
hasActiveFilters = hasActiveFilters,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -108,7 +106,7 @@ fun LibraryPager(
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LibraryPagerEmptyScreen(
|
||||
internal fun LibraryPagerEmptyScreen(
|
||||
searchQuery: String?,
|
||||
hasActiveFilters: Boolean,
|
||||
contentPadding: PaddingValues,
|
||||
@ -119,6 +117,7 @@ private fun LibraryPagerEmptyScreen(
|
||||
else -> R.string.information_no_manga_category
|
||||
}
|
||||
|
||||
// TODO: vertically center this better
|
||||
EmptyScreen(
|
||||
textResource = msg,
|
||||
modifier = Modifier.padding(contentPadding),
|
||||
|
@ -212,43 +212,43 @@ private fun SearchResult(
|
||||
}
|
||||
|
||||
Crossfade(targetState = result) {
|
||||
LazyColumn(
|
||||
modifier = modifier.fillMaxSize(),
|
||||
state = listState,
|
||||
contentPadding = contentPadding,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
when {
|
||||
it == null -> {
|
||||
/* Don't show anything just yet */
|
||||
}
|
||||
// No result
|
||||
it.isEmpty() -> item { EmptyScreen(stringResource(R.string.no_results_found)) }
|
||||
// Show result list
|
||||
else -> items(
|
||||
items = it,
|
||||
key = { i -> i.hashCode() },
|
||||
) { item ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { onItemClick(item) }
|
||||
.padding(horizontal = 24.dp, vertical = 14.dp),
|
||||
) {
|
||||
Text(
|
||||
text = item.title,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
fontWeight = FontWeight.Normal,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
)
|
||||
Text(
|
||||
text = item.breadcrumbs,
|
||||
modifier = Modifier.paddingFromBaseline(top = 16.dp),
|
||||
maxLines = 1,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
)
|
||||
when {
|
||||
it == null -> {}
|
||||
it.isEmpty() -> {
|
||||
EmptyScreen(stringResource(R.string.no_results_found))
|
||||
}
|
||||
else -> {
|
||||
LazyColumn(
|
||||
modifier = modifier.fillMaxSize(),
|
||||
state = listState,
|
||||
contentPadding = contentPadding,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
items(
|
||||
items = it,
|
||||
key = { i -> i.hashCode() },
|
||||
) { item ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { onItemClick(item) }
|
||||
.padding(horizontal = 24.dp, vertical = 14.dp),
|
||||
) {
|
||||
Text(
|
||||
text = item.title,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
fontWeight = FontWeight.Normal,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
)
|
||||
Text(
|
||||
text = item.breadcrumbs,
|
||||
modifier = Modifier.paddingFromBaseline(top = 16.dp),
|
||||
maxLines = 1,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user