diff --git a/android/Omnivore/app/src/main/graphql/ApplyLabels.graphql b/android/Omnivore/app/src/main/graphql/ApplyLabels.graphql new file mode 100644 index 000000000..0e9fe8cf0 --- /dev/null +++ b/android/Omnivore/app/src/main/graphql/ApplyLabels.graphql @@ -0,0 +1,12 @@ +mutation SetLabels($input: SetLabelsInput!) { + setLabels(input: $input) { + ... on SetLabelsSuccess { + labels { + ...LabelFields + } + } + ... on SetLabelsError { + errorCodes + } + } +} diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/networking/SavedItemLabelMutations.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/networking/SavedItemLabelMutations.kt new file mode 100644 index 000000000..39755b22f --- /dev/null +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/networking/SavedItemLabelMutations.kt @@ -0,0 +1,13 @@ +package app.omnivore.omnivore.networking + +import app.omnivore.omnivore.graphql.generated.SetLabelsMutation +import app.omnivore.omnivore.graphql.generated.type.SetLabelsInput + +suspend fun Networker.updateLabelsForSavedItem(input: SetLabelsInput): Boolean { + return try { + val result = authenticatedApolloClient().mutation(SetLabelsMutation(input)).execute() + return result.data?.setLabels?.onSetLabelsSuccess?.labels != null + } catch (e: java.lang.Exception) { + false + } +} diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/components/LabelsSelectionSheet.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/components/LabelsSelectionSheet.kt index 28af4a225..788317d10 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/components/LabelsSelectionSheet.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/components/LabelsSelectionSheet.kt @@ -26,19 +26,55 @@ import app.omnivore.omnivore.ui.library.LibraryViewModel @Composable fun LabelsSelectionSheet(viewModel: LibraryViewModel) { val isActive: Boolean by viewModel.showLabelsSelectionSheetLiveData.observeAsState(false) + val labels: List by viewModel.savedItemLabelsLiveData.observeAsState(listOf()) + val currentSavedItemData = viewModel.currentSavedItemUnderEdit() if (isActive) { - Dialog(onDismissRequest = { viewModel.showLabelsSelectionSheetLiveData.value = false } ) { - LabelsSelectionSheetContent(viewModel = viewModel, initialSelectedLabels = viewModel.activeLabelsLiveData.value ?: listOf()) + Dialog(onDismissRequest = { + viewModel.labelsSelectionCurrentItemLiveData.value = null + viewModel.showLabelsSelectionSheetLiveData.value = false + } ) { + if (currentSavedItemData != null) { + LabelsSelectionSheetContent( + labels = labels, + initialSelectedLabels = currentSavedItemData.labels, + onCancel = { + viewModel.showLabelsSelectionSheetLiveData.value = false + viewModel.labelsSelectionCurrentItemLiveData.value = null + }, + onSave = { + if (it != labels) { + viewModel.updateSavedItemLabels(savedItemID = currentSavedItemData.cardData.savedItemId, labels = it) + } + viewModel.labelsSelectionCurrentItemLiveData.value = null + viewModel.showLabelsSelectionSheetLiveData.value = false + } + ) + } else { + LabelsSelectionSheetContent( + labels = labels, + initialSelectedLabels = viewModel.activeLabelsLiveData.value ?: listOf(), + onCancel = { viewModel.showLabelsSelectionSheetLiveData.value = false }, + onSave = { + viewModel.updateAppliedLabels(it) + viewModel.labelsSelectionCurrentItemLiveData.value = null + viewModel.showLabelsSelectionSheetLiveData.value = false + } + ) + } } } } @OptIn(ExperimentalMaterial3Api::class) @Composable -fun LabelsSelectionSheetContent(viewModel: LibraryViewModel, initialSelectedLabels: List) { +fun LabelsSelectionSheetContent( + labels: List, + initialSelectedLabels: List, + onCancel: () -> Unit, + onSave: (List) -> Unit +) { val listState = rememberLazyListState() - val labels: List by viewModel.savedItemLabelsLiveData.observeAsState(listOf()) val selectedLabels = remember { mutableStateOf(initialSelectedLabels) } Surface( @@ -62,26 +98,19 @@ fun LabelsSelectionSheetContent(viewModel: LibraryViewModel, initialSelectedLabe modifier = Modifier .fillMaxWidth() ) { - TextButton(onClick = { viewModel.showLabelsSelectionSheetLiveData.value = false }) { + TextButton(onClick = onCancel) { Text(text = "Cancel") } Text("Filter by Label", fontWeight = FontWeight.ExtraBold) - TextButton( - onClick = { - selectedLabels.value?.let { - viewModel.updateAppliedLabels(it) - } - viewModel.showLabelsSelectionSheetLiveData.value = false - } - ) { + TextButton(onClick = { onSave(selectedLabels.value) }) { Text(text = "Done") } } } items(labels) { label -> - val isLabelSelected = (selectedLabels.value ?: listOf()).contains(label) + val isLabelSelected = selectedLabels.value.contains(label) Row( horizontalArrangement = Arrangement.SpaceBetween, 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 5808462fb..d2b273654 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 @@ -10,9 +10,9 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import app.omnivore.omnivore.* import app.omnivore.omnivore.dataService.* +import app.omnivore.omnivore.graphql.generated.type.SetLabelsInput import app.omnivore.omnivore.networking.* -import app.omnivore.omnivore.persistence.entities.SavedItemCardDataWithLabels -import app.omnivore.omnivore.persistence.entities.SavedItemLabel +import app.omnivore.omnivore.persistence.entities.* import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.* import java.time.Instant @@ -40,6 +40,7 @@ class LibraryViewModel @Inject constructor( val appliedFilterLiveData = MutableLiveData(SavedItemFilter.INBOX) val appliedSortFilterLiveData = MutableLiveData(SavedItemSortFilter.NEWEST) val showLabelsSelectionSheetLiveData = MutableLiveData(false) + val labelsSelectionCurrentItemLiveData = MutableLiveData(null) val savedItemLabelsLiveData = dataService.db.savedItemLabelDao().getSavedItemLabelsLiveData() val activeLabelsLiveData = MutableLiveData>(listOf()) @@ -261,11 +262,39 @@ class LibraryViewModel @Inject constructor( } } SavedItemAction.EditLabels -> { - Log.d("label", "itemID") + labelsSelectionCurrentItemLiveData.value = itemID + showLabelsSelectionSheetLiveData.value = true } } } + fun updateSavedItemLabels(savedItemID: String, labels: List) { + viewModelScope.launch { + withContext(Dispatchers.IO) { + val input = SetLabelsInput(labelIds = labels.map { it.savedItemLabelId }, pageId = savedItemID) + val networkResult = networker.updateLabelsForSavedItem(input) + + // TODO: assign a server sync status to these + val crossRefs = labels.map { + SavedItemAndSavedItemLabelCrossRef( + savedItemLabelId = it.savedItemLabelId, + savedItemId = savedItemID + ) + } + + dataService.db.savedItemAndSavedItemLabelCrossRefDao().insertAll(crossRefs) + } + } + } + + fun currentSavedItemUnderEdit(): SavedItemCardDataWithLabels? { + labelsSelectionCurrentItemLiveData.value?.let { itemID -> + return itemsLiveData.value?.first { it.cardData.savedItemId == itemID } + } + + return null + } + private fun searchQueryString(): String { var query = "${appliedFilterLiveData.value?.queryString} ${appliedSortFilterLiveData.value?.queryString}" val searchText = searchTextLiveData.value ?: ""