fix following filters

This commit is contained in:
Stefano Sansone
2024-04-25 00:16:00 +02:00
parent 0dea9b9ba0
commit e5c79fc6fa
7 changed files with 201 additions and 190 deletions

View File

@ -26,7 +26,7 @@ interface SavedItemDao {
suspend fun getSavedItemWithLabelsAndHighlights(slug: String): SavedItemWithLabelsAndHighlights?
@Query("DELETE FROM savedItem WHERE savedItemId = :itemID")
fun deleteById(itemID: String)
suspend fun deleteById(itemID: String)
@Query("DELETE FROM savedItem WHERE savedItemId in (:itemIDs)")
fun deleteByIds(itemIDs: List<String>)
@ -80,7 +80,12 @@ interface SavedItemDao {
"AND SavedItem.isArchived IN (:allowedArchiveStates) " +
"AND SavedItem.contentReader IN (:allowedContentReaders) " +
"AND CASE WHEN :hasRequiredLabels THEN SavedItemLabel.name in (:requiredLabels) ELSE 1 END " +
"AND CASE WHEN :hasExcludedLabels THEN SavedItemLabel.name is NULL OR SavedItemLabel.name not in (:excludedLabels) ELSE 1 END " +
"AND CASE WHEN :hasExcludedLabels THEN NOT EXISTS ( " +
" SELECT 1 FROM SavedItemAndSavedItemLabelCrossRef " +
" INNER JOIN SavedItemLabel ON SavedItemAndSavedItemLabelCrossRef.savedItemLabelId = SavedItemLabel.savedItemLabelId " +
" WHERE SavedItemAndSavedItemLabelCrossRef.savedItemId = SavedItem.savedItemId " +
" AND SavedItemLabel.name IN (:excludedLabels) " +
") ELSE 1 END " +
"GROUP BY SavedItem.savedItemId " +

View File

@ -27,6 +27,7 @@ import app.omnivore.omnivore.feature.library.LabelBottomSheet
import app.omnivore.omnivore.feature.library.LibraryBottomSheetState
import app.omnivore.omnivore.feature.library.LibraryNavigationBar
import app.omnivore.omnivore.feature.library.LibraryViewContent
import app.omnivore.omnivore.feature.library.SavedItemSortFilter
import app.omnivore.omnivore.feature.save.SaveViewModel
import app.omnivore.omnivore.navigation.Routes
import app.omnivore.omnivore.navigation.TopLevelDestination
@ -46,10 +47,6 @@ internal fun FollowingScreen(
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
val showBottomSheet: LibraryBottomSheetState by viewModel.bottomSheetState.observeAsState(
LibraryBottomSheetState.HIDDEN
)
viewModel.snackbarMessage?.let {
coroutineScope.launch {
snackbarHostState.showSnackbar(it)
@ -58,9 +55,15 @@ internal fun FollowingScreen(
}
val labels by viewModel.labelsState.collectAsStateWithLifecycle()
val currentTopLevelDestination =
TopLevelDestination.entries.find { it.route == navController.currentDestination?.route }
val selectedItem: SavedItemWithLabelsAndHighlights? by viewModel.actionsMenuItemLiveData.observeAsState()
val savedItemFilter by viewModel.appliedFilterState.collectAsStateWithLifecycle()
val activeLabels by viewModel.activeLabels.collectAsStateWithLifecycle()
val sortFilter: SavedItemSortFilter by viewModel.appliedSortFilterLiveData.collectAsStateWithLifecycle()
val bottomSheetState: LibraryBottomSheetState by viewModel.bottomSheetState.collectAsStateWithLifecycle()
when (showBottomSheet) {
when (bottomSheetState) {
LibraryBottomSheetState.ADD_LINK -> {
AddLinkBottomSheet(saveViewModel) {
viewModel.bottomSheetState.value = LibraryBottomSheetState.HIDDEN
@ -100,9 +103,6 @@ internal fun FollowingScreen(
}
}
val currentTopLevelDestination = TopLevelDestination.entries.find { it.route == navController.currentDestination?.route }
val selectedItem: SavedItemWithLabelsAndHighlights? by viewModel.actionsMenuItemLiveData.observeAsState()
Scaffold(
topBar = {
LibraryNavigationBar(
@ -118,6 +118,13 @@ internal fun FollowingScreen(
when (uiState) {
is FollowingUiState.Success -> {
LibraryViewContent(
itemsFilter = savedItemFilter,
activeLabels = activeLabels,
sortFilter = sortFilter,
updateSavedItemFilter = { viewModel.updateSavedItemFilter(it) },
updateSavedItemSortFilter = { viewModel.updateSavedItemSortFilter(it) },
setBottomSheetState = { viewModel.setBottomSheetState(it) },
updateAppliedLabels = { viewModel.updateAppliedLabels(it) },
isFollowingScreen = currentTopLevelDestination == TopLevelDestination.FOLLOWING,
{ viewModel.actionsMenuItemLiveData.postValue(null) },
savedItemViewModel = viewModel,

View File

@ -55,7 +55,7 @@ class FollowingViewModel @Inject constructor(
LibraryQuery(
allowedArchiveStates = listOf(0),
sortKey = "newest",
requiredLabels = listOf(),
requiredLabels = listOf("Newsletter", "RSS"),
excludedLabels = listOf(),
allowedContentReaders = listOf("WEB", "PDF", "EPUB")
)
@ -68,12 +68,11 @@ class FollowingViewModel @Inject constructor(
started = SharingStarted.Lazily,
initialValue = FollowingUiState.Loading
)
val appliedFilterState = MutableStateFlow(SavedItemFilter.FOLLOWING)
val appliedSortFilterLiveData = MutableStateFlow(SavedItemSortFilter.NEWEST)
val bottomSheetState = MutableStateFlow(LibraryBottomSheetState.HIDDEN)
val appliedFilterLiveData = MutableLiveData(
SavedItemFilter.FOLLOWING
)
val appliedSortFilterLiveData = MutableLiveData(SavedItemSortFilter.NEWEST)
val bottomSheetState = MutableLiveData(LibraryBottomSheetState.HIDDEN)
val currentItem = mutableStateOf<String?>(null)
val labelsState = libraryRepository.getSavedItemsLabels().stateIn(
@ -95,7 +94,7 @@ class FollowingViewModel @Inject constructor(
}
}
updateSavedItemFilter(appliedFilterLiveData.value ?: SavedItemFilter.INBOX)
updateSavedItemFilter(appliedFilterState.value)
}
private fun syncLabels() {
@ -114,6 +113,10 @@ class FollowingViewModel @Inject constructor(
load()
}
fun setBottomSheetState(state: LibraryBottomSheetState) {
bottomSheetState.value = state
}
private fun getLastSyncTime(): Instant? = runBlocking {
datastoreRepo.getString(DatastoreKeys.libraryLastSyncTimestamp)?.let {
try {
@ -159,10 +162,10 @@ class FollowingViewModel @Inject constructor(
}
}
private fun updateSavedItemFilter(filter: SavedItemFilter) {
fun updateSavedItemFilter(filter: SavedItemFilter) {
viewModelScope.launch {
datastoreRepo.putString(DatastoreKeys.lastUsedSavedItemFilter, filter.rawValue)
appliedFilterLiveData.value = filter
appliedFilterState.value = filter
handleFilterChanges()
}
}
@ -185,50 +188,47 @@ class FollowingViewModel @Inject constructor(
private fun handleFilterChanges() {
librarySearchCursor = null
if (appliedSortFilterLiveData.value != null && appliedFilterLiveData.value != null) {
val sortKey = when (appliedSortFilterLiveData.value) {
SavedItemSortFilter.NEWEST -> "newest"
SavedItemSortFilter.OLDEST -> "oldest"
SavedItemSortFilter.RECENTLY_READ -> "recentlyRead"
SavedItemSortFilter.RECENTLY_PUBLISHED -> "recentlyPublished"
else -> "newest"
}
val allowedArchiveStates = when (appliedFilterLiveData.value) {
SavedItemFilter.ALL -> listOf(0, 1)
SavedItemFilter.ARCHIVED -> listOf(1)
else -> listOf(0)
}
val allowedContentReaders = when (appliedFilterLiveData.value) {
SavedItemFilter.FILES -> listOf("PDF", "EPUB")
else -> listOf("WEB", "PDF", "EPUB")
}
var requiredLabels = when (appliedFilterLiveData.value) {
SavedItemFilter.NEWSLETTERS -> listOf("Newsletter")
SavedItemFilter.FEEDS -> listOf("RSS")
else -> activeLabels.value.map { it.name }
}
activeLabels.value.let { it ->
requiredLabels = requiredLabels + it.map { it.name }
}
val excludeLabels = when (appliedFilterLiveData.value) {
SavedItemFilter.READ_LATER -> listOf("Newsletter", "RSS")
else -> listOf()
}
_libraryQuery.value = LibraryQuery(
allowedArchiveStates = allowedArchiveStates,
sortKey = sortKey,
requiredLabels = requiredLabels,
excludedLabels = excludeLabels,
allowedContentReaders = allowedContentReaders
)
val sortKey = when (appliedSortFilterLiveData.value) {
SavedItemSortFilter.NEWEST -> "newest"
SavedItemSortFilter.OLDEST -> "oldest"
SavedItemSortFilter.RECENTLY_READ -> "recentlyRead"
SavedItemSortFilter.RECENTLY_PUBLISHED -> "recentlyPublished"
}
val allowedArchiveStates = when (appliedFilterState.value) {
SavedItemFilter.ALL -> listOf(0, 1)
SavedItemFilter.ARCHIVED -> listOf(1)
else -> listOf(0)
}
val allowedContentReaders = when (appliedFilterState.value) {
SavedItemFilter.FILES -> listOf("PDF", "EPUB")
else -> listOf("WEB", "PDF", "EPUB")
}
var requiredLabels = when (appliedFilterState.value) {
SavedItemFilter.NEWSLETTERS -> listOf("Newsletter")
SavedItemFilter.FEEDS -> listOf("RSS")
else -> activeLabels.value.map { it.name }
}
activeLabels.value.let { it ->
requiredLabels = requiredLabels + it.map { it.name }
}
val excludeLabels = when (appliedFilterState.value) {
SavedItemFilter.NON_FEED -> listOf("Newsletter", "RSS")
else -> listOf()
}
_libraryQuery.value = LibraryQuery(
allowedArchiveStates = allowedArchiveStates,
sortKey = sortKey,
requiredLabels = requiredLabels + listOf("Newsletter", "RSS"),
excludedLabels = excludeLabels,
allowedContentReaders = allowedContentReaders
)
}
private suspend fun syncItems() {
@ -365,7 +365,7 @@ class FollowingViewModel @Inject constructor(
private fun searchQueryString(): String {
var query =
"${appliedFilterLiveData.value?.queryString} ${appliedSortFilterLiveData.value?.queryString}"
"${appliedFilterState.value?.queryString} ${appliedSortFilterLiveData.value?.queryString}"
activeLabels.value.let {
if (it.isNotEmpty()) {

View File

@ -16,7 +16,6 @@ import androidx.compose.material3.SuggestionChipDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
@ -26,8 +25,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.intl.Locale
import androidx.compose.ui.text.toLowerCase
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import app.omnivore.omnivore.R
import app.omnivore.omnivore.core.database.entities.SavedItemLabel
import app.omnivore.omnivore.feature.components.LabelChipColors
@ -35,19 +32,19 @@ import app.omnivore.omnivore.feature.components.LabelChipColors
@Composable
fun LibraryFilterBar(
isFollowingScreen: Boolean,
viewModel: LibraryViewModel = hiltViewModel()
itemsFilter: SavedItemFilter,
sortFilter: SavedItemSortFilter,
activeLabels: List<SavedItemLabel>,
setBottomSheetState: (LibraryBottomSheetState) -> Unit,
updateSavedItemFilter: (SavedItemFilter) -> Unit,
updateSavedItemSortFilter: (SavedItemSortFilter) -> Unit,
updateAppliedLabels: (List<SavedItemLabel>) -> Unit
) {
var isSavedItemFilterMenuExpanded by remember { mutableStateOf(false) }
val activeSavedItemFilter: SavedItemFilter by viewModel.appliedFilterLiveData.observeAsState(
if (isFollowingScreen) SavedItemFilter.FOLLOWING else SavedItemFilter.INBOX
)
val activeLabels: List<SavedItemLabel> by viewModel.activeLabels.collectAsStateWithLifecycle()
var isSavedItemSortFilterMenuExpanded by remember { mutableStateOf(false) }
val activeSavedItemSortFilter: SavedItemSortFilter by viewModel.appliedSortFilterLiveData.observeAsState(
SavedItemSortFilter.NEWEST
)
val listState = rememberLazyListState()
Column {
@ -62,7 +59,7 @@ fun LibraryFilterBar(
item {
AssistChip(onClick = { isSavedItemFilterMenuExpanded = true },
label = { Text(
activeSavedItemFilter.displayText
itemsFilter.displayText
) },
trailingIcon = {
Icon(
@ -73,7 +70,7 @@ fun LibraryFilterBar(
modifier = Modifier.padding(end = 6.dp)
)
AssistChip(onClick = { isSavedItemSortFilterMenuExpanded = true },
label = { Text(activeSavedItemSortFilter.displayText) },
label = { Text(sortFilter.displayText) },
trailingIcon = {
Icon(
Icons.Default.ArrowDropDown,
@ -83,7 +80,7 @@ fun LibraryFilterBar(
modifier = Modifier.padding(end = 6.dp)
)
AssistChip(
onClick = { viewModel.bottomSheetState.value = LibraryBottomSheetState.LABEL },
onClick = { setBottomSheetState(LibraryBottomSheetState.LABEL) },
label = { Text(stringResource(R.string.library_filter_bar_label_labels)) },
trailingIcon = {
Icon(
@ -97,10 +94,12 @@ fun LibraryFilterBar(
items(activeLabels.sortedWith(compareBy { it.name.toLowerCase(Locale.current) })) { label ->
val chipColors = LabelChipColors.fromHex(label.color)
AssistChip(onClick = {
viewModel.updateAppliedLabels((viewModel.activeLabels.value
?: listOf()).filter { it.savedItemLabelId != label.savedItemLabelId })
},
AssistChip(
onClick = {
updateAppliedLabels(
activeLabels.filter { it.savedItemLabelId != label.savedItemLabelId }
)
},
label = { Text(label.name) },
border = null,
colors = SuggestionChipDefaults.elevatedSuggestionChipColors(
@ -122,11 +121,11 @@ fun LibraryFilterBar(
isFollowingScreen = isFollowingScreen,
isExpanded = isSavedItemFilterMenuExpanded,
onDismiss = { isSavedItemFilterMenuExpanded = false },
actionHandler = { viewModel.updateSavedItemFilter(it) }
actionHandler = { updateSavedItemFilter(it) }
)
SavedItemSortFilterContextMenu(isExpanded = isSavedItemSortFilterMenuExpanded,
onDismiss = { isSavedItemSortFilterMenuExpanded = false },
actionHandler = { viewModel.updateSavedItemSortFilter(it) })
actionHandler = { updateSavedItemSortFilter(it) })
}
}

View File

@ -90,10 +90,6 @@ internal fun LibraryView(
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
val showBottomSheet: LibraryBottomSheetState by viewModel.bottomSheetState.observeAsState(
LibraryBottomSheetState.HIDDEN
)
viewModel.snackbarMessage?.let {
coroutineScope.launch {
snackbarHostState.showSnackbar(it)
@ -102,9 +98,15 @@ internal fun LibraryView(
}
val labels by viewModel.labelsState.collectAsStateWithLifecycle()
val currentTopLevelDestination =
TopLevelDestination.entries.find { it.route == navController.currentDestination?.route }
val selectedItem: SavedItemWithLabelsAndHighlights? by viewModel.actionsMenuItemLiveData.observeAsState()
val savedItemFilter by viewModel.appliedFilterState.collectAsStateWithLifecycle()
val activeLabels by viewModel.activeLabels.collectAsStateWithLifecycle()
val sortFilter: SavedItemSortFilter by viewModel.appliedSortFilterLiveData.collectAsStateWithLifecycle()
val bottomSheetState: LibraryBottomSheetState by viewModel.bottomSheetState.collectAsStateWithLifecycle()
when (showBottomSheet) {
when (bottomSheetState) {
LibraryBottomSheetState.ADD_LINK -> {
AddLinkBottomSheet(saveViewModel) {
viewModel.bottomSheetState.value = LibraryBottomSheetState.HIDDEN
@ -112,8 +114,7 @@ internal fun LibraryView(
}
LibraryBottomSheetState.LABEL -> {
LabelBottomSheet(
deleteCurrentItem = { viewModel.currentItem.value = null },
LabelBottomSheet(deleteCurrentItem = { viewModel.currentItem.value = null },
labels = labels,
currentSavedItemData = viewModel.currentSavedItemUnderEdit(),
labelsViewModel,
@ -125,13 +126,11 @@ internal fun LibraryView(
viewModel.updateSavedItemLabels(savedItemId, labels)
},
activeLabels,
{ viewModel.updateAppliedLabels(it) }
)
{ viewModel.updateAppliedLabels(it) })
}
LibraryBottomSheetState.EDIT -> {
EditBottomSheet(
editInfoViewModel,
EditBottomSheet(editInfoViewModel,
deleteCurrentItem = { viewModel.currentItem.value = null },
{ viewModel.refresh() },
viewModel.currentSavedItemUnderEdit()
@ -144,24 +143,26 @@ internal fun LibraryView(
}
}
val currentTopLevelDestination = TopLevelDestination.entries.find { it.route == navController.currentDestination?.route }
val selectedItem: SavedItemWithLabelsAndHighlights? by viewModel.actionsMenuItemLiveData.observeAsState()
Scaffold(
topBar = {
LibraryNavigationBar(
currentDestination = currentTopLevelDestination,
LibraryNavigationBar(currentDestination = currentTopLevelDestination,
savedItemViewModel = viewModel,
onSearchClicked = { navController.navigate(Routes.Search.route) },
onAddLinkClicked = {
viewModel.bottomSheetState.value = LibraryBottomSheetState.ADD_LINK
}
)
})
},
) { paddingValues ->
when (uiState) {
is LibraryUiState.Success -> {
LibraryViewContent(
itemsFilter = savedItemFilter,
activeLabels = activeLabels,
sortFilter = sortFilter,
updateSavedItemFilter = { viewModel.updateSavedItemFilter(it) },
updateSavedItemSortFilter = { viewModel.updateSavedItemSortFilter(it) },
setBottomSheetState = { viewModel.setBottomSheetState(it) },
updateAppliedLabels = { viewModel.updateAppliedLabels(it) },
isFollowingScreen = currentTopLevelDestination == TopLevelDestination.FOLLOWING,
{ viewModel.actionsMenuItemLiveData.postValue(null) },
savedItemViewModel = viewModel,
@ -176,9 +177,9 @@ internal fun LibraryView(
viewModel.handleSavedItemAction(id, action)
},
{ viewModel.loadUsingSearchAPI() },
{ viewModel.initialLoad() }
)
{ viewModel.initialLoad() })
}
is LibraryUiState.Loading -> {
Box(
modifier = Modifier
@ -186,9 +187,10 @@ internal fun LibraryView(
.background(MaterialTheme.colorScheme.background),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator(strokeCap = StrokeCap.Round)
CircularProgressIndicator(strokeCap = StrokeCap.Round)
}
}
else -> {
// TODO
}
@ -218,8 +220,7 @@ fun LabelBottomSheet(
) {
if (currentSavedItemData != null) {
LabelsSelectionSheetContent(
labels = labels,
LabelsSelectionSheetContent(labels = labels,
labelsViewModel = labelsViewModel,
initialSelectedLabels = currentSavedItemData.labels,
onCancel = {
@ -236,11 +237,9 @@ fun LabelBottomSheet(
},
onCreateLabel = { newLabelName, labelHexValue ->
createNewSavedItemLabel(newLabelName, labelHexValue)
}
)
})
} else { // Is used in library mode
LabelsSelectionSheetContent(
labels = labels,
LabelsSelectionSheetContent(labels = labels,
labelsViewModel = labelsViewModel,
initialSelectedLabels = activeLabels,
onCancel = { onDismiss() },
@ -252,8 +251,7 @@ fun LabelBottomSheet(
},
onCreateLabel = { newLabelName, labelHexValue ->
createNewSavedItemLabel(newLabelName, labelHexValue)
}
)
})
}
}
}
@ -261,8 +259,7 @@ fun LabelBottomSheet(
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AddLinkBottomSheet(
saveViewModel: SaveViewModel,
onDismiss: () -> Unit = {}
saveViewModel: SaveViewModel, onDismiss: () -> Unit = {}
) {
ModalBottomSheet(
onDismissRequest = { onDismiss() },
@ -272,17 +269,13 @@ fun AddLinkBottomSheet(
),
) {
AddLinkSheetContent(
viewModel = saveViewModel,
onCancel = {
saveViewModel.state.value = SaveState.DEFAULT
onDismiss()
},
onLinkAdded = {
saveViewModel.state.value = SaveState.DEFAULT
onDismiss()
}
)
AddLinkSheetContent(viewModel = saveViewModel, onCancel = {
saveViewModel.state.value = SaveState.DEFAULT
onDismiss()
}, onLinkAdded = {
saveViewModel.state.value = SaveState.DEFAULT
onDismiss()
})
}
}
@ -302,8 +295,7 @@ fun EditBottomSheet(
skipPartiallyExpanded = true
),
) {
EditInfoSheetContent(
savedItemId = currentSavedItemUnderEdit?.savedItem?.savedItemId,
EditInfoSheetContent(savedItemId = currentSavedItemUnderEdit?.savedItem?.savedItemId,
title = currentSavedItemUnderEdit?.savedItem?.title,
author = currentSavedItemUnderEdit?.savedItem?.author,
description = currentSavedItemUnderEdit?.savedItem?.descriptionText,
@ -316,8 +308,7 @@ fun EditBottomSheet(
deleteCurrentItem()
refresh()
onDismiss()
}
)
})
}
}
@ -325,6 +316,13 @@ fun EditBottomSheet(
@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class)
@Composable
fun LibraryViewContent(
itemsFilter: SavedItemFilter,
activeLabels: List<SavedItemLabel>,
sortFilter: SavedItemSortFilter,
updateSavedItemFilter:(SavedItemFilter) -> Unit,
updateSavedItemSortFilter: (SavedItemSortFilter) -> Unit,
setBottomSheetState: (LibraryBottomSheetState) -> Unit,
updateAppliedLabels: (List<SavedItemLabel>) -> Unit,
isFollowingScreen: Boolean,
selectItem: () -> Unit,
savedItemViewModel: SavedItemViewModel,
@ -359,54 +357,63 @@ fun LibraryViewContent(
.nestedScroll(pullToRefreshState.nestedScrollConnection)
) {
Column {
LibraryFilterBar(isFollowingScreen)
LibraryFilterBar(
isFollowingScreen,
itemsFilter,
sortFilter,
activeLabels,
setBottomSheetState,
updateSavedItemFilter,
updateSavedItemSortFilter,
updateAppliedLabels
)
HorizontalDivider()
LazyColumn(
state = listState,
verticalArrangement = Arrangement.Top,
horizontalAlignment = Alignment.CenterHorizontally
) {
items(
items = items,
key = { item -> item.savedItem.savedItemId }
) { cardDataWithLabels ->
items(items = items,
key = { item -> item.savedItem.savedItemId }) { cardDataWithLabels ->
val swipeThreshold = 0.45f
val currentThresholdFraction = remember { mutableStateOf(0f) }
val currentItem by rememberUpdatedState(cardDataWithLabels.savedItem)
val swipeState = rememberDismissState(
confirmStateChange = {
when(it) {
DismissValue.Default -> {
val swipeState = rememberDismissState(confirmStateChange = {
when (it) {
DismissValue.Default -> {
return@rememberDismissState false
}
DismissValue.DismissedToEnd -> {
if (currentThresholdFraction.value < swipeThreshold) {
return@rememberDismissState false
}
DismissValue.DismissedToEnd -> {
if (currentThresholdFraction.value < swipeThreshold) {
return@rememberDismissState false
}
}
DismissValue.DismissedToStart -> {
if (currentThresholdFraction.value < swipeThreshold) {
return@rememberDismissState false
}
}
}
if (it == DismissValue.DismissedToEnd) { // Archiving/UnArchiving.
if (currentItem.isArchived) {
onUnarchive(currentItem.savedItemId)
} else {
onArchive(currentItem.savedItemId)
DismissValue.DismissedToStart -> {
if (currentThresholdFraction.value < swipeThreshold) {
return@rememberDismissState false
}
} else if (it == DismissValue.DismissedToStart) { // Deleting.
onDelete(currentItem.savedItemId)
}
true
}
)
if (it == DismissValue.DismissedToEnd) { // Archiving/UnArchiving.
if (currentItem.isArchived) {
onUnarchive(currentItem.savedItemId)
} else {
onArchive(currentItem.savedItemId)
}
} else if (it == DismissValue.DismissedToStart) { // Deleting.
onDelete(currentItem.savedItemId)
}
true
})
SwipeToDismiss(
state = swipeState,
directions = setOf(DismissDirection.StartToEnd, DismissDirection.EndToStart),
directions = setOf(
DismissDirection.StartToEnd, DismissDirection.EndToStart
),
dismissThresholds = { FractionalThreshold(swipeThreshold) },
background = {
val direction = swipeState.dismissDirection ?: return@SwipeToDismiss
@ -434,8 +441,7 @@ fun LibraryViewContent(
Modifier
.fillMaxSize()
.background(color)
.padding(horizontal = 20.dp),
contentAlignment = alignment
.padding(horizontal = 20.dp), contentAlignment = alignment
) {
currentThresholdFraction.value = swipeState.progress.fraction
Icon(
@ -453,8 +459,7 @@ fun LibraryViewContent(
labels = cardDataWithLabels.labels,
highlights = cardDataWithLabels.highlights
)
SavedItemCard(
selected = selected,
SavedItemCard(selected = selected,
savedItemViewModel = savedItemViewModel,
savedItem = savedItem,
onClickHandler = {
@ -467,11 +472,9 @@ fun LibraryViewContent(
},
actionHandler = {
onSavedItemAction(
currentItem.savedItemId,
it
currentItem.savedItemId, it
)
}
)
})
},
)
when {
@ -513,9 +516,7 @@ private fun Reset(state: DismissState) {
@Composable
fun InfiniteListHandler(
listState: LazyListState,
buffer: Int = 2,
onLoadMore: () -> Unit
listState: LazyListState, buffer: Int = 2, onLoadMore: () -> Unit
) {
val loadMore = remember {
derivedStateOf {
@ -528,9 +529,7 @@ fun InfiniteListHandler(
}
LaunchedEffect(loadMore) {
snapshotFlow { loadMore.value }
.distinctUntilChanged()
.collect {
snapshotFlow { loadMore.value }.distinctUntilChanged().collect {
onLoadMore()
}
}

View File

@ -64,10 +64,10 @@ class LibraryViewModel @Inject constructor(
initialValue = LibraryUiState.Loading
)
val appliedFilterLiveData = MutableLiveData<SavedItemFilter>()
val appliedFilterState = MutableStateFlow(SavedItemFilter.INBOX)
val appliedSortFilterLiveData = MutableStateFlow(SavedItemSortFilter.NEWEST)
val bottomSheetState = MutableStateFlow(LibraryBottomSheetState.HIDDEN)
val appliedSortFilterLiveData = MutableLiveData(SavedItemSortFilter.NEWEST)
val bottomSheetState = MutableLiveData(LibraryBottomSheetState.HIDDEN)
val currentItem = mutableStateOf<String?>(null)
val labelsState = libraryRepository.getSavedItemsLabels().stateIn(
@ -88,7 +88,7 @@ class LibraryViewModel @Inject constructor(
}
}
updateSavedItemFilter(appliedFilterLiveData.value ?: SavedItemFilter.INBOX)
updateSavedItemFilter(appliedFilterState.value)
}
private fun syncLabels() {
@ -107,6 +107,10 @@ class LibraryViewModel @Inject constructor(
load()
}
fun setBottomSheetState(state: LibraryBottomSheetState) {
bottomSheetState.value = state
}
private fun getLastSyncTime(): Instant? = runBlocking {
datastoreRepo.getString(DatastoreKeys.libraryLastSyncTimestamp)?.let {
try {
@ -155,7 +159,7 @@ class LibraryViewModel @Inject constructor(
fun updateSavedItemFilter(filter: SavedItemFilter) {
viewModelScope.launch {
datastoreRepo.putString(DatastoreKeys.lastUsedSavedItemFilter, filter.rawValue)
appliedFilterLiveData.value = filter
appliedFilterState.value = filter
handleFilterChanges()
}
}
@ -178,7 +182,7 @@ class LibraryViewModel @Inject constructor(
private fun handleFilterChanges() {
librarySearchCursor = null
if (appliedSortFilterLiveData.value != null && appliedFilterLiveData.value != null) {
if (appliedSortFilterLiveData.value != null && appliedFilterState.value != null) {
val sortKey = when (appliedSortFilterLiveData.value) {
SavedItemSortFilter.NEWEST -> "newest"
SavedItemSortFilter.OLDEST -> "oldest"
@ -187,18 +191,18 @@ class LibraryViewModel @Inject constructor(
else -> "newest"
}
val allowedArchiveStates = when (appliedFilterLiveData.value) {
val allowedArchiveStates = when (appliedFilterState.value) {
SavedItemFilter.ALL -> listOf(0, 1)
SavedItemFilter.ARCHIVED -> listOf(1)
else -> listOf(0)
}
val allowedContentReaders = when (appliedFilterLiveData.value) {
val allowedContentReaders = when (appliedFilterState.value) {
SavedItemFilter.FILES -> listOf("PDF", "EPUB")
else -> listOf("WEB", "PDF", "EPUB")
}
var requiredLabels = when (appliedFilterLiveData.value) {
var requiredLabels = when (appliedFilterState.value) {
SavedItemFilter.NEWSLETTERS -> listOf("Newsletter")
SavedItemFilter.FEEDS -> listOf("RSS")
else -> activeLabels.value.map { it.name }
@ -208,9 +212,8 @@ class LibraryViewModel @Inject constructor(
requiredLabels = requiredLabels + it.map { it.name }
}
val excludeLabels = when (appliedFilterLiveData.value) {
SavedItemFilter.READ_LATER -> listOf("Newsletter", "RSS")
val excludeLabels = when (appliedFilterState.value) {
SavedItemFilter.NON_FEED -> listOf("Newsletter", "RSS")
else -> listOf("Newsletter", "RSS")
}
@ -357,7 +360,7 @@ class LibraryViewModel @Inject constructor(
private fun searchQueryString(): String {
var query =
"${appliedFilterLiveData.value?.queryString} ${appliedSortFilterLiveData.value?.queryString}"
"${appliedFilterState.value?.queryString} ${appliedSortFilterLiveData.value?.queryString}"
activeLabels.value.let {
if (it.isNotEmpty()) {

View File

@ -9,13 +9,11 @@ import androidx.compose.ui.text.font.FontWeight
enum class SavedItemFilter(val displayText: String, val rawValue: String, val queryString: String) {
FOLLOWING("Following", "following", "in:following use:folders"),
INBOX("Inbox", rawValue = "inbox", "in:inbox use:folders"),
READ_LATER("Non-Feed Items", "nonFeed", "no:subscription"),
NON_FEED("Non-Feed Items", "nonFeed", "no:subscription"),
FEEDS("Feeds", "feeds", "in:inbox label:RSS"),
NEWSLETTERS("Newsletters", "newsletters", "in:inbox label:Newsletter"),
ALL("All", "all", "in:all"),
ARCHIVED("Archived", "archived", "in:archive"),
// HAS_HIGHLIGHTS("Highlighted", "hasHighlights", "has:highlights"),
FILES("Files", "files", "type:file"),
}
@ -35,7 +33,7 @@ fun SavedItemFilterContextMenu(
} else {
listOf(
SavedItemFilter.INBOX,
SavedItemFilter.READ_LATER,
SavedItemFilter.NON_FEED,
SavedItemFilter.ALL,
SavedItemFilter.ARCHIVED,
SavedItemFilter.FILES