Change search UI and refactor some stuff.

This commit is contained in:
Thomas Andres Gomez 2022-07-12 17:14:26 +02:00
parent 182c2bda48
commit 20a7811602
25 changed files with 520 additions and 393 deletions

View file

@ -91,6 +91,7 @@ dependencies {
// Android Compose
implementation "androidx.compose.ui:ui:1.2.0-rc02"
implementation "androidx.compose.material:material:1.1.1"
implementation "androidx.constraintlayout:constraintlayout-compose:1.0.1"
implementation "androidx.compose.runtime:runtime-livedata:1.1.1"
implementation "androidx.compose.ui:ui-tooling-preview:1.1.1"
debugImplementation "androidx.compose.ui:ui-tooling:1.1.1"

View file

@ -16,7 +16,7 @@ import com.pixelized.biblib.R
import com.pixelized.biblib.ui.composable.StateUio
import com.pixelized.biblib.ui.screen.home.detail.BookDetailViewModel
import com.pixelized.biblib.ui.screen.home.detail.DetailScreen
import com.pixelized.biblib.ui.screen.home.detail.BookUio
import com.pixelized.biblib.ui.screen.home.detail.BookDetailUio
import com.pixelized.biblib.utils.extention.showToast
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@ -61,7 +61,7 @@ fun rememberDetailBottomSheetState(
): DetailBottomSheetState {
val context: Context = LocalContext.current
val detail = rememberSaveable(scope, viewModel, bottomSheetState) {
mutableStateOf<BookUio?>(null)
mutableStateOf<BookDetailUio?>(null)
}
val controller = DetailBottomSheetState(
context = context,
@ -80,9 +80,9 @@ class DetailBottomSheetState constructor(
private val viewModel: BookDetailViewModel,
private val scope: CoroutineScope,
val bottomSheetState: ModalBottomSheetState,
bookDetail: MutableState<BookUio?>,
bookDetail: MutableState<BookDetailUio?>,
) {
var bookDetail: BookUio? by bookDetail
var bookDetail: BookDetailUio? by bookDetail
private set
fun expandBookDetail(id: Int) {

View file

@ -27,8 +27,8 @@ import com.pixelized.biblib.R
import com.pixelized.biblib.ui.composable.Search
import com.pixelized.biblib.ui.scaffold.*
import com.pixelized.biblib.ui.scaffold.SearchScaffoldState.ContentState
import com.pixelized.biblib.ui.screen.connectivity.ConnectivityViewModel
import com.pixelized.biblib.ui.screen.home.common.composable.ConnectivityHeader
import com.pixelized.biblib.ui.screen.home.common.connectivity.ConnectivityViewModel
import com.pixelized.biblib.ui.screen.home.common.connectivity.ConnectivityHeader
import com.pixelized.biblib.ui.screen.home.page.Page
import com.pixelized.biblib.ui.screen.home.page.books.BooksPage
import com.pixelized.biblib.ui.screen.home.page.news.NewsPage

View file

@ -1,13 +1,10 @@
package com.pixelized.biblib.ui.screen.home.common.composable
package com.pixelized.biblib.ui.screen.home.common.connectivity
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.expandVertically
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.sizeIn
import androidx.compose.foundation.layout.*
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
@ -41,17 +38,19 @@ fun ConnectivityHeader(
.sizeIn(minHeight = ButtonDefaults.MinHeight)
.padding(
horizontal = MaterialTheme.bibLib.dimen.thumbnail.padding,
vertical = MaterialTheme.bibLib.dimen.thumbnail.arrangement,
vertical = MaterialTheme.bibLib.dimen.dp8,
),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
) {
Icon(
modifier = Modifier.padding(end = MaterialTheme.bibLib.dimen.dp8),
modifier = Modifier.size(MaterialTheme.bibLib.dimen.dp16),
imageVector = Icons.Default.CloudOff,
tint = MaterialTheme.colors.onError,
contentDescription = null
)
Text(
modifier = Modifier.padding(start = MaterialTheme.bibLib.dimen.dp8),
style = MaterialTheme.typography.caption,
color = MaterialTheme.colors.onError,
text = stringResource(id = R.string.error_offline),

View file

@ -1,4 +1,4 @@
package com.pixelized.biblib.ui.screen.connectivity
package com.pixelized.biblib.ui.screen.home.common.connectivity
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf

View file

@ -0,0 +1,392 @@
package com.pixelized.biblib.ui.screen.home.common.item
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
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.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.Dimension
import com.pixelized.biblib.R
import com.pixelized.biblib.ui.theme.BibLibTheme
import com.pixelized.biblib.utils.extention.bibLib
import com.pixelized.biblib.utils.extention.default
import com.skydoves.landscapist.glide.GlideImage
@Stable
data class BookThumbnailUio(
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: BookThumbnailUio?,
onClick: (BookThumbnailUio) -> Unit = default<BookThumbnailUio>(),
) {
if (thumbnail != null) {
SmallBookThumbnailContent(
modifier = modifier,
thumbnail = thumbnail,
onClick = onClick,
)
} else {
SmallBookThumbnailPlaceHolder(
modifier = modifier,
)
}
}
@Composable
private fun SmallBookThumbnailContent(
modifier: Modifier = Modifier,
thumbnail: BookThumbnailUio,
onClick: (BookThumbnailUio) -> Unit = default<BookThumbnailUio>(),
) {
val dimen = MaterialTheme.bibLib.dimen
Card(
modifier = modifier
.clickable { onClick(thumbnail) }
.fillMaxWidth()
.wrapContentHeight(),
) {
ConstraintLayout(
modifier = Modifier.fillMaxWidth(),
) {
val (cover, title, author, genre, date) = createRefs()
GlideImage(
modifier = Modifier
.constrainAs(cover) {
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
start.linkTo(parent.start)
}
.size(size = MaterialTheme.bibLib.dimen.thumbnail.cover),
previewPlaceholder = R.drawable.ic_fondation_thumbnail,
imageModel = thumbnail.cover,
)
Text(
modifier = Modifier.constrainAs(title) {
top.linkTo(parent.top, margin = dimen.dp8)
start.linkTo(cover.end, margin = dimen.dp8)
end.linkTo(parent.end, margin = dimen.dp8)
width = Dimension.fillToConstraints
},
style = MaterialTheme.typography.h6,
color = MaterialTheme.colors.onSurface,
text = thumbnail.title,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
)
Text(
modifier = Modifier.constrainAs(author) {
top.linkTo(title.bottom, margin = dimen.dp4)
bottom.linkTo(genre.top, margin = dimen.dp4)
start.linkTo(cover.end, margin = dimen.dp8)
end.linkTo(parent.end, margin = dimen.dp8)
width = Dimension.fillToConstraints
height = Dimension.fillToConstraints
},
style = MaterialTheme.typography.body1,
color = MaterialTheme.colors.onSurface,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
text = thumbnail.author
)
Text(
modifier = Modifier.constrainAs(genre) {
bottom.linkTo(parent.bottom, margin = dimen.dp8)
start.linkTo(cover.end, margin = dimen.dp8)
end.linkTo(date.start, margin = dimen.dp8)
width = Dimension.fillToConstraints
},
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.caption,
color = MaterialTheme.colors.onSurface,
text = thumbnail.genre
)
Text(
modifier = Modifier.constrainAs(date) {
bottom.linkTo(parent.bottom, margin = dimen.dp8)
end.linkTo(parent.end, margin = dimen.dp8)
},
style = MaterialTheme.typography.caption,
color = MaterialTheme.colors.onSurface,
text = thumbnail.date ?: ""
)
}
}
}
@Composable
private fun SmallBookThumbnailPlaceHolder(
modifier: Modifier = Modifier,
) {
val dimen = MaterialTheme.bibLib.dimen
Card(
modifier = modifier
.fillMaxWidth()
.wrapContentHeight(),
) {
ConstraintLayout(
modifier = Modifier.fillMaxWidth(),
) {
val (cover, title, author, genre, date) = createRefs()
Box(
modifier = Modifier
.constrainAs(cover) {
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
start.linkTo(parent.start)
}
.size(size = MaterialTheme.bibLib.dimen.thumbnail.cover)
.background(
color = MaterialTheme.bibLib.color.placeHolder,
shape = MaterialTheme.shapes.medium,
),
)
Box(
modifier = Modifier
.constrainAs(title) {
top.linkTo(parent.top, margin = dimen.dp8)
start.linkTo(cover.end, margin = dimen.dp8)
}
.size(width = 120.dp, height = 16.dp)
.background(
color = MaterialTheme.bibLib.color.placeHolder,
shape = CircleShape,
),
)
Box(
modifier = Modifier
.constrainAs(author) {
top.linkTo(title.bottom, margin = dimen.dp4)
start.linkTo(cover.end, margin = dimen.dp8)
}
.size(width = 100.dp, height = 16.dp)
.background(
color = MaterialTheme.bibLib.color.placeHolder,
shape = CircleShape,
),
)
Box(
modifier = Modifier
.constrainAs(genre) {
bottom.linkTo(parent.bottom, margin = dimen.dp8)
start.linkTo(cover.end, margin = dimen.dp8)
}
.size(width = 80.dp, height = 16.dp)
.background(
color = MaterialTheme.bibLib.color.placeHolder,
shape = CircleShape,
),
)
Box(
modifier = Modifier
.constrainAs(date) {
bottom.linkTo(parent.bottom, margin = dimen.dp8)
end.linkTo(parent.end, margin = dimen.dp8)
}
.size(width = 40.dp, height = 16.dp)
.background(
color = MaterialTheme.bibLib.color.placeHolder,
shape = CircleShape,
),
)
}
}
}
@Composable
fun LargeBookThumbnail(
modifier: Modifier = Modifier,
thumbnail: BookThumbnailUio?,
onClick: (BookThumbnailUio) -> Unit = default<BookThumbnailUio>(),
) {
if (thumbnail != null) {
LargeBookThumbnailContent(
modifier = modifier,
thumbnail = thumbnail,
onClick = onClick,
)
} else {
LargeBookThumbnailPlaceHolder(
modifier = modifier,
)
}
}
@Composable
private fun LargeBookThumbnailContent(
modifier: Modifier = Modifier,
thumbnail: BookThumbnailUio,
onClick: (BookThumbnailUio) -> Unit = default<BookThumbnailUio>(),
) {
Column(
modifier = modifier.clickable { thumbnail.let(onClick) }
) {
GlideImage(
modifier = Modifier
.fillMaxWidth()
.aspectRatio(64f / 102f),
previewPlaceholder = R.drawable.ic_fondatoin_cover,
imageModel = thumbnail.cover,
)
Text(
style = MaterialTheme.typography.body1,
color = MaterialTheme.colors.onSurface,
text = thumbnail.title,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
Text(
style = MaterialTheme.typography.caption,
color = MaterialTheme.colors.onSurface,
text = thumbnail.author
)
Text(
style = MaterialTheme.typography.caption,
color = MaterialTheme.colors.onSurface,
text = thumbnail.date ?: ""
)
}
}
@Composable
private fun LargeBookThumbnailPlaceHolder(
modifier: Modifier = Modifier,
) {
Column(modifier = modifier) {
Box(
modifier = Modifier
.fillMaxWidth()
.aspectRatio(64f / 102f)
.background(
color = MaterialTheme.bibLib.color.placeHolder,
shape = MaterialTheme.shapes.medium,
),
)
Box(
modifier = Modifier
.padding(top = MaterialTheme.bibLib.dimen.dp4)
.size(width = 80.dp, height = 12.dp)
.background(
color = MaterialTheme.bibLib.color.placeHolder,
shape = CircleShape,
),
)
Box(
modifier = Modifier
.padding(top = MaterialTheme.bibLib.dimen.dp4)
.size(width = 60.dp, height = 12.dp)
.background(
color = MaterialTheme.bibLib.color.placeHolder,
shape = CircleShape,
),
)
Box(
modifier = Modifier
.padding(top = MaterialTheme.bibLib.dimen.dp4)
.size(width = 40.dp, height = 12.dp)
.background(
color = MaterialTheme.bibLib.color.placeHolder,
shape = CircleShape,
),
)
}
}
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun SmallBookThumbnailPreview() {
BibLibTheme {
SmallBookThumbnail(
thumbnail = BookThumbnailUio(
id = 0,
genre = "Sci-Fi",
title = "Foundation",
author = "Asimov",
date = "1951",
isNew = false,
cover = "",
)
)
}
}
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun SmallBookThumbnailEmptyPreview() {
BibLibTheme {
SmallBookThumbnail(
thumbnail = null,
)
}
}
@Composable
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun LargeBookThumbnailPreview() {
BibLibTheme {
LargeBookThumbnail(
modifier = Modifier.width(168.dp),
thumbnail = BookThumbnailUio(
id = 0,
genre = "Sci-Fi",
title = "Foundation",
author = "Asimov",
date = "1951",
isNew = false,
cover = "",
),
)
}
}
@Composable
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun LargeBookThumbnailEmptyPreview() {
BibLibTheme {
LargeBookThumbnail(
modifier = Modifier.width(168.dp),
thumbnail = null,
)
}
}

View file

@ -1,10 +1,10 @@
package com.pixelized.biblib.ui.screen.home.common.composable
package com.pixelized.biblib.ui.screen.home.common.preview
import androidx.compose.runtime.Composable
import androidx.paging.PagingData
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import com.pixelized.biblib.ui.screen.home.common.uio.BookThumbnailUio
import com.pixelized.biblib.ui.screen.home.common.item.BookThumbnailUio
import kotlinx.coroutines.flow.flowOf

View file

@ -1,11 +0,0 @@
package com.pixelized.biblib.ui.screen.home.common.uio
class BookThumbnailUio(
val id: Int,
val genre: String,
val title: String,
val author: String,
val date: String?,
val isNew: Boolean,
val cover: String,
)

View file

@ -2,12 +2,10 @@ package com.pixelized.biblib.ui.screen.home.detail
import android.util.Log
import androidx.lifecycle.ViewModel
import com.pixelized.biblib.model.book.Book
import com.pixelized.biblib.network.client.IBibLibClient
import com.pixelized.biblib.network.factory.BookFactory
import com.pixelized.biblib.ui.composable.StateUio
import com.pixelized.biblib.utils.extention.capitalize
import com.pixelized.biblib.utils.extention.shortDate
import com.pixelized.biblib.utils.extention.toDetailUio
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@ -18,7 +16,7 @@ class BookDetailViewModel @Inject constructor(
private val client: IBibLibClient,
) : ViewModel() {
suspend fun getDetail(id: Int): StateUio<BookUio> {
suspend fun getDetail(id: Int): StateUio<BookDetailUio> {
return withContext(Dispatchers.IO) {
try {
val book = getBookDetail(id = id)
@ -30,25 +28,10 @@ class BookDetailViewModel @Inject constructor(
}
}
private suspend fun getBookDetail(id: Int): BookUio {
private suspend fun getBookDetail(id: Int): BookDetailUio {
val factory = BookFactory()
val response = client.service.detail(id)
val book = factory.fromDetailResponseToBook(response)
return book.toUio()
}
private fun Book.toUio(): BookUio {
return BookUio(
id = id,
title = title,
author = author.joinToString { it.name },
rating = rating?.toFloat() ?: 0.0f,
language = language?.displayLanguage?.capitalize() ?: "",
date = releaseDate.shortDate(),
series = series?.name,
description = synopsis ?: "",
cover = "${IBibLibClient.COVER_URL}/$id.jpg",
)
return book.toDetailUio()
}
}

View file

@ -1,15 +0,0 @@
package com.pixelized.biblib.ui.screen.home.detail
import java.io.Serializable
data class BookUio(
val id: Int,
val title: String,
val author: String,
val rating: Float,
val language: String,
val date: String?,
val series: String?,
val description: String,
val cover: String,
) : Serializable

View file

@ -43,16 +43,29 @@ import com.pixelized.biblib.ui.theme.BibLibTheme
import com.pixelized.biblib.utils.extention.bibLib
import com.pixelized.biblib.utils.extention.default
import com.skydoves.landscapist.CircularReveal
import com.skydoves.landscapist.ShimmerParams
import com.skydoves.landscapist.glide.GlideImage
import kotlinx.coroutines.launch
import java.io.Serializable
@Stable
data class BookDetailUio(
val id: Int,
val title: String,
val author: String,
val rating: Float,
val language: String,
val date: String?,
val series: String?,
val description: String,
val cover: String,
) : Serializable
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun DetailScreen(
detailState: DetailBottomSheetState = LocalDetailBottomSheetState.current,
profileViewModel: ProfileViewModel = hiltViewModel(),
detail: BookUio? = null,
detail: BookDetailUio? = null,
) {
val scope = rememberCoroutineScope()
val sheet = rememberModalBottomSheetState(
@ -77,9 +90,6 @@ fun DetailScreen(
.navigationBarsPadding()
.padding(bottom = MaterialTheme.bibLib.dimen.dp16),
emails = (user as StateUio.Success<UserUio>).value.amazonEmails,
onAction = {
}
)
}
}
@ -109,7 +119,7 @@ fun DetailScreen(
@Composable
private fun DetailScreenContent(
modifier: Modifier = Modifier,
book: BookUio,
book: BookDetailUio,
onMobi: () -> Unit = default(),
onEpub: () -> Unit = default(),
onSend: () -> Unit = default(),
@ -136,7 +146,7 @@ private fun DetailScreenContent(
)
}
},
previewPlaceholder = R.drawable.ic_launcher_foreground,
previewPlaceholder = R.drawable.ic_fondatoin_cover,
circularReveal = CircularReveal(duration = 1000),
contentScale = ContentScale.FillHeight,
imageModel = book.cover,
@ -288,7 +298,7 @@ private fun TitleLabel(
private fun DetailScreenSendContent(
modifier: Modifier = Modifier,
emails: List<String>,
onAction: (email: String) -> Unit = default<String>(),
onEmail: (email: String) -> Unit = default<String>(),
) {
Column(
modifier = modifier.fillMaxWidth(),
@ -306,7 +316,7 @@ private fun DetailScreenSendContent(
items(items = emails) { email ->
Row(
modifier = Modifier
.clickable(onClick = { onAction(email) })
.clickable(onClick = { onEmail(email) })
.height(height = MaterialTheme.bibLib.dimen.dp52)
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
@ -340,7 +350,7 @@ private fun DetailScreenSendContent(
@Preview(showBackground = true, uiMode = UI_MODE_NIGHT_NO)
@Preview(showBackground = true, uiMode = UI_MODE_NIGHT_YES)
private fun DetailScreenContentPreview() {
val book = BookUio(
val book = BookDetailUio(
id = 90,
title = "Foundation",
author = "Asimov",

View file

@ -1,107 +0,0 @@
package com.pixelized.biblib.ui.screen.home.page.books
import android.content.res.Configuration
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.material.Card
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import com.pixelized.biblib.R
import com.pixelized.biblib.ui.screen.home.common.uio.BookThumbnailUio
import com.pixelized.biblib.ui.theme.BibLibTheme
import com.pixelized.biblib.utils.extention.bibLib
import com.skydoves.landscapist.glide.GlideImage
@Composable
fun BookThumbnail(
modifier: Modifier = Modifier,
thumbnail: BookThumbnailUio?,
onClick: (BookThumbnailUio) -> Unit = { },
) {
val currentOnClick by rememberUpdatedState(newValue = onClick)
Card(
modifier = modifier
.clickable(enabled = thumbnail != null) {
thumbnail?.let { currentOnClick(it) }
}
.wrapContentHeight(),
) {
Row(
modifier = Modifier.height(MaterialTheme.bibLib.dimen.thumbnail.cover.height),
verticalAlignment = Alignment.CenterVertically,
) {
thumbnail?.cover?.let { it ->
GlideImage(
modifier = Modifier.size(MaterialTheme.bibLib.dimen.thumbnail.cover),
previewPlaceholder = R.drawable.ic_launcher_foreground,
imageModel = it,
)
}
Column(
modifier = Modifier
.fillMaxWidth()
.padding(MaterialTheme.bibLib.dimen.dp8)
) {
Text(
modifier = Modifier.padding(bottom = MaterialTheme.bibLib.dimen.dp4),
style = MaterialTheme.typography.h6,
color = MaterialTheme.colors.onSurface,
text = thumbnail?.title ?: "",
maxLines = 2,
overflow = TextOverflow.Ellipsis,
)
Text(
style = MaterialTheme.typography.body1,
color = MaterialTheme.colors.onSurface,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
text = thumbnail?.author ?: ""
)
Spacer(modifier = Modifier.weight(1f))
Row(modifier = Modifier.fillMaxWidth()) {
Text(
style = MaterialTheme.typography.caption,
color = MaterialTheme.colors.onSurface,
text = thumbnail?.genre ?: ""
)
Spacer(modifier = Modifier.weight(1f))
Text(
style = MaterialTheme.typography.caption,
color = MaterialTheme.colors.onSurface,
text = thumbnail?.date ?: ""
)
}
}
}
}
}
@Composable
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun BookThumbnailPreview() {
val thumbnail = BookThumbnailUio(
id = 0,
genre = "Sci-Fi",
title = "Foundation",
author = "Asimov",
date = "1951",
isNew = false,
cover = "",
)
BibLibTheme {
BookThumbnail(thumbnail = thumbnail)
}
}

View file

@ -13,8 +13,9 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.items
import com.pixelized.biblib.ui.scaffold.LocalDetailBottomSheetState
import com.pixelized.biblib.ui.screen.home.common.composable.bookPreviewResources
import com.pixelized.biblib.ui.screen.home.common.uio.BookThumbnailUio
import com.pixelized.biblib.ui.screen.home.common.preview.bookPreviewResources
import com.pixelized.biblib.ui.screen.home.common.item.SmallBookThumbnail
import com.pixelized.biblib.ui.screen.home.common.item.BookThumbnailUio
import com.pixelized.biblib.ui.theme.BibLibTheme
import com.pixelized.biblib.utils.extention.bibLib
import com.pixelized.biblib.utils.extention.navigationBarsHeight
@ -48,7 +49,7 @@ private fun BooksPageContent(
items = books,
key = { it.id },
) { thumbnail ->
BookThumbnail(
SmallBookThumbnail(
modifier = Modifier.padding(horizontal = MaterialTheme.bibLib.dimen.thumbnail.padding),
thumbnail = thumbnail,
onClick = {

View file

@ -8,8 +8,9 @@ import androidx.paging.compose.collectAsLazyPagingItems
import com.pixelized.biblib.model.book.Book
import com.pixelized.biblib.network.client.IBibLibClient
import com.pixelized.biblib.repository.book.IBookRepository
import com.pixelized.biblib.ui.screen.home.common.uio.BookThumbnailUio
import com.pixelized.biblib.ui.screen.home.common.item.BookThumbnailUio
import com.pixelized.biblib.utils.extention.longDate
import com.pixelized.biblib.utils.extention.toThumbnailUio
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import javax.inject.Inject
@ -23,22 +24,12 @@ class BooksViewModel @Inject constructor(
private val booksSource = Pager(
config = PagingConfig(pageSize = PAGING_SIZE),
pagingSourceFactory = bookRepository.getBooksSource()
.map { it.toThumbnail() }
.map { it.toThumbnailUio() }
.asPagingSourceFactory(Dispatchers.IO)
).flow
val books @Composable get() = booksSource.collectAsLazyPagingItems()
private fun Book.toThumbnail() = BookThumbnailUio(
id = id,
genre = genre?.joinToString { it.name } ?: "",
title = title,
author = author.joinToString { it.name },
date = releaseDate.longDate(),
isNew = isNew,
cover = "${IBibLibClient.THUMBNAIL_URL}/$id.jpg"
)
companion object {
private const val PAGING_SIZE = 16
}

View file

@ -1,84 +0,0 @@
package com.pixelized.biblib.ui.screen.home.page.news
import android.content.res.Configuration
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.width
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.pixelized.biblib.ui.screen.home.common.uio.BookThumbnailUio
import com.pixelized.biblib.ui.theme.BibLibTheme
import com.pixelized.biblib.utils.extention.bibLib
import com.skydoves.landscapist.glide.GlideImage
import com.pixelized.biblib.R
@Composable
fun NewThumbnail(
modifier: Modifier = Modifier,
thumbnail: BookThumbnailUio?,
onClick: (BookThumbnailUio) -> Unit = { },
) {
Column(
modifier = modifier.clickable { thumbnail?.let(onClick) }
) {
thumbnail?.cover?.let { it ->
GlideImage(
modifier = Modifier
.fillMaxWidth()
.clip(MaterialTheme.bibLib.shape.base.medium)
.aspectRatio(64f / 102f),
previewPlaceholder = R.drawable.ic_baseline_auto_stories_24,
imageModel = it,
)
}
Text(
style = MaterialTheme.typography.body1,
color = MaterialTheme.colors.onSurface,
text = thumbnail?.title ?: "",
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
Text(
style = MaterialTheme.typography.caption,
color = MaterialTheme.colors.onSurface,
text = thumbnail?.author ?: ""
)
Text(
style = MaterialTheme.typography.caption,
color = MaterialTheme.colors.onSurface,
text = thumbnail?.date ?: ""
)
}
}
@Composable
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun BookThumbnailPreview() {
val thumbnail = BookThumbnailUio(
id = 0,
genre = "Sci-Fi",
title = "Foundation",
author = "Asimov",
date = "1951",
isNew = false,
cover = "",
)
BibLibTheme {
NewThumbnail(
modifier = Modifier.width(200.dp),
thumbnail = thumbnail,
)
}
}

View file

@ -6,11 +6,10 @@ import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import androidx.paging.compose.collectAsLazyPagingItems
import com.pixelized.biblib.model.book.Book
import com.pixelized.biblib.network.client.IBibLibClient
import com.pixelized.biblib.repository.book.IBookRepository
import com.pixelized.biblib.ui.screen.home.common.uio.BookThumbnailUio
import com.pixelized.biblib.utils.extention.longDate
import com.pixelized.biblib.ui.screen.home.common.item.BookThumbnailUio
import com.pixelized.biblib.utils.extention.toThumbnailUio
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
@ -25,24 +24,12 @@ class NewsBookViewModel @Inject constructor(
private val newsSource: Flow<PagingData<BookThumbnailUio>> = Pager(
config = PagingConfig(pageSize = PAGING_SIZE),
pagingSourceFactory = bookRepository.getNewsSource()
.map { it.toThumbnail() }
.map { it.toThumbnailUio(coverBaseUrl = IBibLibClient.COVER_URL) }
.asPagingSourceFactory(Dispatchers.IO)
).flow
val news @Composable get() = newsSource.collectAsLazyPagingItems()
private fun Book.toThumbnail(): BookThumbnailUio {
return BookThumbnailUio(
id = id,
genre = genre?.joinToString { it.name } ?: "",
title = title,
author = author.joinToString { it.name },
date = releaseDate.longDate(),
isNew = isNew,
cover = "${IBibLibClient.COVER_URL}/$id.jpg"
)
}
companion object {
private const val PAGING_SIZE = 8
}

View file

@ -7,15 +7,14 @@ import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.tooling.preview.Preview
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.paging.compose.LazyPagingItems
import com.pixelized.biblib.ui.scaffold.DetailBottomSheetState
import com.pixelized.biblib.ui.scaffold.LocalDetailBottomSheetState
import com.pixelized.biblib.ui.screen.home.common.composable.bookPreviewResources
import com.pixelized.biblib.ui.screen.home.common.uio.BookThumbnailUio
import com.pixelized.biblib.ui.screen.home.common.preview.bookPreviewResources
import com.pixelized.biblib.ui.screen.home.common.item.BookThumbnailUio
import com.pixelized.biblib.ui.screen.home.common.item.LargeBookThumbnail
import com.pixelized.biblib.ui.theme.BibLibTheme
import com.pixelized.biblib.utils.extention.bibLib
import com.pixelized.biblib.utils.extention.navigationBarsHeight
@ -52,7 +51,7 @@ private fun NewsPageContent(
items(
count = books.itemCount
) { index ->
NewThumbnail(
LargeBookThumbnail(
thumbnail = books[index],
onClick = {
detailBottomSheetState.expandBookDetail(id = it.id)
@ -66,7 +65,6 @@ private fun NewsPageContent(
@Preview(showBackground = true, uiMode = UI_MODE_NIGHT_NO)
@Preview(showBackground = true, uiMode = UI_MODE_NIGHT_YES)
private fun NewPagePreview() {
val isNetworkAvailable = remember { mutableStateOf(false) }
BibLibTheme {
Column {
NewsPageContent(

View file

@ -1,10 +0,0 @@
package com.pixelized.biblib.ui.screen.home.page.search
data class BookSearchUio(
val id: Int,
val title: String,
val author: List<String>,
val language: String? = null,
val genre: List<String>? = null,
val series: String? = null,
)

View file

@ -2,11 +2,9 @@ package com.pixelized.biblib.ui.screen.home.page.search
import android.content.res.Configuration.UI_MODE_NIGHT_NO
import android.content.res.Configuration.UI_MODE_NIGHT_YES
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.spring
import androidx.compose.foundation.clickable
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
@ -14,13 +12,14 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.material.icons.filled.NavigateNext
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.paging.LoadState
import androidx.paging.PagingData
@ -31,9 +30,12 @@ import com.pixelized.biblib.ui.scaffold.LocalDetailBottomSheetState
import com.pixelized.biblib.ui.scaffold.LocalSearchBottomSheetState
import com.pixelized.biblib.ui.scaffold.LocalSearchViewModel
import com.pixelized.biblib.ui.scaffold.SearchFilter
import com.pixelized.biblib.ui.screen.home.common.item.BookThumbnailUio
import com.pixelized.biblib.ui.screen.home.common.item.SmallBookThumbnail
import com.pixelized.biblib.ui.theme.BibLibTheme
import com.pixelized.biblib.utils.extention.bibLib
import com.pixelized.biblib.utils.extention.default
import com.pixelized.biblib.utils.extention.navigationBarsHeight
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
@ -57,22 +59,16 @@ fun SearchPage(
@Composable
private fun SearchPageContent(
modifier: Modifier = Modifier,
search: Flow<PagingData<BookSearchUio>> = emptyFlow(),
search: Flow<PagingData<BookThumbnailUio>> = emptyFlow(),
filters: List<SearchFilter> = SearchFilter.all,
onFilter: (filter: SearchFilter) -> Unit = default<SearchFilter>(),
) {
val items = search.collectAsLazyPagingItems()
val detail = LocalDetailBottomSheetState.current
SearchLoader(
modifier = Modifier
.fillMaxWidth()
.height(MaterialTheme.bibLib.dimen.dp2),
isLoading = { items.isLoading },
)
LazyColumn(
modifier = modifier,
contentPadding = PaddingValues(bottom = MaterialTheme.bibLib.dimen.thumbnail.padding + navigationBarsHeight()),
verticalArrangement = Arrangement.spacedBy(MaterialTheme.bibLib.dimen.dp8),
) {
item(key = "Search Filter") {
@ -82,17 +78,21 @@ private fun SearchPageContent(
onFilter = onFilter,
)
}
items(items = items, key = { it.id }) {
if (it != null) {
SearchItem(
item = it,
onClick = {
detail.expandBookDetail(it.id)
}
)
}
items(items = items, key = { it.id }) { item ->
SmallBookThumbnail(
modifier = Modifier.padding(horizontal = MaterialTheme.bibLib.dimen.thumbnail.padding),
thumbnail = item,
onClick = { item?.let { detail.expandBookDetail(it.id) } }
)
}
}
SearchLoader(
modifier = Modifier
.fillMaxWidth()
.height(MaterialTheme.bibLib.dimen.dp2),
isLoading = { items.isLoading },
)
}
@Composable
@ -111,34 +111,6 @@ private fun SearchLoader(
)
}
@Composable
private fun SearchItem(
modifier: Modifier = Modifier,
item: BookSearchUio,
onClick: () -> Unit = default()
) {
Row(
modifier = Modifier
.clickable(onClick = onClick)
.fillMaxWidth()
.padding(all = MaterialTheme.bibLib.dimen.dp16)
.then(modifier)
) {
Text(
modifier = Modifier.weight(1f),
style = MaterialTheme.typography.body1,
color = MaterialTheme.colors.onSurface,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
text = item.title
)
Icon(
imageVector = Icons.Default.NavigateNext,
contentDescription = null
)
}
}
@Composable
private fun SearchFilter(
modifier: Modifier = Modifier,
@ -222,25 +194,6 @@ private val LazyPagingItems<*>.isLoading: Boolean
return isLoading
}
@Composable
@Preview(showBackground = true, uiMode = UI_MODE_NIGHT_NO)
@Preview(showBackground = true, uiMode = UI_MODE_NIGHT_YES)
private fun SearchItemPreview() {
BibLibTheme {
SearchItem(
item = BookSearchUio(
id = 0,
title = "Fondation",
author = listOf("Issac Asimov"),
language = "Fr",
genre = listOf("SF, Classic"),
series = "Fondation",
)
)
}
}
@Composable
@Preview(showBackground = true, uiMode = UI_MODE_NIGHT_NO)
@Preview(showBackground = true, uiMode = UI_MODE_NIGHT_YES)

View file

@ -7,7 +7,11 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.*
import com.pixelized.biblib.model.book.Book
import com.pixelized.biblib.network.client.IBibLibClient
import com.pixelized.biblib.repository.book.IBookRepository
import com.pixelized.biblib.ui.screen.home.common.item.BookThumbnailUio
import com.pixelized.biblib.utils.extention.longDate
import com.pixelized.biblib.utils.extention.toThumbnailUio
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@ -86,7 +90,7 @@ class SearchViewModel @Inject constructor(
.combine(language.confirmFlow) { paging, filter ->
paging.filter { filter == null || it.language == null || it.language.id == filter.id }
}
.map { paging -> paging.map { it.toBookSearchUio() } }
.map { paging -> paging.map { it.toThumbnailUio() } }
data class FilterUio(
val id: Int,
@ -129,16 +133,7 @@ class SearchViewModel @Inject constructor(
}
}
private fun Book.toBookSearchUio() = BookSearchUio(
id = id,
title = title,
author = author.map { it.name },
language = language?.displayLanguage,
genre = genre?.map { it.name },
series = series?.name,
)
companion object {
private const val SEARCH_PAGE_SIZE = 15
private const val SEARCH_PAGE_SIZE = 10
}
}

View file

@ -11,7 +11,8 @@ import javax.annotation.concurrent.Immutable
@Stable
@Immutable
data class BibLibColor(
val base: Colors
val base: Colors,
val placeHolder: Color,
)
fun bibLibDarkColors(
@ -25,6 +26,7 @@ fun bibLibDarkColors(
),
) = BibLibColor(
base = base,
placeHolder = BibLibColorPalette.DarkGrey,
)
fun bibLibLightColors(
@ -38,4 +40,5 @@ fun bibLibLightColors(
)
) = BibLibColor(
base = base,
placeHolder = BibLibColorPalette.LightGrey,
)

View file

@ -37,7 +37,7 @@ object BibLibColorPalette {
val LightYellow: Color = Color(0xFFF5BF63)
val VeryLightYellow: Color = Color(0xFFF9D679)
val VeryDarkGrey: Color = Color(0xFF1D1D1D)
val DarkGrey: Color = Color(0xFF727272)
val DarkGrey: Color = Color(0xFF424242)
val Grey: Color = Color(0xFF919195)
val LightGrey: Color = Color(0xFFDFDFDF)
val VeryLightGrey: Color = Color(0xFFF9F9F9)

View file

@ -0,0 +1,41 @@
package com.pixelized.biblib.utils.extention
import androidx.annotation.StringDef
import com.pixelized.biblib.model.book.Book
import com.pixelized.biblib.network.client.IBibLibClient
import com.pixelized.biblib.ui.screen.home.common.item.BookThumbnailUio
import com.pixelized.biblib.ui.screen.home.detail.BookDetailUio
@Retention(AnnotationRetention.SOURCE)
@StringDef(
IBibLibClient.THUMBNAIL_URL,
IBibLibClient.COVER_URL,
)
annotation class CoverUrl
fun Book.toThumbnailUio(
@CoverUrl coverBaseUrl: String = IBibLibClient.THUMBNAIL_URL
) = BookThumbnailUio(
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.toDetailUio(
@CoverUrl coverBaseUrl: String = IBibLibClient.COVER_URL
) = BookDetailUio(
id = id,
title = title,
author = author.joinToString { it.name },
rating = rating?.toFloat() ?: 0.0f,
language = language?.displayLanguage?.capitalize() ?: "",
date = releaseDate.shortDate(),
series = series?.name,
description = synopsis ?: "",
cover = "${coverBaseUrl}/$id.jpg",
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 505 KiB