diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/home/BookSearchViewModel.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/home/BookSearchViewModel.kt index 3421ac0..70f3704 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/home/BookSearchViewModel.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/home/BookSearchViewModel.kt @@ -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 = _updating + private val _display = mutableStateOf(HomeDisplay.MICRO) + val display: State = _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> - val smallPaging: Flow> - val largePaging: Flow> + val paging: Flow> private val _search = mutableStateOf(null) val search: State = _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( diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/home/HomeScreen.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/home/HomeScreen.kt index 2be0dc4..e6f32a8 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/home/HomeScreen.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/home/HomeScreen.kt @@ -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, onUp: () -> Unit, onSort: () -> Unit, - onMicro: () -> Unit, - onSmall: () -> Unit, - onLarge: () -> Unit, + onDisplay: (HomeDisplay) -> Unit, isLoading: State, onPullToRefresh: () -> Unit, - largeGridState: LazyGridState, - largeGrid: Flow>, - smallListState: LazyListState, - smallList: Flow>, - microListState: LazyListState, - microList: Flow>, + display: State, + gridState: LazyGridState, + items: Flow>, 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,95 +375,18 @@ 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( - 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) } - ) - } - } - } - - 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) } - ) - } - } - } + BookList( + modifier = Modifier.fillMaxSize(), + display = display, + items = items, + paddingValues = paddingValues, + state = gridState, + onBook = onBook, + ) } } ) @@ -586,6 +506,105 @@ private fun HomeToolbar( } } +@Composable +private fun BookList( + modifier: Modifier = Modifier, + display: State, + items: Flow>, + 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 = { }, ) } diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/home/common/item/BookThumbnailUio.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/home/common/item/BookThumbnailUio.kt new file mode 100644 index 0000000..e78a242 --- /dev/null +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/home/common/item/BookThumbnailUio.kt @@ -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 { + override val values: Sequence = sequenceOf( + BookThumbnailUio( + id = 0, + title = "Foundation", + series = "Foundation - 1", + genre = "Sci-Fi", + author = "Asimov", + language = "Français", + date = "1951", + isNew = true, + thumbnail = "", + cover = "", + ), + null + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/home/common/item/LargeBookThumbnail.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/home/common/item/LargeBookThumbnail.kt index 507cdd8..fa08a02 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/home/common/item/LargeBookThumbnail.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/home/common/item/LargeBookThumbnail.kt @@ -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( @@ -125,15 +116,4 @@ private fun LargeBookThumbnailPreview( onClick = { }, ) } -} - -private class LargeBookThumbnailPreviewProvider : PreviewParameterProvider { - override val values: Sequence = sequenceOf( - LargeBookThumbnailUio( - id = 0, - isNew = true, - cover = "", - ), - null - ) } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/home/common/item/MicroBookThumbnail.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/home/common/item/MicroBookThumbnail.kt index 7258ad9..33ddcb1 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/home/common/item/MicroBookThumbnail.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/home/common/item/MicroBookThumbnail.kt @@ -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 = { }, ) } diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/home/common/item/SmallBookThumbnail.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/home/common/item/SmallBookThumbnail.kt index 2fe4d60..a8d4c96 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/home/common/item/SmallBookThumbnail.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/home/common/item/SmallBookThumbnail.kt @@ -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 = { }, ) } diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/home/common/preview/bookPreviewResources.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/home/common/preview/bookPreviewResources.kt index bcd9326..685c02f 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/home/common/preview/bookPreviewResources.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/home/common/preview/bookPreviewResources.kt @@ -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 { +fun thumbnailPreviewResources(): Flow> { 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 { - 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 { - 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)) } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/DetailScreen.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/DetailScreen.kt index f73eb01..52611c5 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/DetailScreen.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/DetailScreen.kt @@ -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( diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/DetailScreenContent.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/DetailScreenContent.kt index 26ffe56..cfb62c6 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/DetailScreenContent.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/DetailScreenContent.kt @@ -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 @@ -110,16 +109,29 @@ fun DetailScreenContent( .systemBarsPadding(), verticalArrangement = Arrangement.spacedBy(16.dp) ) { - GlideImage( - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 16.dp) - .height(MaterialTheme.bibLib.dimen.detail.cover), - previewPlaceholder = R.drawable.ic_fondatoin_cover, - circularReveal = CircularReveal(duration = 1000), - contentScale = ContentScale.FillHeight, - imageModel = book.cover, - ) + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + GlideImage( + 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 = { }, ) } diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/home/options/Options.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/home/options/Options.kt index 749e3b1..2132468 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/home/options/Options.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/home/options/Options.kt @@ -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, diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/home/options/OptionsViewModel.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/home/options/OptionsViewModel.kt deleted file mode 100644 index bdfced2..0000000 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/home/options/OptionsViewModel.kt +++ /dev/null @@ -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 = _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) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/biblib/utils/extention/BookEx.kt b/app/src/main/java/com/pixelized/biblib/utils/extention/BookEx.kt index 3506d7e..0d8d005 100644 --- a/app/src/main/java/com/pixelized/biblib/utils/extention/BookEx.kt +++ b/app/src/main/java/com/pixelized/biblib/utils/extention/BookEx.kt @@ -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", ) diff --git a/app/src/main/java/com/pixelized/biblib/utils/extention/DateEx.kt b/app/src/main/java/com/pixelized/biblib/utils/extention/DateEx.kt index 64573d6..37cac46 100644 --- a/app/src/main/java/com/pixelized/biblib/utils/extention/DateEx.kt +++ b/app/src/main/java/com/pixelized/biblib/utils/extention/DateEx.kt @@ -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 diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 486a0f4..bc47762 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -42,6 +42,7 @@ Mot de passe Mémoriser mes identifiants + ouvrir sur bib.bibulle.fr Note Langue Publication diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f08e1a7..58d0d21 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -57,6 +57,8 @@ New + open on bib.bibulle.fr + https://bib.bibulle.fr/book/%1$s Rating Language Release