Catergorie Search basic impl
This commit is contained in:
parent
42b4e414a0
commit
c7603ca71e
12 changed files with 230 additions and 26 deletions
|
|
@ -1,5 +1,6 @@
|
|||
package com.pixelized.biblib.database.dao
|
||||
|
||||
import androidx.paging.DataSource
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
|
|
@ -8,6 +9,9 @@ import com.pixelized.biblib.database.data.AuthorDbo
|
|||
|
||||
@Dao
|
||||
interface AuthorDao {
|
||||
@Query("SELECT * FROM ${AuthorDbo.TABLE}")
|
||||
fun getAll(): DataSource.Factory<Int, AuthorDbo>
|
||||
|
||||
@Query("SELECT * FROM ${AuthorDbo.TABLE} WHERE ${AuthorDbo.ID} LIKE :id")
|
||||
fun get(id: String?): AuthorDbo?
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,18 @@
|
|||
package com.pixelized.biblib.database.dao
|
||||
|
||||
import androidx.paging.DataSource
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import com.pixelized.biblib.database.data.AuthorDbo
|
||||
import com.pixelized.biblib.database.data.GenreDbo
|
||||
|
||||
@Dao
|
||||
interface GenreDao {
|
||||
@Query("SELECT * FROM ${GenreDbo.TABLE}")
|
||||
fun getAll(): DataSource.Factory<Int, GenreDbo>
|
||||
|
||||
@Query("SELECT * FROM ${GenreDbo.TABLE} WHERE ${GenreDbo.ID} LIKE :id")
|
||||
fun get(id: String?): GenreDbo?
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,18 @@
|
|||
package com.pixelized.biblib.database.dao
|
||||
|
||||
import androidx.paging.DataSource
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import com.pixelized.biblib.database.data.GenreDbo
|
||||
import com.pixelized.biblib.database.data.LanguageDbo
|
||||
|
||||
@Dao
|
||||
interface LanguageDao {
|
||||
@Query("SELECT * FROM ${LanguageDbo.TABLE}")
|
||||
fun getAll(): DataSource.Factory<Int, LanguageDbo>
|
||||
|
||||
@Query("SELECT * FROM ${LanguageDbo.TABLE} WHERE ${LanguageDbo.ID} LIKE :id")
|
||||
fun get(id: String?): LanguageDbo?
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,18 @@
|
|||
package com.pixelized.biblib.database.dao
|
||||
|
||||
import androidx.paging.DataSource
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import com.pixelized.biblib.database.data.LanguageDbo
|
||||
import com.pixelized.biblib.database.data.SeriesDbo
|
||||
|
||||
@Dao
|
||||
interface SeriesDao {
|
||||
@Query("SELECT * FROM ${SeriesDbo.TABLE}")
|
||||
fun getAll(): DataSource.Factory<Int, SeriesDbo>
|
||||
|
||||
@Query("SELECT * FROM ${SeriesDbo.TABLE} WHERE ${SeriesDbo.ID} LIKE :id")
|
||||
fun get(id: String?): SeriesDbo?
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,18 @@ class BookRepository @Inject constructor(
|
|||
override fun getBooksSource(): DataSource.Factory<Int, Book> =
|
||||
database.bookDao().getBook().map { it.toBook() }
|
||||
|
||||
override fun getAuthorsSource(): DataSource.Factory<Int, Author> =
|
||||
database.authorDao().getAll().map { it.toAuthor() }
|
||||
|
||||
override fun getSeriesSource(): DataSource.Factory<Int, Series> =
|
||||
database.seriesDao().getAll().map { it.toSeries() }
|
||||
|
||||
override fun getGenresSource(): DataSource.Factory<Int, Genre> =
|
||||
database.genreDao().getAll().map { it.toGenre() }
|
||||
|
||||
override fun getLanguagesSource(): DataSource.Factory<Int, Language> =
|
||||
database.languageDao().getAll().map { it.toLanguage() }
|
||||
|
||||
override suspend fun update(data: List<Book>) {
|
||||
val authors = mutableSetOf<AuthorDbo>()
|
||||
val genres = mutableSetOf<GenreDbo>()
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.pixelized.biblib.repository.book
|
||||
|
||||
import androidx.paging.DataSource
|
||||
import com.pixelized.biblib.model.book.Book
|
||||
import com.pixelized.biblib.model.book.*
|
||||
|
||||
interface IBookRepository {
|
||||
|
||||
|
|
@ -11,5 +11,13 @@ interface IBookRepository {
|
|||
|
||||
fun getBooksSource(): DataSource.Factory<Int, Book>
|
||||
|
||||
fun getAuthorsSource() : DataSource.Factory<Int, Author>
|
||||
|
||||
fun getSeriesSource() : DataSource.Factory<Int, Series>
|
||||
|
||||
fun getGenresSource() : DataSource.Factory<Int, Genre>
|
||||
|
||||
fun getLanguagesSource() : DataSource.Factory<Int, Language>
|
||||
|
||||
suspend fun update(data: List<Book>)
|
||||
}
|
||||
|
|
@ -49,6 +49,13 @@ fun BottomSearchScaffold(
|
|||
focusRequester = state.focusRequester,
|
||||
filter = state.filter,
|
||||
onClose = {
|
||||
when(state.filter) {
|
||||
is SearchFilter.Author -> searchViewModel.authors.confirm("")
|
||||
is SearchFilter.Series -> searchViewModel.series.confirm("")
|
||||
is SearchFilter.Genre -> searchViewModel.genre.confirm("")
|
||||
is SearchFilter.Language -> searchViewModel.language.confirm("")
|
||||
null -> Unit
|
||||
}
|
||||
state.collapse()
|
||||
}
|
||||
)
|
||||
|
|
@ -125,6 +132,13 @@ sealed class SearchFilter(
|
|||
value = value,
|
||||
)
|
||||
|
||||
class Series(
|
||||
value: String? = null,
|
||||
): SearchFilter(
|
||||
label = R.string.search_filter_serie,
|
||||
value = value,
|
||||
)
|
||||
|
||||
class Genre(
|
||||
value: String? = null,
|
||||
) : SearchFilter(
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@ 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.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
|
|
@ -18,10 +18,17 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.paging.PagingData
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import androidx.paging.compose.items
|
||||
import com.pixelized.biblib.ui.scaffold.LocalBottomSearchState
|
||||
import com.pixelized.biblib.ui.scaffold.SearchFilter
|
||||
import com.pixelized.biblib.ui.theme.BibLibTheme
|
||||
import com.pixelized.biblib.utils.extention.bibLib
|
||||
import com.pixelized.biblib.utils.extention.default
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
|
||||
@Composable
|
||||
fun CategorySearchPage(
|
||||
|
|
@ -31,26 +38,48 @@ fun CategorySearchPage(
|
|||
onClose: () -> Unit = default(),
|
||||
) {
|
||||
val bottomSearchState = LocalBottomSearchState.current
|
||||
|
||||
CategorySearchPageContent(
|
||||
focusRequester = focusRequester,
|
||||
filter = filter,
|
||||
onClose = onClose,
|
||||
searchValue = {
|
||||
searchFlow = {
|
||||
when (bottomSearchState.filter) {
|
||||
is SearchFilter.Author -> searchViewModel.author
|
||||
is SearchFilter.Genre -> searchViewModel.genre
|
||||
is SearchFilter.Language -> searchViewModel.language
|
||||
null -> ""
|
||||
is SearchFilter.Author -> searchViewModel.authors.filterFlow
|
||||
is SearchFilter.Series -> searchViewModel.series.filterFlow
|
||||
is SearchFilter.Genre -> searchViewModel.genre.filterFlow
|
||||
is SearchFilter.Language -> searchViewModel.language.filterFlow
|
||||
null -> emptyFlow()
|
||||
}
|
||||
},
|
||||
dataFlow = {
|
||||
when (bottomSearchState.filter) {
|
||||
is SearchFilter.Author -> searchViewModel.authors.dataFlow
|
||||
is SearchFilter.Series -> searchViewModel.series.dataFlow
|
||||
is SearchFilter.Genre -> searchViewModel.genre.dataFlow
|
||||
is SearchFilter.Language -> searchViewModel.language.dataFlow
|
||||
null -> emptyFlow()
|
||||
}
|
||||
},
|
||||
onSearchChange = {
|
||||
when (bottomSearchState.filter) {
|
||||
is SearchFilter.Author -> searchViewModel.filterAuthor(it)
|
||||
is SearchFilter.Genre -> searchViewModel.filterGenre(it)
|
||||
is SearchFilter.Language -> searchViewModel.filterLanguage(it)
|
||||
is SearchFilter.Author -> searchViewModel.authors.filter(it)
|
||||
is SearchFilter.Series -> searchViewModel.series.filter(it)
|
||||
is SearchFilter.Genre -> searchViewModel.genre.filter(it)
|
||||
is SearchFilter.Language -> searchViewModel.language.filter(it)
|
||||
null -> Unit
|
||||
}
|
||||
},
|
||||
onData = {
|
||||
when (bottomSearchState.filter) {
|
||||
is SearchFilter.Author -> searchViewModel.authors.confirm(it)
|
||||
is SearchFilter.Series -> searchViewModel.series.confirm(it)
|
||||
is SearchFilter.Genre -> searchViewModel.genre.confirm(it)
|
||||
is SearchFilter.Language -> searchViewModel.language.confirm(it)
|
||||
null -> Unit
|
||||
}
|
||||
bottomSearchState.collapse()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -58,10 +87,13 @@ fun CategorySearchPage(
|
|||
fun CategorySearchPageContent(
|
||||
focusRequester: FocusRequester = FocusRequester(),
|
||||
filter: SearchFilter?,
|
||||
searchValue: () -> String,
|
||||
searchFlow: () -> Flow<String>,
|
||||
dataFlow: () -> Flow<PagingData<String>> = { emptyFlow() },
|
||||
onSearchChange: (String) -> Unit = default<String>(),
|
||||
onData: (String) -> Unit = default<String>(),
|
||||
onClose: () -> Unit = default(),
|
||||
) {
|
||||
val data = dataFlow().collectAsLazyPagingItems()
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.imePadding()
|
||||
|
|
@ -98,7 +130,7 @@ fun CategorySearchPageContent(
|
|||
text = "Rechercher"
|
||||
)
|
||||
},
|
||||
value = searchValue(),
|
||||
value = searchFlow().collectAsState(initial = "").value,
|
||||
singleLine = true,
|
||||
colors = TextFieldDefaults.outlinedTextFieldColors(
|
||||
focusedBorderColor = Color.Transparent,
|
||||
|
|
@ -107,6 +139,21 @@ fun CategorySearchPageContent(
|
|||
),
|
||||
onValueChange = onSearchChange
|
||||
)
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
items(items = data, key = { it }) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.clickable { onData(it ?: "") }
|
||||
.fillMaxWidth()
|
||||
.padding(all = MaterialTheme.bibLib.dimen.dp16),
|
||||
style = MaterialTheme.typography.body1,
|
||||
color = MaterialTheme.colors.onSurface,
|
||||
text = it ?: ""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -117,7 +164,7 @@ private fun CategorySearchPageContentPreview() {
|
|||
BibLibTheme {
|
||||
CategorySearchPageContent(
|
||||
filter = SearchFilter.Author(),
|
||||
searchValue = { "Asimov" },
|
||||
searchFlow = { flow { "Asimov" } },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -26,21 +26,12 @@ import com.pixelized.biblib.ui.theme.BibLibTheme
|
|||
import com.pixelized.biblib.utils.extention.bibLib
|
||||
import com.pixelized.biblib.utils.extention.default
|
||||
|
||||
|
||||
@Composable
|
||||
fun SearchPage(
|
||||
searchViewModel: SearchViewModel = LocalSearchViewModel.current
|
||||
) {
|
||||
val bottomSearchState = LocalBottomSearchState.current
|
||||
val filters by remember {
|
||||
derivedStateOf {
|
||||
listOf(
|
||||
SearchFilter.Author(value = searchViewModel.author),
|
||||
SearchFilter.Genre(value = searchViewModel.genre),
|
||||
SearchFilter.Language(value = searchViewModel.language),
|
||||
)
|
||||
}
|
||||
}
|
||||
val filters = rememberSearchFilter()
|
||||
|
||||
SearchPageContent(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
|
|
@ -129,6 +120,29 @@ private fun SearchChipFilter(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun rememberSearchFilter(
|
||||
searchViewModel: SearchViewModel = LocalSearchViewModel.current
|
||||
): List<SearchFilter> {
|
||||
val authors by searchViewModel.authors.search
|
||||
val genre by searchViewModel.genre.search
|
||||
val language by searchViewModel.language.search
|
||||
val series by searchViewModel.series.search
|
||||
|
||||
val filters by remember {
|
||||
derivedStateOf {
|
||||
listOf(
|
||||
SearchFilter.Author(value = authors),
|
||||
SearchFilter.Genre(value = genre),
|
||||
SearchFilter.Language(value = language),
|
||||
SearchFilter.Series(value = series)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return filters
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true, uiMode = UI_MODE_NIGHT_NO)
|
||||
@Preview(showBackground = true, uiMode = UI_MODE_NIGHT_YES)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,88 @@
|
|||
package com.pixelized.biblib.ui.screen.home.page.search
|
||||
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.paging.*
|
||||
import com.pixelized.biblib.repository.book.IBookRepository
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class SearchViewModel @Inject constructor(
|
||||
bookRepository: IBookRepository,
|
||||
) : ViewModel() {
|
||||
|
||||
var search: String by mutableStateOf("")
|
||||
private set
|
||||
|
||||
val authors = FilterManager(
|
||||
scope = viewModelScope,
|
||||
source = bookRepository.getAuthorsSource()
|
||||
.map { it.name }
|
||||
.asPagingSourceFactory(Dispatchers.IO)
|
||||
)
|
||||
|
||||
val series = FilterManager(
|
||||
scope = viewModelScope,
|
||||
source = bookRepository.getSeriesSource()
|
||||
.map { it.name }
|
||||
.asPagingSourceFactory(Dispatchers.IO)
|
||||
)
|
||||
|
||||
val genre = FilterManager(
|
||||
scope = viewModelScope,
|
||||
source = bookRepository.getGenresSource()
|
||||
.map { it.name }
|
||||
.asPagingSourceFactory(Dispatchers.IO)
|
||||
)
|
||||
|
||||
val language = FilterManager(
|
||||
scope = viewModelScope,
|
||||
source = bookRepository.getLanguagesSource()
|
||||
.map { it.displayLanguage }
|
||||
.asPagingSourceFactory(Dispatchers.IO)
|
||||
)
|
||||
|
||||
fun search(criteria: String) {
|
||||
|
||||
}
|
||||
|
||||
class FilterManager(
|
||||
private val scope: CoroutineScope,
|
||||
source: () -> PagingSource<Int, String>,
|
||||
) {
|
||||
private val _searchFlow = MutableStateFlow("")
|
||||
val searchFlow: Flow<String> get() = _searchFlow
|
||||
|
||||
private val _filterFlow = MutableStateFlow("")
|
||||
val filterFlow: Flow<String> get() = _filterFlow
|
||||
|
||||
val dataFlow: Flow<PagingData<String>> = Pager(
|
||||
config = PagingConfig(pageSize = 30),
|
||||
pagingSourceFactory = source,
|
||||
).flow.cachedIn(scope).combine(this._filterFlow) { paging, filter ->
|
||||
paging.filter { it.contains(filter, ignoreCase = true) }
|
||||
}
|
||||
|
||||
val filter: State<String>
|
||||
@Composable get() = _filterFlow.collectAsState(initial = "")
|
||||
|
||||
val search: State<String>
|
||||
@Composable get() = _searchFlow.collectAsState(initial = "")
|
||||
|
||||
fun filter(criteria: String) {
|
||||
scope.launch { _filterFlow.emit(criteria) }
|
||||
}
|
||||
|
||||
fun confirm(criteria: String) {
|
||||
scope.launch { _searchFlow.emit(criteria) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -42,6 +42,7 @@
|
|||
<string name="detail_emails_title">Envoyer cet eBook à :</string>
|
||||
|
||||
<string name="search_filter_author">Auteur</string>
|
||||
<string name="search_filter_serie">Série</string>
|
||||
<string name="search_filter_genre">Genre</string>
|
||||
<string name="search_filter_language">Langue</string>
|
||||
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@
|
|||
<string name="detail_emails_title">Send this eBook to:</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>
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue