diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/Constants.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/Constants.kt index e85e74d94..19b99de54 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/Constants.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/Constants.kt @@ -16,6 +16,7 @@ object DatastoreKeys { const val preferredWebFontFamily = "preferredWebFontFamily" const val prefersWebHighContrastText = "prefersWebHighContrastText" const val lastUsedSavedItemFilter = "lastUsedSavedItemFilter" + const val lastUsedSavedItemSortFilter = "lastUsedSavedItemSortFilter" } object AppleConstants { diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/library/LibraryFilterBar.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/library/LibraryFilterBar.kt index c84db7da3..dcaf5e524 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/library/LibraryFilterBar.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/library/LibraryFilterBar.kt @@ -17,6 +17,9 @@ fun LibraryFilterBar(viewModel: LibraryViewModel) { var isSavedItemFilterMenuExpanded by remember { mutableStateOf(false) } val activeSavedItemFilter: SavedItemFilter by viewModel.appliedFilterLiveData.observeAsState(SavedItemFilter.INBOX) + var isSavedItemSortFilterMenuExpanded by remember { mutableStateOf(false) } + val activeSavedItemSortFilter: SavedItemSortFilter by viewModel.appliedSortFilterLiveData.observeAsState(SavedItemSortFilter.NEWEST) + Column { Row( horizontalArrangement = Arrangement.Start, @@ -36,8 +39,8 @@ fun LibraryFilterBar(viewModel: LibraryViewModel) { modifier = Modifier.padding(end = 6.dp) ) AssistChip( - onClick = { /* Do something! */ }, - label = { Text("Newest") }, + onClick = { isSavedItemSortFilterMenuExpanded = true }, + label = { Text(activeSavedItemSortFilter.displayText) }, trailingIcon = { Icon( Icons.Default.ArrowDropDown, @@ -53,5 +56,11 @@ fun LibraryFilterBar(viewModel: LibraryViewModel) { onDismiss = { isSavedItemFilterMenuExpanded = false }, actionHandler = { viewModel.updateSavedItemFilter(it) } ) + + SavedItemSortFilterContextMenu( + isExpanded = isSavedItemSortFilterMenuExpanded, + onDismiss = { isSavedItemSortFilterMenuExpanded = false }, + actionHandler = { viewModel.updateSavedItemSortFilter(it) } + ) } } diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/library/LibraryViewModel.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/library/LibraryViewModel.kt index d8facaa37..32efeea18 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/library/LibraryViewModel.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/library/LibraryViewModel.kt @@ -24,21 +24,6 @@ class LibraryViewModel @Inject constructor( private val dataService: DataService, private val datastoreRepo: DatastoreRepository ): ViewModel() { - - init { - // Load the last saved library filter - runBlocking { - datastoreRepo.getString(DatastoreKeys.lastUsedSavedItemFilter)?.let { str -> - try { - val filter = SavedItemFilter.values().first { it.rawValue == str } - appliedFilterLiveData.postValue(filter) - } catch (e: Exception) { - Log.d("error", "invalid filter value store in datastore repo: $e") - } - } - } - } - private var cursor: String? = null // These are used to make sure we handle search result @@ -51,8 +36,35 @@ class LibraryViewModel @Inject constructor( val searchItemsLiveData = MutableLiveData>(listOf()) val itemsLiveData = dataService.db.savedItemDao().getLibraryLiveDataWithLabels() val appliedFilterLiveData = MutableLiveData(SavedItemFilter.INBOX) + val appliedSortFilterLiveData = MutableLiveData(SavedItemSortFilter.NEWEST) var isRefreshing by mutableStateOf(false) + var hasLoadedInitialFilters = false + + fun loadInitialFilterValues() { + if (hasLoadedInitialFilters) { return } + hasLoadedInitialFilters = false + + runBlocking { + datastoreRepo.getString(DatastoreKeys.lastUsedSavedItemFilter)?.let { str -> + try { + val filter = SavedItemFilter.values().first { it.rawValue == str } + appliedFilterLiveData.postValue(filter) + } catch (e: Exception) { + Log.d("error", "invalid filter value stored in datastore repo: $e") + } + } + + datastoreRepo.getString(DatastoreKeys.lastUsedSavedItemSortFilter)?.let { str -> + try { + val filter = SavedItemSortFilter.values().first { it.rawValue == str } + appliedSortFilterLiveData.postValue(filter) + } catch (e: Exception) { + Log.d("error", "invalid sort filter value stored in datastore repo: $e") + } + } + } + } fun updateSearchText(text: String) { searchTextLiveData.value = text @@ -80,6 +92,8 @@ class LibraryViewModel @Inject constructor( } fun load(clearPreviousSearch: Boolean = false) { + loadInitialFilterValues() + viewModelScope.launch { if (searchTextLiveData.value != "") { performSearch(clearPreviousSearch) @@ -97,6 +111,14 @@ class LibraryViewModel @Inject constructor( } } + fun updateSavedItemSortFilter(filter: SavedItemSortFilter) { + viewModelScope.launch { + datastoreRepo.putString(DatastoreKeys.lastUsedSavedItemSortFilter, filter.rawValue) + appliedSortFilterLiveData.postValue(filter) + // TODO: update Room query + } + } + private suspend fun syncItems() { val syncStart = Instant.now() val lastSyncDate = getLastSyncTime() ?: Instant.MIN diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/library/SavedItemSortFilter.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/library/SavedItemSortFilter.kt new file mode 100644 index 000000000..58f24174b --- /dev/null +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/library/SavedItemSortFilter.kt @@ -0,0 +1,35 @@ +package app.omnivore.omnivore.ui.library + +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable + +enum class SavedItemSortFilter(val displayText: String, val rawValue: String, val queryString: String) { + NEWEST("Newest", rawValue = "newest", "sort:saved"), + OLDEST("Oldest", rawValue = "oldest", "sort:saved-ASC"), + RECENTLY_READ("Recently Read", rawValue = "recentlyRead", "sort:read"), + RECENTLY_PUBLISHED("Recently Published", rawValue = "recentlyPublished", "sort:published"), +} + +@Composable +fun SavedItemSortFilterContextMenu( + isExpanded: Boolean, + onDismiss: () -> Unit, + actionHandler: (SavedItemSortFilter) -> Unit +) { + DropdownMenu( + expanded = isExpanded, + onDismissRequest = onDismiss + ) { + SavedItemSortFilter.values().forEach { + DropdownMenuItem( + text = { Text(it.displayText) }, + onClick = { + actionHandler(it) + onDismiss() + } + ) + } + } +}