From 21ab6dd9cd241d4112c60dffaf439139f1cc546b Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Tue, 14 Mar 2023 09:38:26 -0700 Subject: [PATCH] handle label chages from within web reader activity --- .../ui/components/LabelsSelectionSheet.kt | 39 ++++++++-- .../ui/reader/WebReaderLoadingContainer.kt | 4 ++ .../omnivore/ui/reader/WebReaderViewModel.kt | 71 +++++++++++++++++-- 3 files changed, 105 insertions(+), 9 deletions(-) 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 41a235693..cf0ab9623 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 @@ -1,6 +1,5 @@ package app.omnivore.omnivore.ui.components -import android.util.Log import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* @@ -11,21 +10,50 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.AddCircle import androidx.compose.material.icons.filled.Check -import androidx.compose.material.icons.filled.CheckCircle 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.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import app.omnivore.omnivore.persistence.entities.SavedItemLabel import app.omnivore.omnivore.ui.library.LibraryViewModel -import app.omnivore.omnivore.ui.reader.WebPreferencesView +import app.omnivore.omnivore.ui.reader.WebReaderParams import app.omnivore.omnivore.ui.reader.WebReaderViewModel +@Composable +fun WebReaderLabelsSelectionSheet(viewModel: WebReaderViewModel) { + val isActive: Boolean by viewModel.showLabelsSelectionSheetLiveData.observeAsState(false) + val labels: List by viewModel.savedItemLabelsLiveData.observeAsState(listOf()) + val webReaderParams: WebReaderParams? by viewModel.webReaderParamsLiveData.observeAsState(null) + + if (isActive) { + Dialog(onDismissRequest = { + viewModel.showLabelsSelectionSheetLiveData.value = false + } ) { + LabelsSelectionSheetContent( + labels = labels, + initialSelectedLabels = webReaderParams?.labels ?: listOf(), + onCancel = { + viewModel.showLabelsSelectionSheetLiveData.value = false + }, + isLibraryMode = false, + onSave = { + if (it != labels) { + viewModel.updateSavedItemLabels(savedItemID = webReaderParams?.item?.savedItemId ?: "", labels = it) + } + viewModel.showLabelsSelectionSheetLiveData.value = false + }, + onCreateLabel = { newLabelName, labelHexValue -> + viewModel.createNewSavedItemLabel(newLabelName, labelHexValue) + } + ) + } + } +} + @Composable fun LabelsSelectionSheet(viewModel: LibraryViewModel) { val isActive: Boolean by viewModel.showLabelsSelectionSheetLiveData.observeAsState(false) @@ -150,7 +178,8 @@ fun LabelsSelectionSheetContent( .fillMaxWidth() .clickable { if (isLabelSelected) { - selectedLabels.value = selectedLabels.value.filter { it.savedItemLabelId != label.savedItemLabelId } + selectedLabels.value = + selectedLabels.value.filter { it.savedItemLabelId != label.savedItemLabelId } } else { selectedLabels.value = selectedLabels.value + listOf(label) } diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderLoadingContainer.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderLoadingContainer.kt index d87b1cf70..6d6d85228 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderLoadingContainer.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderLoadingContainer.kt @@ -30,7 +30,9 @@ import androidx.compose.ui.unit.dp import androidx.core.view.ViewCompat import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat +import androidx.lifecycle.viewmodel.compose.viewModel import app.omnivore.omnivore.MainActivity +import app.omnivore.omnivore.ui.components.WebReaderLabelsSelectionSheet import app.omnivore.omnivore.ui.savedItemViews.SavedItemContextMenu import app.omnivore.omnivore.ui.theme.OmnivoreTheme import com.google.accompanist.systemuicontroller.rememberSystemUiController @@ -188,6 +190,8 @@ fun WebReaderLoadingContainer(slug: String? = null, requestID: String? = null, o } ) } + + WebReaderLabelsSelectionSheet(webReaderViewModel) } LaunchedEffect(shouldPopView) { diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderViewModel.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderViewModel.kt index d3de2c1aa..bec21906c 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderViewModel.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderViewModel.kt @@ -12,8 +12,12 @@ import androidx.lifecycle.viewModelScope import app.omnivore.omnivore.DatastoreKeys import app.omnivore.omnivore.DatastoreRepository import app.omnivore.omnivore.dataService.* +import app.omnivore.omnivore.graphql.generated.type.CreateLabelInput +import app.omnivore.omnivore.graphql.generated.type.SetLabelsInput import app.omnivore.omnivore.persistence.entities.SavedItem import app.omnivore.omnivore.networking.* +import app.omnivore.omnivore.persistence.entities.SavedItemAndSavedItemLabelCrossRef +import app.omnivore.omnivore.persistence.entities.SavedItemLabel import app.omnivore.omnivore.ui.library.SavedItemAction import com.google.gson.Gson import dagger.hilt.android.lifecycle.HiltViewModel @@ -23,7 +27,8 @@ import javax.inject.Inject data class WebReaderParams( val item: SavedItem, - val articleContent: ArticleContent + val articleContent: ArticleContent, + val labels: List ) data class AnnotationWebViewMessage( @@ -46,12 +51,16 @@ class WebReaderViewModel @Inject constructor( val shouldPopViewLiveData = MutableLiveData(false) val hasFetchError = MutableLiveData(false) val currentToolbarHeightLiveData = MutableLiveData(0.0f) + val showLabelsSelectionSheetLiveData = MutableLiveData(false) + val savedItemLabelsLiveData = dataService.db.savedItemLabelDao().getSavedItemLabelsLiveData() var hasTappedExistingHighlight = false var lastTapCoordinates: TapCoordinates? = null private var isLoading = false + private var slug: String? = null fun loadItem(slug: String?, requestID: String?) { + this.slug = slug if (isLoading || webReaderParamsLiveData.value != null) { return } isLoading = true Log.d("reader", "load item called") @@ -88,6 +97,7 @@ class WebReaderViewModel @Inject constructor( val isSuccessful = webReaderParams?.articleContent?.contentStatus == "SUCCEEDED" if (webReaderParams != null && isSuccessful) { + this.slug = webReaderParams.item.slug webReaderParamsLiveData.postValue(webReaderParams) isLoading = false } else if (requestCount < 7) { @@ -114,7 +124,13 @@ class WebReaderViewModel @Inject constructor( ) Log.d("sync", "data loaded from db") - webReaderParamsLiveData.postValue(WebReaderParams(persistedItem.savedItem, articleContent)) + webReaderParamsLiveData.postValue( + WebReaderParams( + persistedItem.savedItem, + articleContent, + persistedItem.labels + ) + ) } isLoading = false } @@ -134,7 +150,7 @@ class WebReaderViewModel @Inject constructor( labelsJSONString = Gson().toJson(articleQueryResult.labels) ) - return WebReaderParams(article, articleContent) + return WebReaderParams(article, articleContent, articleQueryResult.labels) } fun handleSavedItemAction(itemID: String, action: SavedItemAction) { @@ -158,7 +174,7 @@ class WebReaderViewModel @Inject constructor( } } SavedItemAction.EditLabels -> { - Log.d("label", "itemID") + showLabelsSelectionSheetLiveData.value = true } } } @@ -317,4 +333,51 @@ class WebReaderViewModel @Inject constructor( val script = "var event = new Event('updateFontFamily');event.fontFamily = '${font.rawValue}';document.dispatchEvent(event);" enqueueScript(script) } + + 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 + ) + } + + // Remove all labels first + dataService.db.savedItemAndSavedItemLabelCrossRefDao().deleteRefsBySavedItemId(savedItemID) + + // Add back the current labels + dataService.db.savedItemAndSavedItemLabelCrossRefDao().insertAll(crossRefs) + + slug?.let { + loadItemFromDB(it) + } + } + } + } + + fun createNewSavedItemLabel(labelName: String, hexColorValue: String) { + viewModelScope.launch { + withContext(Dispatchers.IO) { + val newLabel = networker.createNewLabel(CreateLabelInput(color = hexColorValue, name = labelName)) + + newLabel?.let { + val savedItemLabel = SavedItemLabel( + savedItemLabelId = it.id, + name = it.name, + color = it.color, + createdAt = it.createdAt as String?, + labelDescription = it.description + ) + + dataService.db.savedItemLabelDao().insertAll(listOf(savedItemLabel)) + } + } + } + } }