Clean search filtering.
This commit is contained in:
parent
e176b6827a
commit
e69af172af
20 changed files with 177 additions and 219 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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" },
|
||||
)
|
||||
}
|
||||
|
|
@ -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),
|
||||
),
|
||||
)
|
||||
|
|
@ -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")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue