Catergorie Search basic impl

This commit is contained in:
Thomas Andres Gomez 2022-07-05 18:43:25 +02:00
parent 42b4e414a0
commit c7603ca71e
12 changed files with 230 additions and 26 deletions

View file

@ -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?

View file

@ -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?

View file

@ -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?

View file

@ -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?

View file

@ -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>()

View file

@ -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>)
}

View file

@ -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(

View file

@ -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" } },
)
}
}

View file

@ -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)

View file

@ -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) }
}
}
}

View file

@ -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>

View file

@ -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>