Fix some layout.

This commit is contained in:
Thomas Andres Gomez 2021-05-21 16:34:26 +02:00
parent 9a5cd11782
commit f79445c12c
10 changed files with 121 additions and 60 deletions

View file

@ -16,10 +16,10 @@ class BookRepository : IBookRepository {
override fun getAll(): List<Book> = override fun getAll(): List<Book> =
database.bookDao().getAll().map { it.toBook() } database.bookDao().getAll().map { it.toBook() }
override fun getNews(): DataSource.Factory<Int, Book> = override fun getNewsSource(): DataSource.Factory<Int, Book> =
database.bookDao().getNews().map { it.toBook() } database.bookDao().getNews().map { it.toBook() }
override fun getBook(): DataSource.Factory<Int, Book> = override fun getBooksSource(): DataSource.Factory<Int, Book> =
database.bookDao().getBook().map { it.toBook() } database.bookDao().getBook().map { it.toBook() }
override suspend fun update(data: List<Book>) { override suspend fun update(data: List<Book>) {

View file

@ -7,9 +7,9 @@ interface IBookRepository {
fun getAll(): List<Book> fun getAll(): List<Book>
fun getNews(): DataSource.Factory<Int, Book> fun getNewsSource(): DataSource.Factory<Int, Book>
fun getBook(): DataSource.Factory<Int, Book> fun getBooksSource(): DataSource.Factory<Int, Book>
suspend fun update(data: List<Book>) suspend fun update(data: List<Book>)
} }

View file

@ -3,10 +3,7 @@ package com.pixelized.biblib.ui.composable.items
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Card import androidx.compose.material.*
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.NavigateNext import androidx.compose.material.icons.filled.NavigateNext
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -34,6 +31,18 @@ private val THUMBNAIL_HEIGHT: Dp = 96.dp
@Composable @Composable
fun BookItem( fun BookItem(
thumbnail: BookThumbnailUio?,
onClick: ((BookThumbnailUio) -> Unit)? = null,
) {
if (thumbnail != null) {
FilledBookItem(thumbnail, onClick)
} else {
EmptyBookItem()
}
}
@Composable
private fun FilledBookItem(
thumbnail: BookThumbnailUio, thumbnail: BookThumbnailUio,
onClick: ((BookThumbnailUio) -> Unit)? = null, onClick: ((BookThumbnailUio) -> Unit)? = null,
) { ) {
@ -78,24 +87,12 @@ fun BookItem(
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
softWrap = false, softWrap = false,
) )
Row( thumbnail.date?.let { date ->
modifier = Modifier.weight(1f), Spacer(modifier = Modifier.weight(1f))
verticalAlignment = Alignment.Bottom
) {
Text( Text(
modifier = Modifier.align(Alignment.End),
style = typography.caption, style = typography.caption,
text = thumbnail.genre, text = date,
overflow = TextOverflow.Ellipsis,
softWrap = false,
)
Spacer(
modifier = Modifier
.widthIn(min = 4.dp)
.weight(1f)
)
Text(
style = typography.caption,
text = thumbnail.date,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
softWrap = false, softWrap = false,
) )
@ -109,13 +106,61 @@ fun BookItem(
} }
} }
@Composable
fun EmptyBookItem() {
Card(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight(),
elevation = 4.dp,
) {
Row(
modifier = Modifier.height(THUMBNAIL_HEIGHT),
) {
Placeholder(
modifier = Modifier
.size(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT)
.clip(RoundedCornerShape(4.dp)),
)
Column(
modifier = Modifier
.padding(8.dp)
.weight(1f)
) {
Placeholder(
modifier = Modifier.size(200.dp, 24.dp)
)
Placeholder(
modifier = Modifier
.size(80.dp, 16.dp)
.padding(top = 4.dp)
)
Spacer(modifier = Modifier.weight(1f))
Placeholder(
modifier = Modifier
.size(60.dp, 14.dp)
.align(Alignment.End)
)
}
Icon(
modifier = Modifier.align(Alignment.CenterVertically),
imageVector = Icons.Default.NavigateNext, contentDescription = ""
)
}
}
}
@Composable
private fun Placeholder(modifier: Modifier) = Surface(modifier = modifier, elevation = 4.dp) {}
@Preview @Preview
@Composable @Composable
fun BookItemLightPreview() { fun BookItemLightPreview() {
Bob[BitmapCache::class] = BitmapCache(LocalContext.current) Bob[BitmapCache::class] = BitmapCache(LocalContext.current)
BibLibTheme { BibLibTheme {
val mock = BookThumbnailMock() val mock = BookThumbnailMock()
BookItem(thumbnail = mock.bookThumbnail) FilledBookItem(thumbnail = mock.bookThumbnail)
} }
} }
@ -125,6 +170,22 @@ fun BookItemDarkPreview() {
Bob[BitmapCache::class] = BitmapCache(LocalContext.current) Bob[BitmapCache::class] = BitmapCache(LocalContext.current)
BibLibTheme(darkTheme = true) { BibLibTheme(darkTheme = true) {
val mock = BookThumbnailMock() val mock = BookThumbnailMock()
BookItem(thumbnail = mock.bookThumbnail) FilledBookItem(thumbnail = mock.bookThumbnail)
}
}
@Preview
@Composable
fun EmptyBookItemLightPreview() {
BibLibTheme {
EmptyBookItem()
}
}
@Preview
@Composable
fun EmptyBookItemDarkPreview() {
BibLibTheme(darkTheme = true) {
EmptyBookItem()
} }
} }

View file

@ -22,6 +22,7 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
@ -121,16 +122,14 @@ fun DetailPage(book: BookUio) {
title = stringResource(id = R.string.detail_language), title = stringResource(id = R.string.detail_language),
label = book.language, label = book.language,
) )
TitleLabel( book.date?.let {
title = stringResource(id = R.string.detail_release), TitleLabel(
label = book.date, title = stringResource(id = R.string.detail_release),
) label = it,
)
}
} }
Row(modifier = Modifier.padding(bottom = 16.dp)) { Row(modifier = Modifier.padding(bottom = 16.dp)) {
TitleLabel(
title = stringResource(id = R.string.detail_genre),
label = book.genre,
)
TitleLabel( TitleLabel(
title = stringResource(id = R.string.detail_series), title = stringResource(id = R.string.detail_series),
label = book.series ?: "-", label = book.series ?: "-",
@ -161,6 +160,7 @@ private fun RowScope.TitleLabel(
) )
Text( Text(
style = typography.body1, style = typography.body1,
textAlign = TextAlign.Center,
text = label, text = label,
) )
} }

View file

@ -31,8 +31,8 @@ fun HomePage(
// https://issuetracker.google.com/issues/177245496 // https://issuetracker.google.com/issues/177245496
val data: LazyPagingItems<BookThumbnailUio> = when (currentPage) { val data: LazyPagingItems<BookThumbnailUio> = when (currentPage) {
is Page.Home.New -> booksViewModel.books.collectAsLazyPagingItems() is Page.Home.New -> booksViewModel.news.collectAsLazyPagingItems()
else -> booksViewModel.news.collectAsLazyPagingItems() else -> booksViewModel.books.collectAsLazyPagingItems()
} }
val lazyListState = rememberLazyListState() val lazyListState = rememberLazyListState()
@ -45,7 +45,7 @@ fun HomePage(
state = lazyListState, state = lazyListState,
) { ) {
items(data) { thumbnail -> items(data) { thumbnail ->
BookItem(thumbnail = thumbnail!!) { item -> BookItem(thumbnail) { item ->
navigationViewModel.navigateTo(Page.Detail(item.id)) navigationViewModel.navigateTo(Page.Detail(item.id))
} }
} }

View file

@ -8,7 +8,7 @@ data class BookThumbnailUio(
val genre: String, val genre: String,
val title: String, val title: String,
val author: String, val author: String,
val date: String, val date: String?,
val isNew: Boolean, val isNew: Boolean,
) { ) {
val cover: URL = URL("${THUMBNAIL_URL}/$id.jpg") val cover: URL = URL("${THUMBNAIL_URL}/$id.jpg")

View file

@ -7,10 +7,9 @@ data class BookUio(
val id: Int, val id: Int,
val title: String, val title: String,
val author: String, val author: String,
val genre: String,
val rating: Float, val rating: Float,
val language: String, val language: String,
val date: String, val date: String?,
val series: String?, val series: String?,
val description: String, val description: String,
) { ) {

View file

@ -8,5 +8,5 @@ val Purple700 = Color(0xFF3700B3)
val Teal200 = Color(0xFF03DAC5) val Teal200 = Color(0xFF03DAC5)
val Green600 = Color(0xFF43a047) val Green600 = Color(0xFF43a047)
val Green600L = Color(0XFF76d275) val Green600L = Color(0xFF76d275)
val Green600D = Color(0XFF00701a) val Green600D = Color(0xFF00701a)

View file

@ -8,6 +8,7 @@ import androidx.lifecycle.viewModelScope
import androidx.paging.Pager import androidx.paging.Pager
import androidx.paging.PagingConfig import androidx.paging.PagingConfig
import androidx.paging.PagingData import androidx.paging.PagingData
import androidx.paging.PagingSource
import com.pixelized.biblib.model.Book import com.pixelized.biblib.model.Book
import com.pixelized.biblib.network.client.IBibLibClient import com.pixelized.biblib.network.client.IBibLibClient
import com.pixelized.biblib.network.factory.BookFactory import com.pixelized.biblib.network.factory.BookFactory
@ -27,25 +28,26 @@ class BooksViewModel : ViewModel(), IBooksViewModel {
private val client: IBibLibClient by inject() private val client: IBibLibClient by inject()
private val apiCache: IAPICacheRepository by inject() private val apiCache: IAPICacheRepository by inject()
private val formatter = SimpleDateFormat("MMMM yyyy", Locale.getDefault()) private val formatter_long = SimpleDateFormat("MMMM yyyy", Locale.getDefault())
private val formatter_short = SimpleDateFormat("MMM yyyy", Locale.getDefault())
private val _state = MutableLiveData<IBooksViewModel.State>(IBooksViewModel.State.Initial) private val _state = MutableLiveData<IBooksViewModel.State>(IBooksViewModel.State.Initial)
override val state: LiveData<IBooksViewModel.State> get() = _state override val state: LiveData<IBooksViewModel.State> get() = _state
private val bookSource private val bookSource: () -> PagingSource<Int, BookThumbnailUio>
get() = bookRepository.getBook() get() = bookRepository.getBooksSource()
.map { it.toThumbnail() } .map { it.toThumbnail() }
.asPagingSourceFactory(Dispatchers.Default) .asPagingSourceFactory(Dispatchers.IO)
override val books: Flow<PagingData<BookThumbnailUio>> = Pager( override val books: Flow<PagingData<BookThumbnailUio>> = Pager(
config = PagingConfig(pageSize = PAGING_SIZE), config = PagingConfig(pageSize = PAGING_SIZE),
pagingSourceFactory = bookSource pagingSourceFactory = bookSource
).flow ).flow
private val newsSource private val newsSource: () -> PagingSource<Int, BookThumbnailUio>
get() = bookRepository.getNews() get() = bookRepository.getNewsSource()
.map { it.toThumbnail() } .map { it.toThumbnail() }
.asPagingSourceFactory(Dispatchers.Default) .asPagingSourceFactory(Dispatchers.IO)
override val news: Flow<PagingData<BookThumbnailUio>> = Pager( override val news: Flow<PagingData<BookThumbnailUio>> = Pager(
config = PagingConfig(pageSize = PAGING_SIZE), config = PagingConfig(pageSize = PAGING_SIZE),
@ -107,7 +109,11 @@ class BooksViewModel : ViewModel(), IBooksViewModel {
genre = genre?.joinToString { it.name } ?: "", genre = genre?.joinToString { it.name } ?: "",
title = title, title = title,
author = author.joinToString { it.name }, author = author.joinToString { it.name },
date = formatter.format(releaseDate).capitalize(Locale.getDefault()), date = if (releaseDate.time < 0) {
null
} else {
formatter_long.format(releaseDate).capitalize(Locale.getDefault())
},
isNew = isNew, isNew = isNew,
) )
@ -115,10 +121,13 @@ class BooksViewModel : ViewModel(), IBooksViewModel {
id = id, id = id,
title = title, title = title,
author = author.joinToString { it.name }, author = author.joinToString { it.name },
genre = genre?.joinToString { it.name } ?: "",
rating = rating?.toFloat() ?: 0.0f, rating = rating?.toFloat() ?: 0.0f,
language = language?.displayLanguage?.capitalize(Locale.getDefault()) ?: "", language = language?.displayLanguage?.capitalize(Locale.getDefault()) ?: "",
date = formatter.format(releaseDate).capitalize(Locale.getDefault()), date = if (releaseDate.time < 0) {
null
} else {
formatter_short.format(releaseDate).capitalize(Locale.getDefault())
},
series = series?.name, series = series?.name,
description = synopsis ?: "", description = synopsis ?: "",
) )

View file

@ -5,7 +5,6 @@ import com.pixelized.biblib.ui.data.BookUio
class BookMock { class BookMock {
val book: BookUio = BookUio( val book: BookUio = BookUio(
id = 90, id = 90,
genre = "Sci-Fi",
title = "Foundation", title = "Foundation",
author = "Asimov", author = "Asimov",
date = "1951", date = "1951",
@ -19,7 +18,6 @@ class BookMock {
112 to BookUio( 112 to BookUio(
id = 112, id = 112,
title = "Prélude à Fondation", title = "Prélude à Fondation",
genre = "Sci-Fi",
author = "Asimov", author = "Asimov",
date = "1988", date = "1988",
series = "Foundation - 1", series = "Foundation - 1",
@ -30,7 +28,6 @@ class BookMock {
78 to BookUio( 78 to BookUio(
id = 78, id = 78,
title = "L'Aube de Fondation", title = "L'Aube de Fondation",
genre = "Sci-Fi",
author = "Asimov", author = "Asimov",
date = "1993", date = "1993",
series = "Foundation - 2", series = "Foundation - 2",
@ -41,7 +38,6 @@ class BookMock {
90 to BookUio( 90 to BookUio(
id = 90, id = 90,
title = "Fondation", title = "Fondation",
genre = "Sci-Fi",
author = "Asimov", author = "Asimov",
date = "1951", date = "1951",
series = "Foundation - 3", series = "Foundation - 3",
@ -52,7 +48,6 @@ class BookMock {
184 to BookUio( 184 to BookUio(
id = 184, id = 184,
title = "Fondation et Empire", title = "Fondation et Empire",
genre = "Sci-Fi",
author = "Asimov", author = "Asimov",
date = "1952", date = "1952",
series = "Foundation - 4", series = "Foundation - 4",
@ -63,7 +58,6 @@ class BookMock {
185 to BookUio( 185 to BookUio(
id = 185, id = 185,
title = "Seconde Fondation", title = "Seconde Fondation",
genre = "Sci-Fi",
author = "Asimov", author = "Asimov",
date = "1953", date = "1953",
series = "Foundation - 5", series = "Foundation - 5",
@ -74,7 +68,6 @@ class BookMock {
119 to BookUio( 119 to BookUio(
id = 119, id = 119,
title = "Fondation foudroyée", title = "Fondation foudroyée",
genre = "Sci-Fi",
author = "Asimov", author = "Asimov",
date = "1982", date = "1982",
series = "Foundation - 6", series = "Foundation - 6",
@ -85,7 +78,6 @@ class BookMock {
163 to BookUio( 163 to BookUio(
id = 163, id = 163,
title = "Terre et Fondation", title = "Terre et Fondation",
genre = "Sci-Fi",
author = "Asimov", author = "Asimov",
date = "1986", date = "1986",
series = "Foundation - 7", series = "Foundation - 7",