Clean search filtering.

This commit is contained in:
Thomas Andres Gomez 2022-10-16 14:51:04 +02:00
parent e176b6827a
commit e69af172af
20 changed files with 177 additions and 219 deletions

View file

@ -0,0 +1,13 @@
package com.pixelized.biblib.model.search
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
@Stable
@Immutable
enum class SearchType {
Author,
Series,
Genre,
Language;
}

View file

@ -0,0 +1,14 @@
package com.pixelized.biblib.model.search
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
@Stable
@Immutable
enum class SortType {
Book,
Author,
Series,
Genre,
Language;
}

View file

@ -1,6 +1,7 @@
package com.pixelized.biblib.repository.search
import com.pixelized.biblib.model.book.*
import com.pixelized.biblib.model.search.SortType
interface ISearchRepository {

View file

@ -7,6 +7,7 @@ import com.pixelized.biblib.database.crossref.BookGenreCrossRef
import com.pixelized.biblib.database.data.*
import com.pixelized.biblib.database.relation.BookRelation
import com.pixelized.biblib.model.book.*
import com.pixelized.biblib.model.search.SortType
import javax.inject.Inject
class SearchRepository @Inject constructor(
@ -42,6 +43,7 @@ class SearchRepository @Inject constructor(
query += args.where(argument = languageId) {
BookDbo.run { "$TABLE.$LANGUAGE_ID LIKE ?" }
}
query += BookDbo.run { " ORDER BY $TABLE.$SORT" }
// Limit and Offset the query.
query += " LIMIT $limit OFFSET $offset;"
// compute the query
@ -57,6 +59,7 @@ class SearchRepository @Inject constructor(
val query = AuthorDbo.run {
"SELECT $TABLE.* FROM $TABLE" +
args.where(argument = search) { "$TABLE.$NAME LIKE '%'||?||'%'" } +
" ORDER BY $TABLE.$SORT" +
" LIMIT $limit OFFSET $offset;"
}
// compute the query
@ -72,6 +75,7 @@ class SearchRepository @Inject constructor(
val query = GenreDbo.run {
"SELECT $TABLE.* FROM $TABLE" +
args.where(argument = search) { "$TABLE.$NAME LIKE '%'||?||'%'" } +
" ORDER BY $TABLE.$NAME" +
" LIMIT $limit OFFSET $offset;"
}
// compute the query
@ -87,6 +91,7 @@ class SearchRepository @Inject constructor(
val query = SeriesDbo.run {
"SELECT $TABLE.* FROM $TABLE" +
args.where(argument = search) { "$TABLE.$NAME LIKE '%'||?||'%'" } +
" ORDER BY $TABLE.$SORT" +
" LIMIT $limit OFFSET $offset;"
}
// compute the query
@ -102,6 +107,7 @@ class SearchRepository @Inject constructor(
val query = LanguageDbo.run {
"SELECT $TABLE.* FROM $TABLE" +
args.where(argument = search) { "$TABLE.$NAME LIKE '%'||?||'%'" } +
" ORDER BY $TABLE.$NAME" +
" LIMIT $limit OFFSET $offset;"
}
// compute the query

View file

@ -12,7 +12,8 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.hilt.navigation.compose.hiltViewModel
import com.pixelized.biblib.ui.screen.home.page.search.FilterSearchPage
import com.pixelized.biblib.model.search.SearchType
import com.pixelized.biblib.ui.screen.home.page.search.bottom.FilterSearchPage
import com.pixelized.biblib.ui.screen.home.page.search.viewModel.BookSearchViewModel
import com.pixelized.biblib.ui.theme.color.ShadowPalette
import kotlinx.coroutines.launch
@ -20,14 +21,14 @@ import kotlinx.coroutines.launch
val LocalBookSearchViewModel = staticCompositionLocalOf<BookSearchViewModel> {
error("SearchViewModel is not ready yet")
}
val LocalCategorySearchBottomSheetState = staticCompositionLocalOf<SearchBottomSheetState> {
error("BottomSearchState is not ready yet")
val LocalFilterSearchBottomSheetState = staticCompositionLocalOf<FilterSearchBottomSheetState> {
error("FilterSearchBottomSheetState is not ready yet")
}
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun CategorySearchBottomSheet(
state: SearchBottomSheetState = rememberSearchBottomSheetState(),
fun SearchOptionBottomSheet(
filterState: FilterSearchBottomSheetState = rememberSearchBottomSheetState(),
bookSearchViewModel: BookSearchViewModel = hiltViewModel(),
content: @Composable () -> Unit,
) {
@ -35,23 +36,23 @@ fun CategorySearchBottomSheet(
CompositionLocalProvider(
LocalBookSearchViewModel provides bookSearchViewModel,
LocalCategorySearchBottomSheetState provides state,
LocalFilterSearchBottomSheetState provides filterState,
) {
ModalBottomSheetLayout(
modifier = Modifier.statusBarsPadding(),
scrimColor = ShadowPalette.scrim,
sheetState = state.bottomSheetState,
sheetState = filterState.bottomSheetState,
sheetContent = {
FilterSearchPage(
focusRequester = state.focusRequester,
focusRequester = filterState.focusRequester,
)
},
content = content,
)
BackHandler(state.bottomSheetState.isVisible) {
BackHandler(filterState.bottomSheetState.isVisible) {
scope.launch {
state.collapse()
filterState.collapse()
}
}
}
@ -65,12 +66,12 @@ fun rememberSearchBottomSheetState(
initialValue = Hidden,
skipHalfExpanded = true,
),
): SearchBottomSheetState {
): FilterSearchBottomSheetState {
val filter = rememberSaveable(bottomSheetState) {
mutableStateOf<SearchFilter?>(null)
mutableStateOf<SearchType?>(null)
}
return remember(bottomSheetState) {
SearchBottomSheetState(
FilterSearchBottomSheetState(
bottomSheetState = bottomSheetState,
focusRequester = focusRequester,
filter = filter,
@ -80,15 +81,15 @@ fun rememberSearchBottomSheetState(
@OptIn(ExperimentalMaterialApi::class)
@Stable
class SearchBottomSheetState constructor(
class FilterSearchBottomSheetState constructor(
val bottomSheetState: ModalBottomSheetState,
val focusRequester: FocusRequester,
filter: MutableState<SearchFilter?>,
filter: MutableState<SearchType?>,
) {
var filter: SearchFilter? by filter
var filter: SearchType? by filter
private set
suspend fun expandSearch(filter: SearchFilter?) {
suspend fun expandSearch(filter: SearchType?) {
this.filter = filter
bottomSheetState.show()
focusRequester.requestFocus()
@ -97,13 +98,4 @@ class SearchBottomSheetState constructor(
suspend fun collapse() {
bottomSheetState.hide()
}
}
@Stable
@Immutable
enum class SearchFilter {
Author,
Series,
Genre,
Language;
}

View file

@ -57,7 +57,7 @@ fun HomeScreen(
val focusRequester: FocusRequester = remember { FocusRequester() }
DetailBottomSheet {
CategorySearchBottomSheet {
SearchOptionBottomSheet {
SearchScaffold(
modifier = Modifier.statusBarsPadding(),
state = searchScaffoldState,
@ -138,7 +138,7 @@ fun HomeScreen(
},
)
val bottomSearchState = LocalCategorySearchBottomSheetState.current
val bottomSearchState = LocalFilterSearchBottomSheetState.current
val bottomDetailState = LocalDetailBottomSheetState.current
BackHandler(
enabled = searchScaffoldState.isExpended || bottomSearchState.bottomSheetState.isVisible || bottomDetailState.bottomSheetState.isVisible

View file

@ -2,19 +2,17 @@ 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.core.Spring
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.spring
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.*
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.stringResource
@ -23,33 +21,31 @@ import androidx.paging.PagingData
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.items
import com.pixelized.biblib.R
import com.pixelized.biblib.ui.scaffold.LocalCategorySearchBottomSheetState
import com.pixelized.biblib.ui.scaffold.LocalDetailBottomSheetState
import com.pixelized.biblib.model.search.SearchType
import com.pixelized.biblib.ui.scaffold.LocalBookSearchViewModel
import com.pixelized.biblib.ui.scaffold.SearchFilter
import com.pixelized.biblib.ui.scaffold.LocalDetailBottomSheetState
import com.pixelized.biblib.ui.scaffold.LocalFilterSearchBottomSheetState
import com.pixelized.biblib.ui.screen.home.common.item.MicroBookThumbnail
import com.pixelized.biblib.ui.screen.home.common.item.MicroBookThumbnailUio
import com.pixelized.biblib.ui.screen.home.page.search.item.SearchFilterList
import com.pixelized.biblib.ui.screen.home.page.search.item.SearchFilterUio
import com.pixelized.biblib.ui.screen.home.page.search.item.searchFilterPreviewItems
import com.pixelized.biblib.ui.screen.home.page.search.viewModel.BookSearchViewModel
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.isLoading
import com.pixelized.biblib.utils.extention.navigationBarsHeight
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.launch
@OptIn(ExperimentalComposeUiApi::class)
@OptIn(ExperimentalComposeUiApi::class, ExperimentalMaterialApi::class)
@Composable
fun SearchPage(
bookSearchViewModel: BookSearchViewModel = LocalBookSearchViewModel.current,
) {
val keyboard = LocalSoftwareKeyboardController.current
val detail = LocalDetailBottomSheetState.current
val search = LocalCategorySearchBottomSheetState.current
val filter = LocalFilterSearchBottomSheetState.current
val focus = LocalFocusManager.current
val scope = rememberCoroutineScope()
@ -58,8 +54,17 @@ fun SearchPage(
search = bookSearchViewModel.paging,
filters = filters(bookSearchViewModel),
onFilter = {
scope.launch {
search.expandSearch(it.id)
if (it.value == null) {
scope.launch {
filter.expandSearch(it.id)
}
} else {
when (it.id) {
SearchType.Author -> bookSearchViewModel.filterAuthor(null)
SearchType.Series -> bookSearchViewModel.filterSeries(null)
SearchType.Genre -> bookSearchViewModel.filterGenre(null)
SearchType.Language -> bookSearchViewModel.filterLanguage(null)
}
}
},
onDetail = {
@ -75,10 +80,10 @@ fun SearchPage(
@Composable
private fun SearchPageContent(
modifier: Modifier = Modifier,
search: Flow<PagingData<MicroBookThumbnailUio>>,
search: Flow<PagingData<MicroBookThumbnailUio>> = emptyFlow(),
filters: List<SearchFilterUio> = emptyList(),
onFilter: (filter: SearchFilterUio) -> Unit,
onDetail: (item: MicroBookThumbnailUio) -> Unit,
onFilter: (filter: SearchFilterUio) -> Unit = default<SearchFilterUio>(),
onDetail: (item: MicroBookThumbnailUio) -> Unit = default<MicroBookThumbnailUio>(),
) {
val items = search.collectAsLazyPagingItems()
@ -105,29 +110,6 @@ private fun SearchPageContent(
)
}
}
SearchLoader(
modifier = Modifier
.fillMaxWidth()
.height(MaterialTheme.bibLib.dimen.dp2),
isLoading = { items.isLoading },
)
}
@Composable
private fun SearchLoader(
modifier: Modifier = Modifier,
isLoading: () -> Boolean,
) {
val alpha by animateFloatAsState(
animationSpec = spring(stiffness = Spring.StiffnessLow),
targetValue = if (isLoading()) 1f else 0f
)
LinearProgressIndicator(
modifier = modifier.alpha(alpha),
color = MaterialTheme.colors.primary,
backgroundColor = Color.Transparent,
)
}
@Composable
@ -135,22 +117,22 @@ private fun filters(
bookSearchViewModel: BookSearchViewModel,
) = listOf(
SearchFilterUio(
id = SearchFilter.Author,
id = SearchType.Author,
label = stringResource(id = R.string.search_filter_author),
value = bookSearchViewModel.author?.filter,
),
SearchFilterUio(
id = SearchFilter.Genre,
id = SearchType.Genre,
label = stringResource(id = R.string.search_filter_genre),
value = bookSearchViewModel.genre?.filter,
),
SearchFilterUio(
id = SearchFilter.Series,
label = stringResource(id = R.string.search_filter_serie),
id = SearchType.Series,
label = stringResource(id = R.string.search_filter_series),
value = bookSearchViewModel.series?.filter,
),
SearchFilterUio(
id = SearchFilter.Language,
id = SearchType.Language,
label = stringResource(id = R.string.search_filter_language),
value = bookSearchViewModel.language?.filter,
),
@ -163,10 +145,6 @@ private fun SearchPageContentPreview() {
BibLibTheme {
SearchPageContent(
modifier = Modifier.fillMaxWidth(),
search = emptyFlow(),
filters = searchFilterPreviewItems(),
onFilter = default<SearchFilterUio>(),
onDetail = default<MicroBookThumbnailUio>(),
)
}
}

View file

@ -1,4 +1,4 @@
package com.pixelized.biblib.ui.screen.home.page.search
package com.pixelized.biblib.ui.screen.home.page.search.bottom
import android.content.res.Configuration.UI_MODE_NIGHT_NO
import android.content.res.Configuration.UI_MODE_NIGHT_YES
@ -21,9 +21,9 @@ import androidx.paging.PagingData
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.items
import com.pixelized.biblib.R
import com.pixelized.biblib.model.search.SearchType
import com.pixelized.biblib.ui.scaffold.LocalBookSearchViewModel
import com.pixelized.biblib.ui.scaffold.LocalCategorySearchBottomSheetState
import com.pixelized.biblib.ui.scaffold.SearchFilter
import com.pixelized.biblib.ui.scaffold.LocalFilterSearchBottomSheetState
import com.pixelized.biblib.ui.screen.home.page.search.viewModel.*
import com.pixelized.biblib.ui.theme.BibLibTheme
import com.pixelized.biblib.utils.extention.bibLib
@ -48,16 +48,16 @@ fun FilterSearchPage(
focusRequester: FocusRequester = FocusRequester(),
) {
val bookSearchViewModel = LocalBookSearchViewModel.current
val bottomSearchState = LocalCategorySearchBottomSheetState.current
val bottomSearchState = LocalFilterSearchBottomSheetState.current
val scope = rememberCoroutineScope()
val filter = bottomSearchState.filter
val viewModel: IFilterViewModel? = remember(filter) {
when (filter) {
SearchFilter.Author -> authorFilterViewModel
SearchFilter.Series -> seriesFilterViewModel
SearchFilter.Genre -> genreViewModel
SearchFilter.Language -> languageViewModel
SearchType.Author -> authorFilterViewModel
SearchType.Series -> seriesFilterViewModel
SearchType.Genre -> genreViewModel
SearchType.Language -> languageViewModel
else -> null
}
}
@ -72,10 +72,10 @@ fun FilterSearchPage(
},
onClose = {
when (filter) {
SearchFilter.Author -> bookSearchViewModel.filterAuthor(null)
SearchFilter.Series -> bookSearchViewModel.filterSeries(null)
SearchFilter.Genre -> bookSearchViewModel.filterGenre(null)
SearchFilter.Language -> bookSearchViewModel.filterLanguage(null)
SearchType.Author -> bookSearchViewModel.filterAuthor(null)
SearchType.Series -> bookSearchViewModel.filterSeries(null)
SearchType.Genre -> bookSearchViewModel.filterGenre(null)
SearchType.Language -> bookSearchViewModel.filterLanguage(null)
else -> Unit
}
scope.launch {
@ -84,10 +84,10 @@ fun FilterSearchPage(
},
onFilter = {
when (filter) {
SearchFilter.Author -> bookSearchViewModel.filterAuthor(it)
SearchFilter.Series -> bookSearchViewModel.filterSeries(it)
SearchFilter.Genre -> bookSearchViewModel.filterGenre(it)
SearchFilter.Language -> bookSearchViewModel.filterLanguage(it)
SearchType.Author -> bookSearchViewModel.filterAuthor(it)
SearchType.Series -> bookSearchViewModel.filterSeries(it)
SearchType.Genre -> bookSearchViewModel.filterGenre(it)
SearchType.Language -> bookSearchViewModel.filterLanguage(it)
else -> Unit
}
scope.launch {
@ -100,7 +100,7 @@ fun FilterSearchPage(
@Composable
fun CategorySearchPageContent(
focusRequester: FocusRequester = FocusRequester(),
filter: SearchFilter?,
filter: SearchType?,
search: () -> String?,
paging: () -> Flow<PagingData<FilterUio>> = { emptyFlow() },
onSearchUpdate: (String) -> Unit = default<String>(),
@ -121,10 +121,10 @@ fun CategorySearchPageContent(
style = MaterialTheme.typography.h6,
color = MaterialTheme.colors.onSurface,
text = when (filter) {
SearchFilter.Author -> stringResource(id = R.string.search_filter_author)
SearchFilter.Genre -> stringResource(id = R.string.search_filter_genre)
SearchFilter.Series -> stringResource(id = R.string.search_filter_serie)
SearchFilter.Language -> stringResource(id = R.string.search_filter_language)
SearchType.Author -> stringResource(id = R.string.search_filter_author)
SearchType.Genre -> stringResource(id = R.string.search_filter_genre)
SearchType.Series -> stringResource(id = R.string.search_filter_series)
SearchType.Language -> stringResource(id = R.string.search_filter_language)
else -> ""
},
)
@ -181,7 +181,7 @@ fun CategorySearchPageContent(
private fun CategorySearchPageContentPreview() {
BibLibTheme {
CategorySearchPageContent(
filter = SearchFilter.Author,
filter = SearchType.Author,
search = { "Asimov" },
)
}

View file

@ -1,22 +1,23 @@
package com.pixelized.biblib.ui.screen.home.page.search.item
import android.content.res.Configuration
import androidx.compose.animation.*
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.*
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.runtime.*
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.KeyboardArrowDown
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.pixelized.biblib.R
import com.pixelized.biblib.ui.scaffold.SearchFilter
import com.pixelized.biblib.model.search.SearchType
import com.pixelized.biblib.ui.theme.BibLibTheme
import com.pixelized.biblib.utils.extention.bibLib
import com.pixelized.biblib.utils.extention.default
@ -24,7 +25,7 @@ import com.pixelized.biblib.utils.extention.default
@Stable
@Immutable
data class SearchFilterUio(
val id: SearchFilter,
val id: SearchType,
val label: String,
val value: String? = null,
)
@ -51,7 +52,7 @@ fun SearchFilterList(
}
}
@OptIn(ExperimentalMaterialApi::class)
@OptIn(ExperimentalMaterialApi::class, ExperimentalAnimationApi::class)
@Composable
private fun SearchFilter(
modifier: Modifier = Modifier,
@ -59,34 +60,34 @@ private fun SearchFilter(
onClick: () -> Unit = default(),
) {
FilterChip(
modifier = modifier,
modifier = modifier.animateContentSize(),
selected = uio.value != null,
onClick = onClick,
) {
Text(
color = MaterialTheme.bibLib.colors.typography.medium,
style = MaterialTheme.typography.caption,
text = uio.label,
)
uio.value?.let {
Text(
color = MaterialTheme.bibLib.colors.typography.medium,
style = MaterialTheme.typography.caption,
text = ": "
trailingIcon = {
Icon(
modifier = Modifier.size(MaterialTheme.bibLib.dimen.dp12),
imageVector = when (uio.value) {
null -> Icons.Default.KeyboardArrowDown
else -> Icons.Default.Close
},
tint = MaterialTheme.bibLib.colors.typography.medium,
contentDescription = null
)
},
) {
AnimatedContent(
targetState = uio.value,
transitionSpec = { fadeIn() with fadeOut() },
) {
Text(
color = MaterialTheme.bibLib.colors.typography.medium,
style = MaterialTheme.typography.caption,
text = it
text = when {
it != null -> stringResource(id = R.string.search_filter_param, uio.label, it)
else -> uio.label
}
)
}
Icon(
imageVector = Icons.Default.ArrowDropDown,
tint = MaterialTheme.bibLib.colors.typography.medium,
contentDescription = null
)
}
}
@ -104,20 +105,20 @@ private fun SearchFilterPreview() {
@Composable
fun searchFilterPreviewItems() = listOf(
SearchFilterUio(
id = SearchFilter.Author,
id = SearchType.Author,
label = stringResource(id = R.string.search_filter_author),
value = "Asimov",
),
SearchFilterUio(
id = SearchFilter.Genre,
id = SearchType.Genre,
label = stringResource(id = R.string.search_filter_genre),
),
SearchFilterUio(
id = SearchFilter.Series,
label = stringResource(id = R.string.search_filter_serie),
id = SearchType.Series,
label = stringResource(id = R.string.search_filter_series),
),
SearchFilterUio(
id = SearchFilter.Language,
id = SearchType.Language,
label = stringResource(id = R.string.search_filter_language),
),
)

View file

@ -1,67 +0,0 @@
package com.pixelized.biblib.ui.screen.home.page.search.item
import android.content.res.Configuration
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
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.filled.History
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.pixelized.biblib.ui.theme.BibLibTheme
import com.pixelized.biblib.utils.extention.bibLib
@Stable
@Immutable
data class SearchHistoryUio(
val label: String,
)
@Composable
fun SearchHistory(
uio: SearchHistoryUio,
) {
SearchHistoryContent(
modifier = Modifier.fillMaxWidth(),
label = uio.label
)
}
@Composable
private fun SearchHistoryContent(
modifier: Modifier = Modifier,
label: String,
) {
Row(
modifier = modifier.padding(all = MaterialTheme.bibLib.dimen.dp16)
) {
Icon(
modifier = Modifier.padding(end = MaterialTheme.bibLib.dimen.dp8),
tint = MaterialTheme.colors.onSurface,
imageVector = Icons.Default.History,
contentDescription = null,
)
Text(
color = MaterialTheme.bibLib.colors.typography.medium,
style = MaterialTheme.typography.body1,
text = label,
)
}
}
@Composable
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun SearchHistoryContentPreview() {
BibLibTheme {
SearchHistory(
uio = SearchHistoryUio("Asimov")
)
}
}

View file

@ -8,7 +8,8 @@ import androidx.lifecycle.viewModelScope
import androidx.paging.*
import com.pixelized.biblib.model.book.Author
import com.pixelized.biblib.repository.search.ISearchRepository
import com.pixelized.biblib.ui.screen.home.page.search.FilterUio
import com.pixelized.biblib.ui.screen.home.page.search.bottom.FilterUio
import com.pixelized.biblib.ui.screen.home.page.search.source.AuthorSearchSource
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers

View file

@ -9,7 +9,7 @@ import androidx.paging.*
import com.pixelized.biblib.model.book.Book
import com.pixelized.biblib.repository.search.ISearchRepository
import com.pixelized.biblib.ui.screen.home.common.item.MicroBookThumbnailUio
import com.pixelized.biblib.ui.screen.home.page.search.FilterUio
import com.pixelized.biblib.ui.screen.home.page.search.bottom.FilterUio
import com.pixelized.biblib.ui.screen.home.page.search.source.BookSearchSource
import com.pixelized.biblib.utils.extention.toMicroThumbnailUio
import dagger.hilt.android.lifecycle.HiltViewModel
@ -65,10 +65,12 @@ class BookSearchViewModel @Inject constructor(
series = criteria
searchSource?.invalidate()
}
fun filterGenre(criteria: FilterUio?) {
genre = criteria
searchSource?.invalidate()
}
fun filterLanguage(criteria: FilterUio?) {
language = criteria
searchSource?.invalidate()

View file

@ -8,7 +8,7 @@ import androidx.lifecycle.viewModelScope
import androidx.paging.*
import com.pixelized.biblib.model.book.Genre
import com.pixelized.biblib.repository.search.ISearchRepository
import com.pixelized.biblib.ui.screen.home.page.search.FilterUio
import com.pixelized.biblib.ui.screen.home.page.search.bottom.FilterUio
import com.pixelized.biblib.ui.screen.home.page.search.source.GenreSearchSource
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers

View file

@ -1,7 +1,7 @@
package com.pixelized.biblib.ui.screen.home.page.search.viewModel
import androidx.paging.PagingData
import com.pixelized.biblib.ui.screen.home.page.search.FilterUio
import com.pixelized.biblib.ui.screen.home.page.search.bottom.FilterUio
import kotlinx.coroutines.flow.Flow
interface IFilterViewModel {

View file

@ -8,7 +8,7 @@ import androidx.lifecycle.viewModelScope
import androidx.paging.*
import com.pixelized.biblib.model.book.Language
import com.pixelized.biblib.repository.search.ISearchRepository
import com.pixelized.biblib.ui.screen.home.page.search.FilterUio
import com.pixelized.biblib.ui.screen.home.page.search.bottom.FilterUio
import com.pixelized.biblib.ui.screen.home.page.search.source.LanguageSearchSource
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers

View file

@ -8,7 +8,7 @@ import androidx.lifecycle.viewModelScope
import androidx.paging.*
import com.pixelized.biblib.model.book.Series
import com.pixelized.biblib.repository.search.ISearchRepository
import com.pixelized.biblib.ui.screen.home.page.search.FilterUio
import com.pixelized.biblib.ui.screen.home.page.search.bottom.FilterUio
import com.pixelized.biblib.ui.screen.home.page.search.source.SeriesSearchSource
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers

View file

@ -13,6 +13,7 @@ data class BibLibDimen(
val dp2: Dp = 2.dp,
val dp4: Dp = 4.dp,
val dp8: Dp = 8.dp,
val dp12: Dp = 12.dp,
val dp16: Dp = 16.dp,
val dp32: Dp = 32.dp,
val dp48: Dp = 48.dp,
@ -33,10 +34,11 @@ data class BibLibDimen(
@Stable
@Immutable
data class BookThumbnail(
val ratio: Float = 1.6f,
val padding: Dp = 16.dp,
val arrangement: Dp = 16.dp,
val cover: DpSize = DpSize(width = 72.dp, height = 115.dp), // ratio 1.6
val micro: DpSize = DpSize(width = 40.dp, height = 64.dp), // ratio 1.6
val cover: DpSize = 72.dp.let { DpSize(width = it, height = it * ratio) },
val micro: DpSize = 48.dp.let { DpSize(width = it, height = it * ratio) },
)
@Stable

View file

@ -2,6 +2,7 @@ package com.pixelized.biblib.utils.extention
import androidx.annotation.StringDef
import com.pixelized.biblib.model.book.Book
import com.pixelized.biblib.model.book.Series
import com.pixelized.biblib.network.client.IBibLibClient
import com.pixelized.biblib.ui.screen.home.common.item.LargeBookThumbnailUio
import com.pixelized.biblib.ui.screen.home.common.item.MicroBookThumbnailUio
@ -20,10 +21,10 @@ fun Book.toMicroThumbnailUio(
@CoverUrl coverBaseUrl: String = IBibLibClient.THUMBNAIL_URL
) = MicroBookThumbnailUio(
id = id,
cover = "${coverBaseUrl}/$id.jpg",
title = title,
author = author.joinToString { it.name },
isNew = isNew,
cover = "${coverBaseUrl}/$id.jpg",
)
fun Book.toSmallThumbnailUio(
@ -58,7 +59,15 @@ fun Book.toDetailUio(
rating = rating?.toFloat() ?: 0.0f,
language = language?.displayLanguage?.capitalize() ?: "",
date = releaseDate.shortDate(),
series = series?.name,
series = series.toLabel(),
description = synopsis ?: "",
cover = "${coverBaseUrl}/$id.jpg",
)
)
fun Series?.toLabel(): String? {
return when {
this != null && index != null -> "$name - $index"
this != null -> name
else -> null
}
}

View file

@ -44,11 +44,14 @@
<string name="search_title">Rechercher sur Biblib</string>
<string name="search_filter_title">Rechercher</string>
<string name="search_filter_param">%1$s : %2$s</string>
<string name="search_filter_author">Auteur</string>
<string name="search_filter_serie">Série</string>
<string name="search_filter_series">Série</string>
<string name="search_filter_genre">Genre</string>
<string name="search_filter_language">Langue</string>
<string name="search_sort_by">Trié par : %1$s</string>
<string name="profile_title">Profile</string>
</resources>

View file

@ -50,10 +50,13 @@
<string name="search_title">Search on Biblib</string>
<string name="search_filter_title">Search</string>
<string name="search_filter_author">Author</string>
<string name="search_filter_serie">Serie</string>
<string name="search_filter_genre">Genre</string>
<string name="search_filter_language">Language</string>
<string name="search_filter_param">%1$s: %2$s</string>
<string name="search_filter_author">Authors</string>
<string name="search_filter_series">Series</string>
<string name="search_filter_genre">Genres</string>
<string name="search_filter_language">Languages</string>
<string name="search_sort_by">Sort by: %1$s</string>
<string name="profile_title">Profil</string>