apply label filters in library live data

This commit is contained in:
Satindar Dhillon
2023-02-26 17:55:56 -08:00
parent 9e1c64e125
commit 0282dcb4ad
4 changed files with 167 additions and 110 deletions

View File

@ -63,6 +63,12 @@ fun DataService.libraryLiveData(
}
}
}
if (labels.isNotEmpty()) {
mediatorLiveData.value = (mediatorLiveData.value ?: listOf()).filter {
it.labels.intersect(labels.toSet()).any()
}
}
}
return mediatorLiveData

View File

@ -1,64 +1,102 @@
package app.omnivore.omnivore.ui.library
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import app.omnivore.omnivore.persistence.entities.SavedItemLabel
import app.omnivore.omnivore.ui.components.LabelChipColors
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LibraryFilterBar(viewModel: LibraryViewModel) {
var isSavedItemFilterMenuExpanded by remember { mutableStateOf(false) }
val activeSavedItemFilter: SavedItemFilter by viewModel.appliedFilterLiveData.observeAsState(SavedItemFilter.INBOX)
val activeLabels: List<SavedItemLabel> by viewModel.activeLabelsLiveData.observeAsState(listOf())
var isSavedItemSortFilterMenuExpanded by remember { mutableStateOf(false) }
val activeSavedItemSortFilter: SavedItemSortFilter by viewModel.appliedSortFilterLiveData.observeAsState(SavedItemSortFilter.NEWEST)
val listState = rememberLazyListState()
Column {
Row(
LazyRow(
state = listState,
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.padding(start = 6.dp)
.fillMaxWidth()
) {
AssistChip(
onClick = { isSavedItemFilterMenuExpanded = true },
label = { Text(activeSavedItemFilter.displayText) },
trailingIcon = {
Icon(
Icons.Default.ArrowDropDown,
contentDescription = "drop down button to change primary library filter"
)
},
modifier = Modifier.padding(end = 6.dp)
)
AssistChip(
onClick = { isSavedItemSortFilterMenuExpanded = true },
label = { Text(activeSavedItemSortFilter.displayText) },
trailingIcon = {
Icon(
Icons.Default.ArrowDropDown,
contentDescription = "drop down button to change library sort order"
)
},
modifier = Modifier.padding(end = 6.dp)
)
AssistChip(
onClick = { viewModel.showLabelsSelectionSheetLiveData.value = true },
label = { Text("Labels") },
trailingIcon = {
Icon(
Icons.Default.ArrowDropDown,
contentDescription = "drop down button to open label selection sheet"
)
},
modifier = Modifier.padding(end = 6.dp)
)
item {
AssistChip(
onClick = { isSavedItemFilterMenuExpanded = true },
label = { Text(activeSavedItemFilter.displayText) },
trailingIcon = {
Icon(
Icons.Default.ArrowDropDown,
contentDescription = "drop down button to change primary library filter"
)
},
modifier = Modifier.padding(end = 6.dp)
)
AssistChip(
onClick = { isSavedItemSortFilterMenuExpanded = true },
label = { Text(activeSavedItemSortFilter.displayText) },
trailingIcon = {
Icon(
Icons.Default.ArrowDropDown,
contentDescription = "drop down button to change library sort order"
)
},
modifier = Modifier.padding(end = 6.dp)
)
AssistChip(
onClick = { viewModel.showLabelsSelectionSheetLiveData.value = true },
label = { Text("Labels") },
trailingIcon = {
Icon(
Icons.Default.ArrowDropDown,
contentDescription = "drop down button to open label selection sheet"
)
},
modifier = Modifier.padding(end = 6.dp)
)
}
items(activeLabels.sortedBy { it.name }) { label ->
val chipColors = LabelChipColors.fromHex(label.color)
AssistChip(
onClick = {
viewModel.updateAppliedLabels(
(viewModel.activeLabelsLiveData.value ?: listOf()).filter { it.savedItemLabelId != label.savedItemLabelId }
)
},
label = { Text(label.name) },
border = null,
colors = SuggestionChipDefaults.elevatedSuggestionChipColors(
containerColor = chipColors.containerColor,
labelColor = chipColors.textColor,
iconContentColor = chipColors.textColor
),
trailingIcon = {
Icon(
Icons.Default.Close,
contentDescription = "close icon to remove label"
)
},
modifier = Modifier
.padding(horizontal = 4.dp)
)
}
}
SavedItemFilterContextMenu(

View File

@ -147,95 +147,102 @@ fun InfiniteListHandler(
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LabelsSelectionSheet(viewModel: LibraryViewModel) {
val listState = rememberLazyListState()
val isActive: Boolean by viewModel.showLabelsSelectionSheetLiveData.observeAsState(false)
val labels: List<SavedItemLabel> by viewModel.savedItemLabelsLiveData.observeAsState(listOf())
val selectedLabels = remember { mutableStateOf(viewModel.activeLabelsLiveData.value) }
if (isActive) {
Dialog(onDismissRequest = { viewModel.showLabelsSelectionSheetLiveData.value = false } ) {
Surface(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colorScheme.background),
shape = RoundedCornerShape(16.dp)
) {
LazyColumn(
state = listState,
verticalArrangement = Arrangement.Top,
horizontalAlignment = Alignment.CenterHorizontally,
LabelsSelectionSheetContent(viewModel = viewModel, initialSelectedLabels = viewModel.activeLabelsLiveData.value ?: listOf())
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LabelsSelectionSheetContent(viewModel: LibraryViewModel, initialSelectedLabels: List<SavedItemLabel>) {
val listState = rememberLazyListState()
val labels: List<SavedItemLabel> by viewModel.savedItemLabelsLiveData.observeAsState(listOf())
val selectedLabels = remember { mutableStateOf(initialSelectedLabels) }
Surface(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colorScheme.background),
shape = RoundedCornerShape(16.dp)
) {
LazyColumn(
state = listState,
verticalArrangement = Arrangement.Top,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxSize()
.padding(horizontal = 6.dp)
) {
item {
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxSize()
.padding(horizontal = 6.dp)
.fillMaxWidth()
) {
item {
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
) {
TextButton(onClick = { viewModel.showLabelsSelectionSheetLiveData.value = false }) {
Text(text = "Cancel")
}
Text("Filter by Label", fontWeight = FontWeight.ExtraBold)
TextButton(
onClick = {
selectedLabels.value?.let {
viewModel.activeLabelsLiveData.value = it
}
viewModel.showLabelsSelectionSheetLiveData.value = false
}
) {
Text(text = "Done")
}
}
TextButton(onClick = { viewModel.showLabelsSelectionSheetLiveData.value = false }) {
Text(text = "Cancel")
}
items(labels) { label ->
val isLabelSelected = (selectedLabels.value ?: listOf()).contains(label)
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.clickable {
if (isLabelSelected) {
selectedLabels.value = (selectedLabels.value ?: listOf()).filter { it.savedItemLabelId != label.savedItemLabelId }
} else {
selectedLabels.value = (selectedLabels.value ?: listOf()) + listOf(label)
}
}
.padding(horizontal = 6.dp)
) {
val chipColors = LabelChipColors.fromHex(label.color)
Text("Filter by Label", fontWeight = FontWeight.ExtraBold)
SuggestionChip(
onClick = {},
label = { Text(label.name) },
border = null,
colors = SuggestionChipDefaults.elevatedSuggestionChipColors(
containerColor = chipColors.containerColor,
labelColor = chipColors.textColor,
iconContentColor = chipColors.textColor
)
)
if (isLabelSelected) {
Icon(
imageVector = Icons.Default.Check,
contentDescription = null
)
TextButton(
onClick = {
selectedLabels.value?.let {
viewModel.updateAppliedLabels(it)
}
viewModel.showLabelsSelectionSheetLiveData.value = false
}
Divider(color = MaterialTheme.colorScheme.outlineVariant, thickness = 1.dp)
) {
Text(text = "Done")
}
}
}
items(labels) { label ->
val isLabelSelected = (selectedLabels.value ?: listOf()).contains(label)
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.clickable {
if (isLabelSelected) {
selectedLabels.value = (selectedLabels.value
?: listOf()).filter { it.savedItemLabelId != label.savedItemLabelId }
} else {
selectedLabels.value = (selectedLabels.value ?: listOf()) + listOf(label)
}
}
.padding(horizontal = 6.dp)
) {
val chipColors = LabelChipColors.fromHex(label.color)
SuggestionChip(
onClick = {},
label = { Text(label.name) },
border = null,
colors = SuggestionChipDefaults.elevatedSuggestionChipColors(
containerColor = chipColors.containerColor,
labelColor = chipColors.textColor,
iconContentColor = chipColors.textColor
)
)
if (isLabelSelected) {
Icon(
imageVector = Icons.Default.Check,
contentDescription = null
)
}
}
Divider(color = MaterialTheme.colorScheme.outlineVariant, thickness = 1.dp)
}
}
}
}

View File

@ -40,13 +40,12 @@ class LibraryViewModel @Inject constructor(
val appliedSortFilterLiveData = MutableLiveData(SavedItemSortFilter.NEWEST)
val showLabelsSelectionSheetLiveData = MutableLiveData(false)
val savedItemLabelsLiveData = dataService.db.savedItemLabelDao().getSavedItemLabelsLiveData()
val activeLabelsLiveData = MutableLiveData<List<SavedItemLabel>>(listOf())
var isRefreshing by mutableStateOf(false)
var showSearchField by mutableStateOf(false)
var hasLoadedInitialFilters = false
var activeLabelsLiveData = MutableLiveData<List<SavedItemLabel>>(listOf())
fun loadInitialFilterValues() {
if (hasLoadedInitialFilters) { return }
hasLoadedInitialFilters = false
@ -129,11 +128,18 @@ class LibraryViewModel @Inject constructor(
}
}
fun updateAppliedLabels(labels: List<SavedItemLabel>) {
viewModelScope.launch {
activeLabelsLiveData.value = labels
handleFilterChanges()
}
}
suspend fun handleFilterChanges() {
if (searchTextLiveData.value != "") {
performSearch(true)
} else if (appliedSortFilterLiveData.value != null && appliedFilterLiveData.value != null) {
itemsLiveDataInternal = dataService.libraryLiveData(appliedFilterLiveData.value!!, appliedSortFilterLiveData.value!!, listOf())
itemsLiveDataInternal = dataService.libraryLiveData(appliedFilterLiveData.value!!, appliedSortFilterLiveData.value!!, activeLabelsLiveData.value ?: listOf())
itemsLiveData.removeSource(itemsLiveDataInternal)
itemsLiveData.addSource(itemsLiveDataInternal, itemsLiveData::setValue)
}