Refactor the thumbnails.

This commit is contained in:
Thomas Andres Gomez 2023-04-06 13:08:52 +02:00
parent f6496e485d
commit 35d27534df
15 changed files with 356 additions and 481 deletions

View file

@ -2,6 +2,7 @@ package com.pixelized.biblib.ui.screen.home
import android.app.Application
import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
@ -13,14 +14,11 @@ import com.pixelized.biblib.model.search.SortType
import com.pixelized.biblib.model.search.SortValue
import com.pixelized.biblib.repository.book.IBookRepository
import com.pixelized.biblib.repository.search.ISearchRepository
import com.pixelized.biblib.ui.screen.home.common.item.LargeBookThumbnailUio
import com.pixelized.biblib.ui.screen.home.common.item.MicroBookThumbnailUio
import com.pixelized.biblib.ui.screen.home.common.item.SmallBookThumbnailUio
import com.pixelized.biblib.ui.screen.home.common.item.BookThumbnailUio
import com.pixelized.biblib.ui.screen.home.filter.FilterChipUio
import com.pixelized.biblib.ui.screen.home.options.OptionsUio
import com.pixelized.biblib.utils.extention.stringResource
import com.pixelized.biblib.utils.extention.toLargeBookThumbnailUio
import com.pixelized.biblib.utils.extention.toMicroThumbnailUio
import com.pixelized.biblib.utils.extention.toSmallThumbnailUio
import com.pixelized.biblib.utils.extention.toThumbnailUio
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
@ -42,10 +40,19 @@ class BookSearchViewModel @Inject constructor(
private val _updating = mutableStateOf(false)
val updating: State<Boolean> = _updating
private val _display = mutableStateOf(HomeDisplay.MICRO)
val display: State<HomeDisplay> = _display
val options = derivedStateOf {
when(display.value) {
HomeDisplay.MICRO -> OptionsUio(micro = true, small = false, large = false)
HomeDisplay.SMALL -> OptionsUio(micro = false, small = true, large = false)
HomeDisplay.GRID -> OptionsUio(micro = false, small = false, large = true)
}
}
private var searchSource: BookSearchSource? = null
val microPaging: Flow<PagingData<MicroBookThumbnailUio>>
val smallPaging: Flow<PagingData<SmallBookThumbnailUio>>
val largePaging: Flow<PagingData<LargeBookThumbnailUio>>
val paging: Flow<PagingData<BookThumbnailUio>>
private val _search = mutableStateOf<String?>(null)
val search: State<String?> = _search
@ -76,14 +83,8 @@ class BookSearchViewModel @Inject constructor(
pagingSourceFactory = ::buildBookSource,
).flow
// keep transaction updated with the pager.
microPaging = searchFlow
.map { pagingData -> pagingData.map { it.toMicroThumbnailUio() } }
.cachedIn(viewModelScope + Dispatchers.IO)
smallPaging = searchFlow
.map { pagingData -> pagingData.map { it.toSmallThumbnailUio() } }
.cachedIn(viewModelScope + Dispatchers.IO)
largePaging = searchFlow
.map { pagingData -> pagingData.map { it.toLargeBookThumbnailUio() } }
paging = searchFlow
.map { pagingData -> pagingData.map { it.toThumbnailUio() } }
.cachedIn(viewModelScope + Dispatchers.IO)
}
@ -104,6 +105,10 @@ class BookSearchViewModel @Inject constructor(
}
}
fun onDisplay(type: HomeDisplay) {
_display.value = type
}
fun source(clean: Boolean = false, block: SortAndFilterScope.() -> Unit) {
val scope = if (clean) {
SortAndFilterScope(

View file

@ -2,11 +2,8 @@ package com.pixelized.biblib.ui.screen.home
import android.content.res.Configuration
import androidx.annotation.StringRes
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.*
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.*
import androidx.compose.foundation.lazy.grid.GridCells
@ -48,26 +45,29 @@ import com.pixelized.biblib.ui.composable.scaffold.*
import com.pixelized.biblib.ui.navigation.LocalScreenNavHostController
import com.pixelized.biblib.ui.navigation.navigateToProfile
import com.pixelized.biblib.ui.screen.home.common.item.*
import com.pixelized.biblib.ui.screen.home.common.preview.thumbnailPreviewResources
import com.pixelized.biblib.ui.screen.home.filter.FilterChip
import com.pixelized.biblib.ui.screen.home.filter.FilterChipUio
import com.pixelized.biblib.ui.screen.home.filter.filterPreview
import com.pixelized.biblib.ui.screen.home.header.LazyGridCollapsingHeaderLayout
import com.pixelized.biblib.ui.screen.home.options.Options
import com.pixelized.biblib.ui.screen.home.options.OptionsUio
import com.pixelized.biblib.ui.screen.home.options.OptionsViewModel
import com.pixelized.biblib.ui.theme.BibLibTheme
import com.pixelized.biblib.utils.extention.bibLib
import com.pixelized.biblib.utils.extention.imeHeight
import com.pixelized.biblib.utils.extention.navigationBarsHeight
import com.skydoves.landscapist.glide.GlideImage
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.launch
@Stable
@Immutable
sealed class HomeScreenErrorUio(
@StringRes val message: Int,
@StringRes val action: Int,
) {
@Stable
@Immutable
class LibraryUpdate(
@StringRes message: Int,
@StringRes action: Int,
@ -77,12 +77,19 @@ sealed class HomeScreenErrorUio(
)
}
@Stable
@Immutable
enum class HomeDisplay {
MICRO,
SMALL,
GRID,
}
@OptIn(ExperimentalMaterialApi::class, ExperimentalComposeUiApi::class)
@Composable
fun HomeScreen(
bookViewModel: BookSearchViewModel = hiltViewModel(),
profileViewModel: HomeViewModel = hiltViewModel(),
optionsViewModel: OptionsViewModel = hiltViewModel(),
) {
val context = LocalContext.current
val navigation = LocalScreenNavHostController.current
@ -137,7 +144,7 @@ fun HomeScreen(
filterViewModel = bookViewModel,
filterState = filterState,
),
options = optionsViewModel.options,
options = bookViewModel.options,
onUp = {
scope.launch {
launch { largeGridState.animateScrollToItem(index = 0) }
@ -159,26 +166,21 @@ fun HomeScreen(
}
}
},
onMicro = optionsViewModel::onMicro,
onSmall = optionsViewModel::onSmall,
onLarge = optionsViewModel::onLarge,
onDisplay = bookViewModel::onDisplay,
isLoading = bookViewModel.updating,
onPullToRefresh = {
scope.launch {
bookViewModel.updateLibrary()
}
},
largeGridState = largeGridState,
largeGrid = bookViewModel.largePaging,
smallListState = smallListState,
smallList = bookViewModel.smallPaging,
microListState = microListState,
microList = bookViewModel.microPaging,
display = bookViewModel.display,
gridState = largeGridState,
items = bookViewModel.paging,
onBook = {
// close the keyboard.
focus.clearFocus(force = true)
keyboard?.hide()
// opent the detail bottom sheet.
// open the detail bottom sheet.
scope.launch {
detailState.expandBookDetail(id = it)
}
@ -295,17 +297,12 @@ private fun HomeScreenContent(
options: State<OptionsUio>,
onUp: () -> Unit,
onSort: () -> Unit,
onMicro: () -> Unit,
onSmall: () -> Unit,
onLarge: () -> Unit,
onDisplay: (HomeDisplay) -> Unit,
isLoading: State<Boolean>,
onPullToRefresh: () -> Unit,
largeGridState: LazyGridState,
largeGrid: Flow<PagingData<LargeBookThumbnailUio>>,
smallListState: LazyListState,
smallList: Flow<PagingData<SmallBookThumbnailUio>>,
microListState: LazyListState,
microList: Flow<PagingData<MicroBookThumbnailUio>>,
display: State<HomeDisplay>,
gridState: LazyGridState,
items: Flow<PagingData<BookThumbnailUio>>,
onBook: (id: Int) -> Unit,
) {
val theme = MaterialTheme.bibLib
@ -348,9 +345,9 @@ private fun HomeScreenContent(
options = options.value,
onUp = onUp,
onSort = onSort,
onMicro = onMicro,
onSmall = onSmall,
onLarge = onLarge,
onMicro = { onDisplay(HomeDisplay.MICRO) },
onSmall = { onDisplay(HomeDisplay.SMALL) },
onLarge = { onDisplay(HomeDisplay.GRID) },
)
},
loader = {
@ -370,7 +367,7 @@ private fun HomeScreenContent(
content = {
val navigationBarsHeight = navigationBarsHeight()
val imeHeight = imeHeight()
val padding = remember(navigationBarsHeight, imeHeight) {
val paddingValues = remember(navigationBarsHeight, imeHeight) {
PaddingValues(
start = theme.dimen.thumbnail.padding,
end = theme.dimen.thumbnail.padding,
@ -378,97 +375,20 @@ private fun HomeScreenContent(
bottom = theme.dimen.thumbnail.padding + max(navigationBarsHeight, imeHeight)
)
}
SwipeRefresh(
state = rememberSwipeRefreshState(isRefreshing = false),
onRefresh = onPullToRefresh,
) {
AnimatedVisibility(
visible = options.value.large,
enter = fadeIn(animationSpec = tween(delayMillis = 150)),
exit = fadeOut(animationSpec = tween()),
) {
val items = largeGrid.collectAsLazyPagingItems()
LazyVerticalGrid(
BookList(
modifier = Modifier.fillMaxSize(),
columns = GridCells.Fixed(2),
contentPadding = padding,
horizontalArrangement = Arrangement.spacedBy(
space = MaterialTheme.bibLib.dimen.thumbnail.arrangement,
),
verticalArrangement = Arrangement.spacedBy(
space = MaterialTheme.bibLib.dimen.thumbnail.arrangement,
),
state = largeGridState,
) {
items(
count = items.itemCount,
key = { items[it]?.id ?: it }
) { index ->
val item = items[index]
LargeBookThumbnail(
thumbnail = item,
onClick = { onBook(it.id) }
display = display,
items = items,
paddingValues = paddingValues,
state = gridState,
onBook = onBook,
)
}
}
}
AnimatedVisibility(
visible = options.value.small,
enter = fadeIn(animationSpec = tween(delayMillis = 150)),
exit = fadeOut(animationSpec = tween()),
) {
val items = smallList.collectAsLazyPagingItems()
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = padding,
verticalArrangement = Arrangement.spacedBy(
space = MaterialTheme.bibLib.dimen.thumbnail.arrangement,
),
state = smallListState,
) {
items(
count = items.itemCount,
key = { items[it]?.id ?: it },
) { index ->
val item = items[index]
SmallBookThumbnail(
thumbnail = item,
onClick = { onBook(it.id) }
)
}
}
}
AnimatedVisibility(
visible = options.value.micro,
enter = fadeIn(animationSpec = tween(delayMillis = 150)),
exit = fadeOut(animationSpec = tween()),
) {
val items = microList.collectAsLazyPagingItems()
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = padding,
verticalArrangement = Arrangement.spacedBy(
space = MaterialTheme.bibLib.dimen.thumbnail.arrangement,
),
state = microListState,
) {
items(
count = items.itemCount,
key = { "microList-${items[it]?.id ?: it}" },
) { index ->
val item = items[index]
MicroBookThumbnail(
thumbnail = item,
onClick = { onBook(it.id) }
)
}
}
}
}
}
)
}
@ -586,6 +506,105 @@ private fun HomeToolbar(
}
}
@Composable
private fun BookList(
modifier: Modifier = Modifier,
display: State<HomeDisplay>,
items: Flow<PagingData<BookThumbnailUio>>,
paddingValues: PaddingValues,
state: LazyGridState,
onBook: (id: Int) -> Unit,
) {
val data = items.collectAsLazyPagingItems()
AnimatedVisibility(
visible = display.value == HomeDisplay.MICRO,
enter = fadeIn(tween(durationMillis = 150)),
exit = fadeOut(tween())
) {
LazyVerticalGrid(
modifier = modifier,
columns = GridCells.Fixed(1),
contentPadding = paddingValues,
horizontalArrangement = Arrangement.spacedBy(
space = MaterialTheme.bibLib.dimen.thumbnail.arrangement,
),
verticalArrangement = Arrangement.spacedBy(
space = MaterialTheme.bibLib.dimen.thumbnail.arrangement,
),
state = state,
) {
items(
count = data.itemCount,
key = { index -> data[index]?.id ?: index }
) { index ->
MicroBookThumbnail(
thumbnail = data[index],
onClick = { onBook(it.id) },
)
}
}
}
AnimatedVisibility(
visible = display.value == HomeDisplay.SMALL,
enter = fadeIn(tween(durationMillis = 150)),
exit = fadeOut(tween())
) {
LazyVerticalGrid(
modifier = modifier,
columns = GridCells.Fixed(1),
contentPadding = paddingValues,
horizontalArrangement = Arrangement.spacedBy(
space = MaterialTheme.bibLib.dimen.thumbnail.arrangement,
),
verticalArrangement = Arrangement.spacedBy(
space = MaterialTheme.bibLib.dimen.thumbnail.arrangement,
),
state = state,
) {
items(
count = data.itemCount,
key = { index -> data[index]?.id ?: index }
) { index ->
SmallBookThumbnail(
thumbnail = data[index],
onClick = { onBook(it.id) },
)
}
}
}
AnimatedVisibility(
visible = display.value == HomeDisplay.GRID,
enter = fadeIn(tween(durationMillis = 150)),
exit = fadeOut(tween())
) {
LazyVerticalGrid(
modifier = modifier,
columns = GridCells.Fixed(2),
contentPadding = paddingValues,
horizontalArrangement = Arrangement.spacedBy(
space = MaterialTheme.bibLib.dimen.thumbnail.arrangement,
),
verticalArrangement = Arrangement.spacedBy(
space = MaterialTheme.bibLib.dimen.thumbnail.arrangement,
),
state = state,
) {
items(
count = data.itemCount,
key = { index -> data[index]?.id ?: index }
) { index ->
LargeBookThumbnail(
thumbnail = data[index],
onClick = { onBook(it.id) },
)
}
}
}
}
@OptIn(ExperimentalComposeUiApi::class)
@Composable
private fun CollapseKeyboardOnScrollHandler(
@ -627,17 +646,12 @@ private fun HomeScreenPreview() {
options = options,
onUp = { },
onSort = { },
onMicro = { },
onSmall = { },
onLarge = { },
onDisplay = { },
isLoading = remember { mutableStateOf(false) },
onPullToRefresh = { },
largeGridState = rememberLazyGridState(),
largeGrid = emptyFlow(),
smallListState = rememberLazyListState(),
smallList = emptyFlow(),
microListState = rememberLazyListState(),
microList = emptyFlow(),
display = remember { mutableStateOf(HomeDisplay.MICRO) },
gridState = rememberLazyGridState(),
items = thumbnailPreviewResources(),
onBook = { },
)
}

View file

@ -0,0 +1,38 @@
package com.pixelized.biblib.ui.screen.home.common.item
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
@Stable
@Immutable
data class BookThumbnailUio(
val id: Int,
val title: String,
val series: String?,
val genre: String?,
val author: String?,
val language: String?,
val date: String?,
val isNew: Boolean,
val thumbnail: String,
val cover: String,
)
class BookThumbnailPreviewProvider : PreviewParameterProvider<BookThumbnailUio?> {
override val values: Sequence<BookThumbnailUio?> = sequenceOf(
BookThumbnailUio(
id = 0,
title = "Foundation",
series = "Foundation - 1",
genre = "Sci-Fi",
author = "Asimov",
language = "Français",
date = "1951",
isNew = true,
thumbnail = "",
cover = "",
),
null
)
}

View file

@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.*
import androidx.compose.material.Card
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@ -16,7 +15,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
import com.pixelized.biblib.R
import com.pixelized.biblib.ui.theme.BibLibTheme
@ -25,18 +23,11 @@ import com.pixelized.biblib.utils.extention.modifier.drawDiagonalLabel
import com.pixelized.biblib.utils.extention.placeholder
import com.skydoves.landscapist.glide.GlideImage
@Stable
data class LargeBookThumbnailUio(
val id: Int,
val isNew: Boolean,
val cover: String,
)
@Composable
fun LargeBookThumbnail(
modifier: Modifier = Modifier,
thumbnail: LargeBookThumbnailUio?,
onClick: (LargeBookThumbnailUio) -> Unit,
thumbnail: BookThumbnailUio?,
onClick: (BookThumbnailUio) -> Unit,
) {
Card(
modifier = modifier
@ -61,8 +52,8 @@ fun LargeBookThumbnail(
@Composable
private fun LargeBookThumbnailContent(
modifier: Modifier = Modifier,
thumbnail: LargeBookThumbnailUio,
onClick: (LargeBookThumbnailUio) -> Unit,
thumbnail: BookThumbnailUio,
onClick: (BookThumbnailUio) -> Unit,
) {
GlideImage(
modifier = Modifier
@ -116,7 +107,7 @@ private fun Modifier.isNew(
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun LargeBookThumbnailPreview(
@PreviewParameter(LargeBookThumbnailPreviewProvider::class) preview: LargeBookThumbnailUio?,
@PreviewParameter(BookThumbnailPreviewProvider::class) preview: BookThumbnailUio?,
) {
BibLibTheme {
LargeBookThumbnail(
@ -126,14 +117,3 @@ private fun LargeBookThumbnailPreview(
)
}
}
private class LargeBookThumbnailPreviewProvider : PreviewParameterProvider<LargeBookThumbnailUio?> {
override val values: Sequence<LargeBookThumbnailUio?> = sequenceOf(
LargeBookThumbnailUio(
id = 0,
isNew = true,
cover = "",
),
null
)
}

View file

@ -11,7 +11,6 @@ import androidx.compose.material.Card
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@ -20,6 +19,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.constraintlayout.compose.ConstraintLayout
@ -31,21 +31,11 @@ import com.pixelized.biblib.utils.extention.modifier.drawDiagonalLabel
import com.pixelized.biblib.utils.extention.placeholder
import com.skydoves.landscapist.glide.GlideImage
@Stable
data class MicroBookThumbnailUio(
val id: Int,
val cover: String,
val title: String,
val author: String,
val series: String,
val isNew: Boolean,
)
@Composable
fun MicroBookThumbnail(
modifier: Modifier = Modifier,
thumbnail: MicroBookThumbnailUio?,
onClick: (MicroBookThumbnailUio) -> Unit,
thumbnail: BookThumbnailUio?,
onClick: (BookThumbnailUio) -> Unit,
) {
if (thumbnail != null) {
MicroBookThumbnailContent(
@ -63,8 +53,8 @@ fun MicroBookThumbnail(
@Composable
private fun MicroBookThumbnailContent(
modifier: Modifier = Modifier,
thumbnail: MicroBookThumbnailUio,
onClick: (MicroBookThumbnailUio) -> Unit,
thumbnail: BookThumbnailUio,
onClick: (BookThumbnailUio) -> Unit,
) {
Card(
modifier = modifier
@ -89,7 +79,7 @@ private fun MicroBookThumbnailContent(
.size(size = MaterialTheme.bibLib.dimen.thumbnail.micro)
.isNew { thumbnail.isNew },
previewPlaceholder = R.drawable.ic_fondation_thumbnail,
imageModel = thumbnail.cover,
imageModel = thumbnail.thumbnail,
)
Text(
@ -117,7 +107,7 @@ private fun MicroBookThumbnailContent(
color = MaterialTheme.bibLib.colors.typography.light,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
text = thumbnail.author,
text = thumbnail.author ?: "",
)
Text(
@ -131,7 +121,7 @@ private fun MicroBookThumbnailContent(
color = MaterialTheme.bibLib.colors.typography.light,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
text = thumbnail.series,
text = thumbnail.series ?: "",
)
}
}
@ -214,31 +204,14 @@ private fun Modifier.isNew(
}
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun MicroBookThumbnailPreview() {
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun MicroBookThumbnailPreview(
@PreviewParameter(BookThumbnailPreviewProvider::class) preview: BookThumbnailUio?,
) {
BibLibTheme {
MicroBookThumbnail(
thumbnail = MicroBookThumbnailUio(
id = 0,
cover = "",
title = "Foundation",
author = "Asimov",
series = "Foundation - 1",
isNew = true,
),
onClick = { },
)
}
}
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun MicroBookThumbnailEmptyPreview() {
BibLibTheme {
MicroBookThumbnail(
thumbnail = null,
thumbnail = preview,
onClick = { },
)
}

View file

@ -8,7 +8,6 @@ import androidx.compose.material.Card
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@ -17,6 +16,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.Dimension
@ -27,22 +27,11 @@ import com.pixelized.biblib.utils.extention.modifier.drawDiagonalLabel
import com.pixelized.biblib.utils.extention.placeholder
import com.skydoves.landscapist.glide.GlideImage
@Stable
data class SmallBookThumbnailUio(
val id: Int,
val genre: String,
val title: String,
val author: String,
val date: String?,
val isNew: Boolean,
val cover: String,
)
@Composable
fun SmallBookThumbnail(
modifier: Modifier = Modifier,
thumbnail: SmallBookThumbnailUio?,
onClick: (SmallBookThumbnailUio) -> Unit,
thumbnail: BookThumbnailUio?,
onClick: (BookThumbnailUio) -> Unit,
) {
if (thumbnail != null) {
SmallBookThumbnailContent(
@ -60,8 +49,8 @@ fun SmallBookThumbnail(
@Composable
private fun SmallBookThumbnailContent(
modifier: Modifier = Modifier,
thumbnail: SmallBookThumbnailUio,
onClick: (SmallBookThumbnailUio) -> Unit,
thumbnail: BookThumbnailUio,
onClick: (BookThumbnailUio) -> Unit,
) {
Card(
modifier = modifier
@ -73,7 +62,7 @@ private fun SmallBookThumbnailContent(
ConstraintLayout(
modifier = Modifier.fillMaxWidth(),
) {
val (cover, title, author, genre, date) = createRefs()
val (cover, title, author, series, genre, date) = createRefs()
GlideImage(
modifier = Modifier
@ -86,7 +75,7 @@ private fun SmallBookThumbnailContent(
.size(size = MaterialTheme.bibLib.dimen.thumbnail.cover)
.isNew { thumbnail.isNew },
previewPlaceholder = R.drawable.ic_fondation_thumbnail,
imageModel = thumbnail.cover,
imageModel = thumbnail.thumbnail,
)
Text(
@ -106,7 +95,7 @@ private fun SmallBookThumbnailContent(
Text(
modifier = Modifier.constrainAs(author) {
top.linkTo(title.bottom, margin = 4.dp)
bottom.linkTo(genre.top, margin = 4.dp)
bottom.linkTo(series.top, margin = 4.dp)
start.linkTo(cover.end, margin = 8.dp)
end.linkTo(parent.end, margin = 8.dp)
width = Dimension.fillToConstraints
@ -116,7 +105,22 @@ private fun SmallBookThumbnailContent(
color = MaterialTheme.bibLib.colors.typography.light,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
text = thumbnail.author
text = thumbnail.author ?: ""
)
Text(
modifier = Modifier.constrainAs(series) {
top.linkTo(author.bottom, margin = 4.dp)
bottom.linkTo(genre.top, margin = 4.dp)
start.linkTo(cover.end, margin = 8.dp)
end.linkTo(parent.end, margin = 8.dp)
width = Dimension.fillToConstraints
},
style = MaterialTheme.typography.caption,
color = MaterialTheme.bibLib.colors.typography.light,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
text = thumbnail.series ?: ""
)
Text(
@ -130,7 +134,7 @@ private fun SmallBookThumbnailContent(
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.caption,
color = MaterialTheme.bibLib.colors.typography.light,
text = thumbnail.genre
text = thumbnail.genre ?: ""
)
Text(
@ -240,32 +244,14 @@ private fun Modifier.isNew(
}
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun SmallBookThumbnailPreview() {
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun SmallBookThumbnailPreview(
@PreviewParameter(BookThumbnailPreviewProvider::class) preview: BookThumbnailUio?,
) {
BibLibTheme {
SmallBookThumbnail(
thumbnail = SmallBookThumbnailUio(
id = 0,
title = "Foundation",
genre = "Sci-Fi",
author = "Asimov",
date = "1951",
isNew = true,
cover = "",
),
onClick = { },
)
}
}
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun SmallBookThumbnailEmptyPreview() {
BibLibTheme {
SmallBookThumbnail(
thumbnail = null,
thumbnail = preview,
onClick = { },
)
}

View file

@ -3,192 +3,99 @@ package com.pixelized.biblib.ui.screen.home.common.preview
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.paging.PagingData
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import com.pixelized.biblib.ui.screen.home.common.item.LargeBookThumbnailUio
import com.pixelized.biblib.ui.screen.home.common.item.MicroBookThumbnailUio
import com.pixelized.biblib.ui.screen.home.common.item.SmallBookThumbnailUio
import com.pixelized.biblib.ui.screen.home.common.item.BookThumbnailUio
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
@Composable
fun microBookThumbnailPreviewResources(): LazyPagingItems<MicroBookThumbnailUio> {
fun thumbnailPreviewResources(): Flow<PagingData<BookThumbnailUio>> {
val thumbnails = remember {
listOf(
MicroBookThumbnailUio(
BookThumbnailUio(
id = 112,
title = "Prélude à Fondation",
author = "Asimov",
series = "Foundation",
genre = "SCI-FI",
isNew = false,
cover = "https://bib.bibulle.fr/api/book/thumbnail/112.jpg",
),
MicroBookThumbnailUio(
id = 78,
title = "L'Aube de Fondation",
author = "Asimov",
series = "Foundation",
isNew = false,
cover = "https://bib.bibulle.fr/api/book/thumbnail/78.jpg",
),
MicroBookThumbnailUio(
id = 90,
title = "Fondation",
author = "Asimov",
series = "Foundation",
isNew = false,
cover = "https://bib.bibulle.fr/api/book/thumbnail/90.jpg",
),
MicroBookThumbnailUio(
id = 184,
title = "Fondation et Empire",
author = "Asimov",
series = "Foundation",
isNew = false,
cover = "https://bib.bibulle.fr/api/book/thumbnail/184.jpg",
),
MicroBookThumbnailUio(
id = 185,
title = "Seconde Fondation",
author = "Asimov",
series = "Foundation",
isNew = false,
cover = "https://bib.bibulle.fr/api/book/thumbnail/185.jpg",
),
MicroBookThumbnailUio(
id = 119,
title = "Fondation foudroyée",
author = "Asimov",
series = "Foundation",
isNew = false,
cover = "https://bib.bibulle.fr/api/book/thumbnail/119.jpg",
),
MicroBookThumbnailUio(
id = 163,
title = "Terre et Fondation",
author = "Asimov",
series = "Foundation",
isNew = true,
cover = "https://bib.bibulle.fr/api/book/thumbnail/163.jpg",
),
)
}
return flowOf(PagingData.from(thumbnails)).collectAsLazyPagingItems()
}
@Composable
fun smallBookThumbnailPreviewResources(): LazyPagingItems<SmallBookThumbnailUio> {
val thumbnails = remember {
listOf(
SmallBookThumbnailUio(
id = 112,
title = "Prélude à Fondation",
author = "Asimov",
language = "Français",
date = "1988",
genre = "Sci-Fi",
isNew = false,
cover = "https://bib.bibulle.fr/api/book/thumbnail/112.jpg",
cover = "https://bib.bibulle.fr/api/book/cover/90.jpg",
thumbnail = "https://bib.bibulle.fr/api/book/thumbnail/112.jpg",
),
SmallBookThumbnailUio(
BookThumbnailUio(
id = 78,
title = "L'Aube de Fondation",
series = "Foundation",
genre = "SCI-FI",
author = "Asimov",
language = "Français",
date = "1993",
genre = "Sci-Fi",
isNew = false,
cover = "https://bib.bibulle.fr/api/book/thumbnail/78.jpg",
),
SmallBookThumbnailUio(
id = 90,
title = "Fondation",
author = "Asimov",
date = "1951",
genre = "Sci-Fi",
isNew = false,
cover = "https://bib.bibulle.fr/api/book/thumbnail/90.jpg",
),
SmallBookThumbnailUio(
id = 184,
title = "Fondation et Empire",
author = "Asimov",
date = "1952",
genre = "Sci-Fi",
isNew = false,
cover = "https://bib.bibulle.fr/api/book/thumbnail/184.jpg",
),
SmallBookThumbnailUio(
id = 185,
title = "Seconde Fondation",
author = "Asimov",
date = "1953",
genre = "Sci-Fi",
isNew = false,
cover = "https://bib.bibulle.fr/api/book/thumbnail/185.jpg",
),
SmallBookThumbnailUio(
id = 119,
title = "Fondation foudroyée",
author = "Asimov",
date = "1982",
genre = "Sci-Fi",
isNew = false,
cover = "https://bib.bibulle.fr/api/book/thumbnail/119.jpg",
),
SmallBookThumbnailUio(
id = 163,
title = "Terre et Fondation",
author = "Asimov",
date = "1986",
genre = "Sci-Fi",
isNew = true,
cover = "https://bib.bibulle.fr/api/book/thumbnail/163.jpg",
),
)
}
return flowOf(PagingData.from(thumbnails)).collectAsLazyPagingItems()
}
@Composable
fun largeBookThumbnailPreviewResources(): LazyPagingItems<LargeBookThumbnailUio> {
val thumbnails = remember {
listOf(
LargeBookThumbnailUio(
id = 112,
isNew = false,
cover = "https://bib.bibulle.fr/api/book/cover/112.jpg",
),
LargeBookThumbnailUio(
id = 78,
isNew = false,
cover = "https://bib.bibulle.fr/api/book/cover/78.jpg",
),
LargeBookThumbnailUio(
id = 90,
isNew = false,
cover = "https://bib.bibulle.fr/api/book/cover/90.jpg",
thumbnail = "https://bib.bibulle.fr/api/book/thumbnail/78.jpg",
),
LargeBookThumbnailUio(
BookThumbnailUio(
id = 90,
title = "Fondation",
series = "Foundation",
genre = "SCI-FI",
author = "Asimov",
language = "Français",
date = "1951",
isNew = false,
cover = "https://bib.bibulle.fr/api/book/cover/90.jpg",
thumbnail = "https://bib.bibulle.fr/api/book/thumbnail/90.jpg",
),
BookThumbnailUio(
id = 184,
title = "Fondation et Empire",
series = "Foundation",
genre = "SCI-FI",
author = "Asimov",
language = "Français",
date = "1952",
isNew = false,
cover = "https://bib.bibulle.fr/api/book/cover/184.jpg",
cover = "https://bib.bibulle.fr/api/book/cover/90.jpg",
thumbnail = "https://bib.bibulle.fr/api/book/thumbnail/184.jpg",
),
LargeBookThumbnailUio(
BookThumbnailUio(
id = 185,
title = "Seconde Fondation",
series = "Foundation",
genre = "SCI-FI",
author = "Asimov",
language = "Français",
date = "1953",
isNew = false,
cover = "https://bib.bibulle.fr/api/book/cover/185.jpg",
cover = "https://bib.bibulle.fr/api/book/cover/90.jpg",
thumbnail = "https://bib.bibulle.fr/api/book/thumbnail/185.jpg",
),
LargeBookThumbnailUio(
BookThumbnailUio(
id = 119,
title = "Fondation foudroyée",
series = "Foundation",
genre = "SCI-FI",
author = "Asimov",
language = "Français",
date = "1982",
isNew = false,
cover = "https://bib.bibulle.fr/api/book/cover/119.jpg",
cover = "https://bib.bibulle.fr/api/book/cover/90.jpg",
thumbnail = "https://bib.bibulle.fr/api/book/thumbnail/119.jpg",
),
LargeBookThumbnailUio(
BookThumbnailUio(
id = 163,
title = "Terre et Fondation",
series = "Foundation",
genre = "SCI-FI",
author = "Asimov",
language = "Français",
date = "1986",
isNew = true,
cover = "https://bib.bibulle.fr/api/book/cover/163.jpg",
cover = "https://bib.bibulle.fr/api/book/cover/90.jpg",
thumbnail = "https://bib.bibulle.fr/api/book/thumbnail/163.jpg",
),
)
}
return flowOf(PagingData.from(thumbnails)).collectAsLazyPagingItems()
return flowOf(PagingData.from(thumbnails))
}

View file

@ -4,13 +4,12 @@ import android.content.Context
import androidx.activity.compose.BackHandler
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import com.pixelized.biblib.R
@ -69,6 +68,8 @@ fun DetailScreen(
when (val detail = detailViewModel.detail.value) {
null -> EmptyDetail()
else -> {
val bibLibUrl = stringResource(id = R.string.detail_open_on_biblib_url, detail.id)
ModalBottomSheetLayout(
modifier = Modifier.fillMaxSize(),
sheetState = emailSheetState,
@ -92,10 +93,11 @@ fun DetailScreen(
if (detailState.bottomSheetState.isVisible) {
DetailScreenContent(
book = detail,
onOpenOnBibLib = {
uriHandler.openUri(bibLibUrl)
},
onAuthor = { onAuthor(it.name, it.id) },
onSeries = { onSeries(it.name, it.id) },
onMobi = { },
onEpub = { },
onSend = {
scope.launch {
send(

View file

@ -89,10 +89,9 @@ data class BookDetailUio(
fun DetailScreenContent(
modifier: Modifier = Modifier,
book: BookDetailUio,
onOpenOnBibLib: () -> Unit,
onAuthor: (BookDetailUio.AuthorUio) -> Unit,
onSeries: (BookDetailUio.SeriesUio) -> Unit,
onMobi: () -> Unit,
onEpub: () -> Unit,
onSend: () -> Unit,
) {
val density = LocalDensity.current
@ -109,17 +108,30 @@ fun DetailScreenContent(
.padding(all = 16.dp)
.systemBarsPadding(),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
GlideImage(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 16.dp)
.height(MaterialTheme.bibLib.dimen.detail.cover),
modifier = Modifier.height(MaterialTheme.bibLib.dimen.detail.cover),
previewPlaceholder = R.drawable.ic_fondatoin_cover,
circularReveal = CircularReveal(duration = 1000),
contentScale = ContentScale.FillHeight,
imageModel = book.cover,
)
Text(
modifier = Modifier
.clickable(onClick = onOpenOnBibLib)
.padding(
horizontal = 8.dp,
vertical = 2.dp,
),
style = MaterialTheme.bibLib.typography.base.caption,
color = MaterialTheme.bibLib.colors.typography.emphasis,
text = stringResource(id = R.string.detail_open_on_biblib)
)
}
AnimatedOffset {
Column(
@ -263,10 +275,9 @@ private fun DetailScreenContentPreview() {
.verticalScroll(state = rememberScrollState())
.padding(all = 16.dp),
book = BookDetailUio.preview(),
onOpenOnBibLib = { },
onAuthor = { },
onSeries = { },
onMobi = { },
onEpub = { },
onSend = { },
)
}

View file

@ -24,7 +24,7 @@ import com.pixelized.biblib.utils.extention.bibLib
@Stable
@Immutable
data class OptionsUio(
val sorting: Boolean,
val sorting: Boolean = false,
val micro: Boolean,
val small: Boolean,
val large: Boolean,

View file

@ -1,27 +0,0 @@
package com.pixelized.biblib.ui.screen.home.options
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
@HiltViewModel
class OptionsViewModel @Inject constructor() : ViewModel() {
private val _options = mutableStateOf(
OptionsUio(sorting = false, micro = true, small = false, large = false)
)
val options: State<OptionsUio> = _options
fun onMicro() {
_options.value = OptionsUio(false, true, false, false)
}
fun onSmall() {
_options.value = OptionsUio(false, false, true, false)
}
fun onLarge() {
_options.value = OptionsUio(false, false, false, true)
}
}

View file

@ -4,9 +4,7 @@ import androidx.annotation.StringDef
import com.pixelized.biblib.model.book.Book
import com.pixelized.biblib.model.book.Series
import com.pixelized.biblib.network.client.IBibLibClient
import com.pixelized.biblib.ui.screen.home.common.item.LargeBookThumbnailUio
import com.pixelized.biblib.ui.screen.home.common.item.MicroBookThumbnailUio
import com.pixelized.biblib.ui.screen.home.common.item.SmallBookThumbnailUio
import com.pixelized.biblib.ui.screen.home.common.item.BookThumbnailUio
import com.pixelized.biblib.ui.screen.home.detail.BookDetailUio
@ -17,34 +15,19 @@ import com.pixelized.biblib.ui.screen.home.detail.BookDetailUio
)
annotation class CoverUrl
fun Book.toMicroThumbnailUio(
@CoverUrl coverBaseUrl: String = IBibLibClient.THUMBNAIL_URL
) = MicroBookThumbnailUio(
fun Book.toThumbnailUio(
@CoverUrl coverBaseUrl: String = IBibLibClient.COVER_URL,
@CoverUrl thumbnailBaseUrl: String = IBibLibClient.THUMBNAIL_URL
) = BookThumbnailUio(
id = id,
cover = "${coverBaseUrl}/$id.jpg",
title = title,
author = author.joinToString { it.name },
series = series?.let { toLabel(it) } ?: "",
isNew = isNew,
)
fun Book.toSmallThumbnailUio(
@CoverUrl coverBaseUrl: String = IBibLibClient.THUMBNAIL_URL
) = SmallBookThumbnailUio(
id = id,
genre = genre?.joinToString { it.name } ?: "",
title = title,
author = author.joinToString { it.name },
date = releaseDate.longDate(),
isNew = isNew,
cover = "${coverBaseUrl}/$id.jpg",
)
fun Book.toLargeBookThumbnailUio(
@CoverUrl coverBaseUrl: String = IBibLibClient.COVER_URL
) = LargeBookThumbnailUio(
id = id,
language = language?.displayLanguage?.capitalize() ?: "",
date = releaseDate.year(),
isNew = isNew,
thumbnail = "${thumbnailBaseUrl}/$id.jpg",
cover = "${coverBaseUrl}/$id.jpg",
)

View file

@ -4,9 +4,9 @@ import java.text.Format
import java.text.SimpleDateFormat
import java.util.*
fun Date.longDate(
fun Date.year(
default: String? = null,
formatter: Format = SimpleDateFormat("MMMM yyyy", Locale.getDefault()),
formatter: Format = SimpleDateFormat("yyyy", Locale.getDefault()),
): String? = format(
default = default,
formatter = formatter

View file

@ -42,6 +42,7 @@
<string name="authentication_password">Mot de passe</string>
<string name="authentication_credential_remember">Mémoriser mes identifiants</string>
<string name="detail_open_on_biblib">ouvrir sur bib.bibulle.fr</string>
<string name="detail_rating">Note</string>
<string name="detail_language">Langue</string>
<string name="detail_release">Publication</string>

View file

@ -57,6 +57,8 @@
<string name="list_is_new" translatable="false">New</string>
<string name="detail_open_on_biblib">open on bib.bibulle.fr</string>
<string name="detail_open_on_biblib_url" translatable="false">https://bib.bibulle.fr/book/%1$s</string>
<string name="detail_rating">Rating</string>
<string name="detail_language">Language</string>
<string name="detail_release">Release</string>