Simplify the detail page. & small code clean up
This commit is contained in:
parent
100f8d32bd
commit
9c8d05e578
9 changed files with 428 additions and 356 deletions
|
|
@ -86,9 +86,6 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// Kotlin core
|
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.8.10"
|
|
||||||
|
|
||||||
// Android core
|
// Android core
|
||||||
implementation 'androidx.core:core-ktx:1.9.0'
|
implementation 'androidx.core:core-ktx:1.9.0'
|
||||||
implementation 'androidx.appcompat:appcompat:1.7.0-alpha02'
|
implementation 'androidx.appcompat:appcompat:1.7.0-alpha02'
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import androidx.compose.material.ExperimentalMaterialApi
|
||||||
import androidx.compose.material.ModalBottomSheetState
|
import androidx.compose.material.ModalBottomSheetState
|
||||||
import androidx.compose.material.ModalBottomSheetValue
|
import androidx.compose.material.ModalBottomSheetValue
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun HandleBottomSheetData(
|
fun HandleBottomSheetData(
|
||||||
|
|
@ -34,12 +35,22 @@ fun HandleBottomSheetDismiss(
|
||||||
val currentOnDismiss by rememberUpdatedState(
|
val currentOnDismiss by rememberUpdatedState(
|
||||||
newValue = onDismiss,
|
newValue = onDismiss,
|
||||||
)
|
)
|
||||||
val haveBeenDismissed by remember(bottomSheetState) {
|
// Showing a bottom sheet produce a :
|
||||||
derivedStateOf {
|
// - currentValue == Hidden targetValue == Hidden
|
||||||
bottomSheetState.currentValue != ModalBottomSheetValue.Hidden && bottomSheetState.targetValue == ModalBottomSheetValue.Hidden
|
// before a :
|
||||||
|
// - currentValue == Hidden targetValue == Expanded
|
||||||
|
// We need to keep track of this behavior to avoid closing the bottom sheet immediately.
|
||||||
|
var skipLaunchEffect by rememberSaveable {
|
||||||
|
mutableStateOf(true)
|
||||||
|
}
|
||||||
|
LaunchedEffect(bottomSheetState.currentValue) {
|
||||||
|
if (skipLaunchEffect.not()) {
|
||||||
|
if (bottomSheetState.currentValue == ModalBottomSheetValue.Hidden && bottomSheetState.targetValue == ModalBottomSheetValue.Hidden) {
|
||||||
|
currentOnDismiss()
|
||||||
|
skipLaunchEffect = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
skipLaunchEffect = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (haveBeenDismissed) {
|
|
||||||
currentOnDismiss()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -11,7 +11,6 @@ import com.pixelized.biblib.model.book.Book
|
||||||
import com.pixelized.biblib.model.search.FilterType
|
import com.pixelized.biblib.model.search.FilterType
|
||||||
import com.pixelized.biblib.model.search.SortType
|
import com.pixelized.biblib.model.search.SortType
|
||||||
import com.pixelized.biblib.model.search.SortValue
|
import com.pixelized.biblib.model.search.SortValue
|
||||||
import com.pixelized.biblib.repository.book.BookRepository
|
|
||||||
import com.pixelized.biblib.repository.book.IBookRepository
|
import com.pixelized.biblib.repository.book.IBookRepository
|
||||||
import com.pixelized.biblib.repository.search.ISearchRepository
|
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.LargeBookThumbnailUio
|
||||||
|
|
@ -105,142 +104,102 @@ class BookSearchViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sort(sortBy: Map<SortType, SortValue?>) {
|
fun source(clean: Boolean = false, block: SortAndFilterScope.() -> Unit) {
|
||||||
this.sortBy = sortBy
|
val scope = if (clean) {
|
||||||
|
SortAndFilterScope(
|
||||||
|
sortBy = mapOf(),
|
||||||
|
isNew = null,
|
||||||
|
title = null,
|
||||||
|
author = null,
|
||||||
|
authorId = null,
|
||||||
|
series = null,
|
||||||
|
seriesId = null,
|
||||||
|
genre = null,
|
||||||
|
genreId = null,
|
||||||
|
language = null,
|
||||||
|
languageId = null,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
SortAndFilterScope(
|
||||||
|
sortBy = sortBy,
|
||||||
|
isNew = filterByNew,
|
||||||
|
title = search.value,
|
||||||
|
author = authorFilter.value.label,
|
||||||
|
authorId = filterByAuthorId,
|
||||||
|
series = seriesFilter.value.label,
|
||||||
|
seriesId = filterBySeriesId,
|
||||||
|
genre = genreFilter.value.label,
|
||||||
|
genreId = filterByGenreId,
|
||||||
|
language = languageFilter.value.label,
|
||||||
|
languageId = filterByLanguageId,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
scope.block()
|
||||||
|
// filter the search
|
||||||
|
if (_search.value != scope.title) {
|
||||||
|
_search.value = scope.title
|
||||||
|
}
|
||||||
|
// clean the new filter
|
||||||
|
if (filterByNew != scope.isNew) {
|
||||||
|
filterByNew = scope.isNew
|
||||||
|
newFilter.value = newFilter.value.copy(
|
||||||
|
selected = scope.isNew == true,
|
||||||
|
closable = scope.isNew == true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// clean the author filter
|
||||||
|
if (filterByAuthorId != scope.authorId) {
|
||||||
|
filterByAuthorId = scope.authorId
|
||||||
|
authorFilter.value = authorFilter.value.copy(
|
||||||
|
selected = scope.authorId != null,
|
||||||
|
label = scope.author,
|
||||||
|
closable = scope.authorId != null,
|
||||||
|
openable = scope.authorId == null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// clean the author filter
|
||||||
|
if (filterBySeriesId != scope.seriesId) {
|
||||||
|
filterBySeriesId = scope.seriesId
|
||||||
|
seriesFilter.value = seriesFilter.value.copy(
|
||||||
|
selected = scope.seriesId != null,
|
||||||
|
label = scope.series,
|
||||||
|
closable = scope.seriesId != null,
|
||||||
|
openable = scope.seriesId == null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// clean the author filter
|
||||||
|
if (filterByGenreId != scope.genreId) {
|
||||||
|
filterByGenreId = scope.genreId
|
||||||
|
genreFilter.value = genreFilter.value.copy(
|
||||||
|
selected = scope.genreId != null,
|
||||||
|
label = scope.genre,
|
||||||
|
closable = scope.genreId != null,
|
||||||
|
openable = scope.genreId == null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// clean the author filter
|
||||||
|
if (filterByLanguageId != scope.languageId) {
|
||||||
|
filterByLanguageId = scope.languageId
|
||||||
|
languageFilter.value = languageFilter.value.copy(
|
||||||
|
selected = scope.languageId != null,
|
||||||
|
label = scope.language,
|
||||||
|
closable = scope.languageId != null,
|
||||||
|
openable = scope.languageId == null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// apply the sorting
|
||||||
|
if (sortBy != scope.sortBy) {
|
||||||
|
sortBy = scope.sortBy
|
||||||
|
}
|
||||||
|
// invalidate the source
|
||||||
searchSource?.invalidate()
|
searchSource?.invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cleanFilterThen(block: BookSearchViewModel.() -> Unit) {
|
fun filterByTitle(criteria: String?) {
|
||||||
// filter the search
|
|
||||||
_search.value = null
|
|
||||||
// clean the new filter
|
|
||||||
filterByNew = null
|
|
||||||
newFilter.value = newFilter.value.copy(selected = false)
|
|
||||||
// clean the author filter
|
|
||||||
filterByAuthorId = null
|
|
||||||
authorFilter.value = authorFilter.value.copy(
|
|
||||||
selected = false, label = null, closable = false, openable = true
|
|
||||||
)
|
|
||||||
// clean the author filter
|
|
||||||
filterBySeriesId = null
|
|
||||||
seriesFilter.value = seriesFilter.value.copy(
|
|
||||||
selected = false, label = null, closable = false, openable = true
|
|
||||||
)
|
|
||||||
// clean the author filter
|
|
||||||
filterByGenreId = null
|
|
||||||
genreFilter.value = genreFilter.value.copy(
|
|
||||||
selected = false, label = null, closable = false, openable = true
|
|
||||||
)
|
|
||||||
// clean the author filter
|
|
||||||
filterByLanguageId = null
|
|
||||||
languageFilter.value = languageFilter.value.copy(
|
|
||||||
selected = false, label = null, closable = false, openable = true
|
|
||||||
)
|
|
||||||
// call the filter
|
|
||||||
this.block()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun filterSearch(criteria: String?) {
|
|
||||||
_search.value = criteria
|
_search.value = criteria
|
||||||
searchSource?.invalidate()
|
searchSource?.invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun filterByNew(criteria: Boolean) {
|
|
||||||
if (filterByNew != criteria) {
|
|
||||||
filterByNew = if (criteria) true else null
|
|
||||||
newFilter.value = newFilter.value.copy(selected = criteria)
|
|
||||||
searchSource?.invalidate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun filterByAuthor(label: String?, criteria: Int?) {
|
|
||||||
if (filterByAuthorId != criteria) {
|
|
||||||
filterByAuthorId = criteria
|
|
||||||
authorFilter.value = if (criteria != null) {
|
|
||||||
authorFilter.value.copy(
|
|
||||||
selected = true,
|
|
||||||
label = label,
|
|
||||||
closable = true,
|
|
||||||
openable = false
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
authorFilter.value.copy(
|
|
||||||
selected = false,
|
|
||||||
label = null,
|
|
||||||
closable = false,
|
|
||||||
openable = true
|
|
||||||
)
|
|
||||||
}
|
|
||||||
searchSource?.invalidate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun filterBySeries(label: String?, criteria: Int?) {
|
|
||||||
if (filterBySeriesId != criteria) {
|
|
||||||
filterBySeriesId = criteria
|
|
||||||
seriesFilter.value = if (criteria != null) {
|
|
||||||
seriesFilter.value.copy(
|
|
||||||
selected = true,
|
|
||||||
label = label,
|
|
||||||
closable = true,
|
|
||||||
openable = false
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
seriesFilter.value.copy(
|
|
||||||
selected = false,
|
|
||||||
label = null,
|
|
||||||
closable = false,
|
|
||||||
openable = true
|
|
||||||
)
|
|
||||||
}
|
|
||||||
searchSource?.invalidate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun filterByGenre(label: String?, criteria: Int?) {
|
|
||||||
if (filterByGenreId != criteria) {
|
|
||||||
filterByGenreId = criteria
|
|
||||||
genreFilter.value = if (criteria != null) {
|
|
||||||
genreFilter.value.copy(
|
|
||||||
selected = true,
|
|
||||||
label = label,
|
|
||||||
closable = true,
|
|
||||||
openable = false
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
genreFilter.value.copy(
|
|
||||||
selected = false,
|
|
||||||
label = null,
|
|
||||||
closable = false,
|
|
||||||
openable = true
|
|
||||||
)
|
|
||||||
}
|
|
||||||
searchSource?.invalidate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun filterByLanguage(label: String?, criteria: Int?) {
|
|
||||||
if (filterByLanguageId != criteria) {
|
|
||||||
filterByLanguageId = criteria
|
|
||||||
languageFilter.value = if (criteria != null) {
|
|
||||||
languageFilter.value.copy(
|
|
||||||
selected = true,
|
|
||||||
label = label,
|
|
||||||
closable = true,
|
|
||||||
openable = false
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
languageFilter.value.copy(
|
|
||||||
selected = false,
|
|
||||||
label = null,
|
|
||||||
closable = false,
|
|
||||||
openable = true
|
|
||||||
)
|
|
||||||
}
|
|
||||||
searchSource?.invalidate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun buildBookSource(): PagingSource<Int, Book> {
|
private fun buildBookSource(): PagingSource<Int, Book> {
|
||||||
return BookSearchSource(
|
return BookSearchSource(
|
||||||
searchRepository = searchRepository,
|
searchRepository = searchRepository,
|
||||||
|
|
@ -312,6 +271,79 @@ class BookSearchViewModel @Inject constructor(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
class SortAndFilterScope(
|
||||||
|
sortBy: Map<SortType, SortValue?>,
|
||||||
|
isNew: Boolean?,
|
||||||
|
title: String?,
|
||||||
|
author: String?,
|
||||||
|
authorId: Int?,
|
||||||
|
series: String?,
|
||||||
|
seriesId: Int?,
|
||||||
|
genre: String?,
|
||||||
|
genreId: Int?,
|
||||||
|
language: String?,
|
||||||
|
languageId: Int?,
|
||||||
|
) {
|
||||||
|
var sortBy: Map<SortType, SortValue?> = sortBy
|
||||||
|
private set
|
||||||
|
var isNew: Boolean? = isNew
|
||||||
|
private set
|
||||||
|
var title: String? = title
|
||||||
|
private set
|
||||||
|
var author: String? = author
|
||||||
|
private set
|
||||||
|
var authorId: Int? = authorId
|
||||||
|
private set
|
||||||
|
var series: String? = series
|
||||||
|
private set
|
||||||
|
var seriesId: Int? = seriesId
|
||||||
|
private set
|
||||||
|
var genre: String? = genre
|
||||||
|
private set
|
||||||
|
var genreId: Int? = genreId
|
||||||
|
private set
|
||||||
|
var language: String? = language
|
||||||
|
private set
|
||||||
|
var languageId: Int? = languageId
|
||||||
|
private set
|
||||||
|
|
||||||
|
fun sortBy(type: SortType, value: SortValue?) {
|
||||||
|
this.sortBy = mapOf(type to value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sortBy(sortBy: Map<SortType, SortValue?>) {
|
||||||
|
this.sortBy = sortBy
|
||||||
|
}
|
||||||
|
|
||||||
|
fun filterByNew(isNew: Boolean) {
|
||||||
|
this.isNew = if (isNew) true else null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun filterByTitle(title: String?) {
|
||||||
|
this.title = title
|
||||||
|
}
|
||||||
|
|
||||||
|
fun filterByAuthor(author: String?, authorId: Int?) {
|
||||||
|
this.author = author
|
||||||
|
this.authorId = authorId
|
||||||
|
}
|
||||||
|
|
||||||
|
fun filterBySeries(series: String?, seriesId: Int?) {
|
||||||
|
this.series = series
|
||||||
|
this.seriesId = seriesId
|
||||||
|
}
|
||||||
|
|
||||||
|
fun filterByGenre(genre: String?, genreId: Int?) {
|
||||||
|
this.genre = genre
|
||||||
|
this.genreId = genreId
|
||||||
|
}
|
||||||
|
|
||||||
|
fun filterByLanguage(language: String?, languageId: Int?) {
|
||||||
|
this.language = language
|
||||||
|
this.languageId = languageId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val SEARCH_PAGE_SIZE = 20
|
private const val SEARCH_PAGE_SIZE = 20
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,8 @@ import com.google.accompanist.swiperefresh.SwipeRefresh
|
||||||
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
|
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
|
||||||
import com.pixelized.biblib.R
|
import com.pixelized.biblib.R
|
||||||
import com.pixelized.biblib.model.search.FilterType
|
import com.pixelized.biblib.model.search.FilterType
|
||||||
|
import com.pixelized.biblib.model.search.SortType
|
||||||
|
import com.pixelized.biblib.model.search.SortValue
|
||||||
import com.pixelized.biblib.ui.LocalSnackHostState
|
import com.pixelized.biblib.ui.LocalSnackHostState
|
||||||
import com.pixelized.biblib.ui.composable.scaffold.*
|
import com.pixelized.biblib.ui.composable.scaffold.*
|
||||||
import com.pixelized.biblib.ui.navigation.LocalScreenNavHostController
|
import com.pixelized.biblib.ui.navigation.LocalScreenNavHostController
|
||||||
|
|
@ -104,14 +106,16 @@ fun HomeScreen(
|
||||||
DetailBottomSheet(
|
DetailBottomSheet(
|
||||||
bottomDetailState = detailState,
|
bottomDetailState = detailState,
|
||||||
onAuthor = { label: String?, id: Int? ->
|
onAuthor = { label: String?, id: Int? ->
|
||||||
bookViewModel.cleanFilterThen {
|
bookViewModel.source(clean = true) {
|
||||||
filterByAuthor(label = label, criteria = id)
|
filterByAuthor(author = label, authorId = id)
|
||||||
|
sortBy(type = SortType.Title, value = SortValue.ASC)
|
||||||
}
|
}
|
||||||
scope.launch { detailState.collapse() }
|
scope.launch { detailState.collapse() }
|
||||||
},
|
},
|
||||||
onSeries = { label: String?, id: Int? ->
|
onSeries = { label: String?, id: Int? ->
|
||||||
bookViewModel.cleanFilterThen {
|
bookViewModel.source(clean = true) {
|
||||||
filterBySeries(label = label, criteria = id)
|
filterBySeries(series = label, seriesId = id)
|
||||||
|
sortBy(type = SortType.Series, value = SortValue.ASC)
|
||||||
}
|
}
|
||||||
scope.launch { detailState.collapse() }
|
scope.launch { detailState.collapse() }
|
||||||
},
|
},
|
||||||
|
|
@ -123,7 +127,7 @@ fun HomeScreen(
|
||||||
avatar = profileViewModel.avatar,
|
avatar = profileViewModel.avatar,
|
||||||
search = bookViewModel.search,
|
search = bookViewModel.search,
|
||||||
onSearch = {
|
onSearch = {
|
||||||
bookViewModel.filterSearch(criteria = it)
|
bookViewModel.filterByTitle(criteria = it)
|
||||||
},
|
},
|
||||||
onAvatarTap = {
|
onAvatarTap = {
|
||||||
navigation.navigateToProfile()
|
navigation.navigateToProfile()
|
||||||
|
|
@ -149,7 +153,9 @@ fun HomeScreen(
|
||||||
scope.launch {
|
scope.launch {
|
||||||
val result = sortingState.show(sortBy = bookViewModel.sortBy)
|
val result = sortingState.show(sortBy = bookViewModel.sortBy)
|
||||||
if (result is SortBottomSheetResult.ActionPerformed) {
|
if (result is SortBottomSheetResult.ActionPerformed) {
|
||||||
bookViewModel.sort(sortBy = result.sortBy)
|
bookViewModel.source {
|
||||||
|
sortBy(sortBy = result.sortBy)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -182,6 +188,12 @@ fun HomeScreen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CollapseKeyboardOnScrollHandler(
|
||||||
|
largeGridState = largeGridState,
|
||||||
|
smallListState = smallListState,
|
||||||
|
microListState = microListState
|
||||||
|
)
|
||||||
|
|
||||||
LaunchedEffect(key1 = "HomeScreenLaunchedEffect") {
|
LaunchedEffect(key1 = "HomeScreenLaunchedEffect") {
|
||||||
// update
|
// update
|
||||||
launch {
|
launch {
|
||||||
|
|
@ -212,53 +224,58 @@ private fun rememberOnFilter(
|
||||||
return remember {
|
return remember {
|
||||||
{
|
{
|
||||||
if (it.id == FilterType.New) {
|
if (it.id == FilterType.New) {
|
||||||
filterViewModel.filterByNew(criteria = it.selected.not())
|
filterViewModel.source {
|
||||||
|
filterByNew(isNew = it.selected.not())
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (it.selected.not()) {
|
if (it.selected.not()) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
val result = filterState.show(type = it.id)
|
val result = filterState.show(type = it.id)
|
||||||
|
|
||||||
if (result is FilterBottomSheetResult.ActionPerformed) {
|
if (result is FilterBottomSheetResult.ActionPerformed) {
|
||||||
when (it.id) {
|
filterViewModel.source {
|
||||||
FilterType.Author -> filterViewModel.filterByAuthor(
|
when (it.id) {
|
||||||
label = result.filter.filterLabel,
|
FilterType.Author -> filterByAuthor(
|
||||||
criteria = result.filter.filterId,
|
author = result.filter.filterLabel,
|
||||||
)
|
authorId = result.filter.filterId,
|
||||||
FilterType.Series -> filterViewModel.filterBySeries(
|
)
|
||||||
label = result.filter.filterLabel,
|
FilterType.Series -> filterBySeries(
|
||||||
criteria = result.filter.filterId,
|
series = result.filter.filterLabel,
|
||||||
)
|
seriesId = result.filter.filterId,
|
||||||
FilterType.Genre -> filterViewModel.filterByGenre(
|
)
|
||||||
label = result.filter.filterLabel,
|
FilterType.Genre -> filterByGenre(
|
||||||
criteria = result.filter.filterId,
|
genre = result.filter.filterLabel,
|
||||||
)
|
genreId = result.filter.filterId,
|
||||||
FilterType.Language -> filterViewModel.filterByLanguage(
|
)
|
||||||
label = result.filter.filterLabel,
|
FilterType.Language -> filterByLanguage(
|
||||||
criteria = result.filter.filterId,
|
language = result.filter.filterLabel,
|
||||||
)
|
languageId = result.filter.filterId,
|
||||||
else -> Unit
|
)
|
||||||
|
else -> Unit
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
when (it.id) {
|
filterViewModel.source {
|
||||||
FilterType.Author -> filterViewModel.filterByAuthor(
|
when (it.id) {
|
||||||
label = null,
|
FilterType.Author -> filterByAuthor(
|
||||||
criteria = null,
|
author = null,
|
||||||
)
|
authorId = null,
|
||||||
FilterType.Series -> filterViewModel.filterBySeries(
|
)
|
||||||
label = null,
|
FilterType.Series -> filterBySeries(
|
||||||
criteria = null,
|
series = null,
|
||||||
)
|
seriesId = null,
|
||||||
FilterType.Genre -> filterViewModel.filterByGenre(
|
)
|
||||||
label = null,
|
FilterType.Genre -> filterByGenre(
|
||||||
criteria = null,
|
genre = null,
|
||||||
)
|
genreId = null,
|
||||||
FilterType.Language -> filterViewModel.filterByLanguage(
|
)
|
||||||
label = null,
|
FilterType.Language -> filterByLanguage(
|
||||||
criteria = null,
|
language = null,
|
||||||
)
|
languageId = null,
|
||||||
else -> Unit
|
)
|
||||||
|
else -> Unit
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -569,6 +586,26 @@ private fun HomeToolbar(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalComposeUiApi::class)
|
||||||
|
@Composable
|
||||||
|
private fun CollapseKeyboardOnScrollHandler(
|
||||||
|
largeGridState: LazyGridState,
|
||||||
|
smallListState: LazyListState,
|
||||||
|
microListState: LazyListState,
|
||||||
|
) {
|
||||||
|
val keyboard = LocalSoftwareKeyboardController.current
|
||||||
|
val focus = LocalFocusManager.current
|
||||||
|
|
||||||
|
if (
|
||||||
|
largeGridState.isScrollInProgress
|
||||||
|
|| smallListState.isScrollInProgress
|
||||||
|
|| microListState.isScrollInProgress
|
||||||
|
) {
|
||||||
|
focus.clearFocus()
|
||||||
|
keyboard?.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
|
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
|
|
|
||||||
|
|
@ -91,24 +91,21 @@ fun DetailScreen(
|
||||||
content = {
|
content = {
|
||||||
if (detailState.bottomSheetState.isVisible) {
|
if (detailState.bottomSheetState.isVisible) {
|
||||||
DetailScreenContent(
|
DetailScreenContent(
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.verticalScroll(rememberScrollState())
|
|
||||||
.padding(all = 16.dp)
|
|
||||||
.systemBarsPadding(),
|
|
||||||
book = detail,
|
book = detail,
|
||||||
onAuthor = { onAuthor(detail.author, detail.authorId) },
|
onAuthor = { onAuthor(it.name, it.id) },
|
||||||
onSeries = { onSeries(detail.series, detail.seriesId) },
|
onSeries = { onSeries(it.name, it.id) },
|
||||||
onMobi = { },
|
onMobi = { },
|
||||||
onEpub = { },
|
onEpub = { },
|
||||||
onSend = {
|
onSend = {
|
||||||
send(
|
scope.launch {
|
||||||
context = context,
|
send(
|
||||||
profileViewModel = profileViewModel,
|
context = context,
|
||||||
snackBarHost = snackBarHost,
|
profileViewModel = profileViewModel,
|
||||||
emailSheetState = emailSheetState,
|
snackBarHost = snackBarHost,
|
||||||
user = profileViewModel.user
|
emailSheetState = emailSheetState,
|
||||||
)
|
user = profileViewModel.user
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
DetailScreenSendLoader(
|
DetailScreenSendLoader(
|
||||||
|
|
|
||||||
|
|
@ -11,19 +11,17 @@ import androidx.compose.material.Icon
|
||||||
import androidx.compose.material.MaterialTheme
|
import androidx.compose.material.MaterialTheme
|
||||||
import androidx.compose.material.Text
|
import androidx.compose.material.Text
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Download
|
|
||||||
import androidx.compose.material.icons.filled.Send
|
import androidx.compose.material.icons.filled.Send
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.runtime.Immutable
|
|
||||||
import androidx.compose.runtime.Stable
|
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
|
import androidx.compose.ui.layout.onSizeChanged
|
||||||
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
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.compose.ui.unit.dp
|
||||||
import androidx.core.text.HtmlCompat
|
import androidx.core.text.HtmlCompat
|
||||||
import androidx.core.text.toSpannable
|
import androidx.core.text.toSpannable
|
||||||
|
|
@ -36,7 +34,6 @@ import com.pixelized.biblib.utils.extention.bibLib
|
||||||
import com.pixelized.biblib.utils.extention.placeholder
|
import com.pixelized.biblib.utils.extention.placeholder
|
||||||
import com.skydoves.landscapist.CircularReveal
|
import com.skydoves.landscapist.CircularReveal
|
||||||
import com.skydoves.landscapist.glide.GlideImage
|
import com.skydoves.landscapist.glide.GlideImage
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
|
|
@ -45,25 +42,40 @@ data class BookDetailUio(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val title: String,
|
val title: String,
|
||||||
val authorId: Int?,
|
val authorId: Int?,
|
||||||
val author: String,
|
val authors: List<AuthorUio>,
|
||||||
val rating: Float,
|
val rating: Float,
|
||||||
val language: String,
|
val language: String,
|
||||||
val date: String?,
|
val date: String?,
|
||||||
val seriesId: Int?,
|
val seriesId: Int?,
|
||||||
val series: String?,
|
val series: SeriesUio?,
|
||||||
val description: String,
|
val description: String,
|
||||||
val cover: String,
|
val cover: String,
|
||||||
val placeHolder: Boolean,
|
val placeHolder: Boolean,
|
||||||
) : Serializable {
|
) : Serializable {
|
||||||
|
@Stable
|
||||||
|
@Immutable
|
||||||
|
data class AuthorUio(
|
||||||
|
val id: Int,
|
||||||
|
val name: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
@Immutable
|
||||||
|
data class SeriesUio(
|
||||||
|
val id: Int,
|
||||||
|
val name: String,
|
||||||
|
val label: String,
|
||||||
|
)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun preview() = BookDetailUio(
|
fun preview() = BookDetailUio(
|
||||||
id = 90,
|
id = 90,
|
||||||
title = "Foundation",
|
title = "Foundation",
|
||||||
authorId = null,
|
authorId = null,
|
||||||
author = "Asimov",
|
authors = listOf(AuthorUio(id = 0, name = "Asimov")),
|
||||||
date = "1951",
|
date = "1951",
|
||||||
seriesId = null,
|
seriesId = null,
|
||||||
series = "Foundation - 1",
|
series = SeriesUio(id = 0, "Foundation", label = "Foundation - 1"),
|
||||||
description = "En ce début de treizième millénaire, l'Empire n'a jamais été aussi puissant, aussi étendu à travers toute la galaxie. C'est dans sa capitale, Trantor, que l'éminent savant Hari Seldon invente la psychohistoire, une science nouvelle permettant de prédire l'avenir. Grâce à elle, Seldon prévoit l'effondrement de l'Empire d'ici cinq siècles, suivi d'une ère de ténèbres de trente mille ans. Réduire cette période à mille ans est peut-être possible, à condition de mener à terme son projet : la Fondation, chargée de rassembler toutes les connaissances humaines. Une entreprise visionnaire qui rencontre de nombreux et puissants détracteurs...",
|
description = "En ce début de treizième millénaire, l'Empire n'a jamais été aussi puissant, aussi étendu à travers toute la galaxie. C'est dans sa capitale, Trantor, que l'éminent savant Hari Seldon invente la psychohistoire, une science nouvelle permettant de prédire l'avenir. Grâce à elle, Seldon prévoit l'effondrement de l'Empire d'ici cinq siècles, suivi d'une ère de ténèbres de trente mille ans. Réduire cette période à mille ans est peut-être possible, à condition de mener à terme son projet : la Fondation, chargée de rassembler toutes les connaissances humaines. Une entreprise visionnaire qui rencontre de nombreux et puissants détracteurs...",
|
||||||
rating = 4.5f,
|
rating = 4.5f,
|
||||||
language = "Français",
|
language = "Français",
|
||||||
|
|
@ -77,162 +89,146 @@ data class BookDetailUio(
|
||||||
fun DetailScreenContent(
|
fun DetailScreenContent(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
book: BookDetailUio,
|
book: BookDetailUio,
|
||||||
onAuthor: () -> Unit,
|
onAuthor: (BookDetailUio.AuthorUio) -> Unit,
|
||||||
onSeries: () -> Unit,
|
onSeries: (BookDetailUio.SeriesUio) -> Unit,
|
||||||
onMobi: () -> Unit,
|
onMobi: () -> Unit,
|
||||||
onEpub: () -> Unit,
|
onEpub: () -> Unit,
|
||||||
onSend: suspend () -> Unit,
|
onSend: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val scope = rememberCoroutineScope()
|
val density = LocalDensity.current
|
||||||
|
val fabHeight = remember { mutableStateOf(0.dp) }
|
||||||
|
|
||||||
AnimatedDelayer(
|
AnimatedDelayer(
|
||||||
targetState = book.id,
|
targetState = book.id,
|
||||||
) {
|
) {
|
||||||
Column(
|
Box {
|
||||||
modifier = modifier,
|
Column(
|
||||||
) {
|
|
||||||
GlideImage(
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxSize()
|
||||||
.padding(vertical = 16.dp)
|
.verticalScroll(rememberScrollState())
|
||||||
.height(MaterialTheme.bibLib.dimen.detail.cover),
|
.padding(all = 16.dp)
|
||||||
previewPlaceholder = R.drawable.ic_fondatoin_cover,
|
.systemBarsPadding(),
|
||||||
circularReveal = CircularReveal(duration = 1000),
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
contentScale = ContentScale.FillHeight,
|
|
||||||
imageModel = book.cover,
|
|
||||||
)
|
|
||||||
|
|
||||||
Row(modifier = Modifier.padding(vertical = 16.dp)) {
|
|
||||||
AnimatedOffset(
|
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
) {
|
|
||||||
Button(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
onClick = onMobi,
|
|
||||||
) {
|
|
||||||
Icon(imageVector = Icons.Default.Download, contentDescription = null)
|
|
||||||
Spacer(modifier = Modifier.width(4.dp))
|
|
||||||
Text(text = stringResource(id = R.string.action_mobi))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.padding(all = 4.dp))
|
|
||||||
|
|
||||||
AnimatedOffset(
|
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
) {
|
|
||||||
Button(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
onClick = onEpub,
|
|
||||||
) {
|
|
||||||
Icon(imageVector = Icons.Default.Download, contentDescription = null)
|
|
||||||
Spacer(modifier = Modifier.width(4.dp))
|
|
||||||
Text(text = stringResource(id = R.string.action_epub))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.padding(all = 4.dp))
|
|
||||||
|
|
||||||
AnimatedOffset(
|
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
) {
|
|
||||||
Button(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
onClick = { scope.launch { onSend() } },
|
|
||||||
) {
|
|
||||||
Icon(imageVector = Icons.Default.Send, contentDescription = "")
|
|
||||||
Spacer(modifier = Modifier.width(4.dp))
|
|
||||||
Text(text = stringResource(id = R.string.action_send))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AnimatedOffset(
|
|
||||||
modifier = Modifier
|
|
||||||
.align(alignment = Alignment.CenterHorizontally)
|
|
||||||
.padding(bottom = 4.dp),
|
|
||||||
) {
|
) {
|
||||||
Text(
|
GlideImage(
|
||||||
style = MaterialTheme.typography.h5,
|
|
||||||
color = MaterialTheme.colors.onSurface,
|
|
||||||
text = book.title,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
AnimatedOffset(
|
|
||||||
modifier = Modifier
|
|
||||||
.align(alignment = Alignment.CenterHorizontally)
|
|
||||||
.padding(bottom = 16.dp),
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
modifier = Modifier.clickable(onClick = onAuthor),
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
style = MaterialTheme.typography.h6,
|
|
||||||
color = MaterialTheme.colors.onSurface,
|
|
||||||
text = book.author,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Row(
|
|
||||||
modifier = Modifier.padding(bottom = 8.dp),
|
|
||||||
) {
|
|
||||||
AnimatedOffset(modifier = Modifier.weight(1f)) {
|
|
||||||
TitleLabel(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
title = stringResource(id = R.string.detail_rating),
|
|
||||||
label = book.rating.toString(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
AnimatedOffset(modifier = Modifier.weight(1f)) {
|
|
||||||
TitleLabel(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
title = stringResource(id = R.string.detail_language),
|
|
||||||
label = book.language,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
AnimatedOffset(modifier = Modifier.weight(1f)) {
|
|
||||||
TitleLabel(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
title = stringResource(id = R.string.detail_release),
|
|
||||||
label = book.date ?: "-",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AnimatedOffset(
|
|
||||||
modifier = Modifier.padding(bottom = 16.dp),
|
|
||||||
) {
|
|
||||||
TitleLabel(
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.clickable(onClick = onSeries),
|
.padding(vertical = 16.dp)
|
||||||
title = stringResource(id = R.string.detail_series),
|
.height(MaterialTheme.bibLib.dimen.detail.cover),
|
||||||
label = book.series ?: "-",
|
previewPlaceholder = R.drawable.ic_fondatoin_cover,
|
||||||
|
circularReveal = CircularReveal(duration = 1000),
|
||||||
|
contentScale = ContentScale.FillHeight,
|
||||||
|
imageModel = book.cover,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
AnimatedOffset {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
style = MaterialTheme.typography.h5,
|
||||||
|
color = MaterialTheme.colors.onSurface,
|
||||||
|
text = book.title,
|
||||||
|
)
|
||||||
|
book.series?.let { series ->
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.clickable(onClick = { onSeries(series) }),
|
||||||
|
style = MaterialTheme.typography.body1,
|
||||||
|
color = MaterialTheme.colors.onSurface,
|
||||||
|
text = series.label,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimatedOffset {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
) {
|
||||||
|
book.authors.forEach { author ->
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.clickable(onClick = { onAuthor(author) }),
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
style = MaterialTheme.typography.h6,
|
||||||
|
color = MaterialTheme.colors.onSurface,
|
||||||
|
text = author.name,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
AnimatedOffset(modifier = Modifier.weight(1f)) {
|
||||||
|
TitleLabel(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
title = stringResource(id = R.string.detail_rating),
|
||||||
|
label = book.rating.toString(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimatedOffset(modifier = Modifier.weight(1f)) {
|
||||||
|
TitleLabel(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
title = stringResource(id = R.string.detail_language),
|
||||||
|
label = book.language,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimatedOffset(modifier = Modifier.weight(1f)) {
|
||||||
|
TitleLabel(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
title = stringResource(id = R.string.detail_release),
|
||||||
|
label = book.date ?: "-",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimatedOffset {
|
||||||
|
SpannedText(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.placeholder(
|
||||||
|
shape = MaterialTheme.bibLib.shapes.base.medium,
|
||||||
|
visible = { book.placeHolder },
|
||||||
|
),
|
||||||
|
style = MaterialTheme.typography.body1,
|
||||||
|
color = MaterialTheme.colors.onSurface,
|
||||||
|
text = HtmlCompat.fromHtml(
|
||||||
|
book.description,
|
||||||
|
HtmlCompat.FROM_HTML_MODE_COMPACT,
|
||||||
|
).toSpannable(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(height = fabHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimatedOffset {
|
AnimatedOffset(
|
||||||
SpannedText(
|
modifier = Modifier
|
||||||
modifier = Modifier
|
.align(Alignment.BottomEnd)
|
||||||
.fillMaxWidth()
|
.padding(all = 16.dp)
|
||||||
.placeholder(
|
.systemBarsPadding()
|
||||||
shape = MaterialTheme.bibLib.shapes.base.medium,
|
.onSizeChanged { fabHeight.value = with(density) { it.height.toDp() } },
|
||||||
visible = { book.placeHolder },
|
) {
|
||||||
),
|
Button(
|
||||||
style = MaterialTheme.typography.body1,
|
onClick = onSend,
|
||||||
color = MaterialTheme.colors.onSurface,
|
) {
|
||||||
text = HtmlCompat.fromHtml(
|
Icon(imageVector = Icons.Default.Send, contentDescription = "")
|
||||||
book.description,
|
Spacer(modifier = Modifier.width(4.dp))
|
||||||
HtmlCompat.FROM_HTML_MODE_COMPACT,
|
Text(text = stringResource(id = R.string.action_send))
|
||||||
).toSpannable(),
|
}
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun Spacer(height: State<Dp>) {
|
||||||
|
Spacer(modifier = Modifier.height(height = height.value))
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun TitleLabel(
|
private fun TitleLabel(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
|
@ -245,13 +241,12 @@ private fun TitleLabel(
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
style = MaterialTheme.typography.body2,
|
style = MaterialTheme.typography.body2,
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
color = MaterialTheme.colors.onSurface,
|
color = MaterialTheme.colors.onSurface,
|
||||||
text = title,
|
text = title,
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
style = MaterialTheme.typography.body1,
|
style = MaterialTheme.typography.body1,
|
||||||
textAlign = TextAlign.Center,
|
fontWeight = FontWeight.Bold,
|
||||||
color = MaterialTheme.colors.onSurface,
|
color = MaterialTheme.colors.onSurface,
|
||||||
text = label,
|
text = label,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ fun Book.toMicroThumbnailUio(
|
||||||
cover = "${coverBaseUrl}/$id.jpg",
|
cover = "${coverBaseUrl}/$id.jpg",
|
||||||
title = title,
|
title = title,
|
||||||
author = author.joinToString { it.name },
|
author = author.joinToString { it.name },
|
||||||
series = toLabel(series) ?: "",
|
series = series?.let { toLabel(it) } ?: "",
|
||||||
isNew = isNew,
|
isNew = isNew,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -55,21 +55,26 @@ fun Book.toDetailUio(
|
||||||
id = id,
|
id = id,
|
||||||
title = title,
|
title = title,
|
||||||
authorId = author.firstOrNull()?.id,
|
authorId = author.firstOrNull()?.id,
|
||||||
author = author.joinToString { it.name },
|
authors = author.map { BookDetailUio.AuthorUio(id = it.id, it.name) },
|
||||||
rating = rating?.toFloat() ?: 0.0f,
|
rating = rating?.toFloat() ?: 0.0f,
|
||||||
language = language?.displayLanguage?.capitalize() ?: "",
|
language = language?.displayLanguage?.capitalize() ?: "",
|
||||||
date = releaseDate.shortDate(),
|
date = releaseDate.shortDate(),
|
||||||
seriesId = series?.id,
|
seriesId = series?.id,
|
||||||
series = toLabel(series),
|
series = series?.let {
|
||||||
|
BookDetailUio.SeriesUio(
|
||||||
|
id = it.id,
|
||||||
|
name = it.name,
|
||||||
|
label = toLabel(series)
|
||||||
|
)
|
||||||
|
},
|
||||||
description = synopsis ?: "",
|
description = synopsis ?: "",
|
||||||
cover = "${coverBaseUrl}/$id.jpg",
|
cover = "${coverBaseUrl}/$id.jpg",
|
||||||
placeHolder = placeHolder,
|
placeHolder = placeHolder,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun Book.toLabel(series: Series?): String? {
|
fun Book.toLabel(series: Series): String {
|
||||||
return when {
|
return when {
|
||||||
series?.name != null && seriesIndex != null -> "${series.name} - $seriesIndex"
|
seriesIndex != null -> "${series.name} - $seriesIndex"
|
||||||
series?.name != null -> series.name
|
else -> series.name
|
||||||
else -> null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -46,7 +46,6 @@
|
||||||
<string name="detail_language">Langue</string>
|
<string name="detail_language">Langue</string>
|
||||||
<string name="detail_release">Publication</string>
|
<string name="detail_release">Publication</string>
|
||||||
<string name="detail_genre">Genre</string>
|
<string name="detail_genre">Genre</string>
|
||||||
<string name="detail_series">Séries</string>
|
|
||||||
|
|
||||||
<string name="detail_option_mail">Envoyer cet eBook à :</string>
|
<string name="detail_option_mail">Envoyer cet eBook à :</string>
|
||||||
<string name="detail_option_format">Format de l\'eBook :</string>
|
<string name="detail_option_format">Format de l\'eBook :</string>
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,6 @@
|
||||||
<string name="detail_language">Language</string>
|
<string name="detail_language">Language</string>
|
||||||
<string name="detail_release">Release</string>
|
<string name="detail_release">Release</string>
|
||||||
<string name="detail_genre">Genre</string>
|
<string name="detail_genre">Genre</string>
|
||||||
<string name="detail_series">Series</string>
|
|
||||||
|
|
||||||
<string name="detail_option_mail">Send this eBook to:</string>
|
<string name="detail_option_mail">Send this eBook to:</string>
|
||||||
<string name="detail_option_format">eBook format:</string>
|
<string name="detail_option_format">eBook format:</string>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue