From 4b87f1dd20d1eeaa33f3e1d6cc01400ecd8f8195 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Thu, 20 Apr 2023 17:58:26 +0800 Subject: [PATCH] Implement article actions on the library --- .../ui/library/LibraryNavigationBar.kt | 175 ++++++++++++++---- .../omnivore/ui/library/LibraryView.kt | 51 +++-- .../omnivore/ui/library/LibraryViewModel.kt | 3 +- .../omnivore/ui/library/SavedItemViewModel.kt | 1 + .../omnivore/ui/library/SearchViewModel.kt | 44 ++--- .../ui/savedItemViews/SavedItemCard.kt | 3 +- .../app/src/main/res/drawable/unarchive.xml | 9 + 7 files changed, 211 insertions(+), 75 deletions(-) create mode 100644 android/Omnivore/app/src/main/res/drawable/unarchive.xml diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/library/LibraryNavigationBar.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/library/LibraryNavigationBar.kt index c26e868a1..acdff92e0 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/library/LibraryNavigationBar.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/library/LibraryNavigationBar.kt @@ -9,6 +9,7 @@ import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.* +import androidx.compose.material.icons.outlined.Close import androidx.compose.material.icons.outlined.Delete import androidx.compose.material3.* import androidx.compose.runtime.* @@ -58,49 +59,147 @@ fun LibraryNavigationBar( } }, actions = { - if (actionsMenuItem != null) { - IconButton(onClick = onSearchClicked) { - Icon( - painter = painterResource(id = R.drawable.archive_outline), - contentDescription = null - ) - } - IconButton(onClick = onSearchClicked) { - Icon( - painter = painterResource(id = R.drawable.tag), - contentDescription = null - ) - } - IconButton(onClick = onSearchClicked) { - Icon( - imageVector = Icons.Outlined.Delete, - contentDescription = null - ) - } - IconButton(onClick = onSettingsIconClick) { - Icon( - imageVector = Icons.Default.MoreVert, - contentDescription = null - ) - } - } else { - IconButton(onClick = onSearchClicked) { - Icon( - imageVector = Icons.Filled.Search, - contentDescription = null - ) - } + actionsMenuItem?.let { + IconButton(onClick = { + savedItemViewModel.handleSavedItemAction( + it.savedItemId, + if (it.isArchived) SavedItemAction.Unarchive else SavedItemAction.Archive + ) }) { + if (it.isArchived) { + Icon( + painter = painterResource(id = R.drawable.unarchive), + contentDescription = null + ) + } else { + Icon( + painter = painterResource(id = R.drawable.archive_outline), + contentDescription = null + ) + } + } + IconButton(onClick = { savedItemViewModel.handleSavedItemAction(it.savedItemId, SavedItemAction.EditLabels) }) { + Icon( + painter = painterResource(id = R.drawable.tag), + contentDescription = null + ) + } + IconButton(onClick = { savedItemViewModel.handleSavedItemAction(it.savedItemId, SavedItemAction.Delete) }) { + Icon( + imageVector = Icons.Outlined.Delete, + contentDescription = null + ) + } +// IconButton(onClick = onSettingsIconClick) { +// Icon( +// imageVector = Icons.Default.MoreVert, +// contentDescription = null +// ) +// } + } ?: run { + IconButton(onClick = onSearchClicked) { + Icon( + imageVector = Icons.Filled.Search, + contentDescription = null + ) + } - IconButton(onClick = onSettingsIconClick) { - Icon( - imageVector = Icons.Default.MoreVert, - contentDescription = null - ) + IconButton(onClick = onSettingsIconClick) { + Icon( + imageVector = Icons.Default.MoreVert, + contentDescription = null + ) + } } - } } ) } +// +//@OptIn(ExperimentalMaterial3Api::class) +//@Composable +//fun LibraryBottomBar( +// savedItemViewModel: SavedItemViewModel, +// onSearchClicked: () -> Unit, +// onSettingsIconClick: () -> Unit +//) { +// val actionsMenuItem: SavedItemCardData? by savedItemViewModel.actionsMenuItemLiveData.observeAsState(null) +// +// BottomAppBar( +//// colors = TopAppBarDefaults.topAppBarColors( +//// containerColor = if (actionsMenuItem == null) MaterialTheme.colorScheme.background else MaterialTheme.colorScheme.surfaceVariant +//// ), +//// trailingIcon = { +//// if (actionsMenuItem != null) { +//// IconButton(onClick = { +//// savedItemViewModel.actionsMenuItemLiveData.postValue(null) +//// }) { +//// Icon( +//// imageVector = androidx.compose.material.icons.Icons.Filled.ArrowBack, +//// modifier = Modifier, +//// contentDescription = "Back" +//// ) +//// } +//// } +//// }, +// floatingActionButton = { +// IconButton(onClick = onSearchClicked) { +// Icon( +// imageVector = Icons.Outlined.Close, +// contentDescription = null +// ) +// } +// }, +// actions = { +// actionsMenuItem?.let { +// IconButton(onClick = onSearchClicked) { +// if (it.isArchived) { +// Icon( +// painter = painterResource(id = R.drawable.unarchive), +// contentDescription = null +// ) +// } else { +// Icon( +// painter = painterResource(id = R.drawable.archive_outline), +// contentDescription = null +// ) +// } +// } +// IconButton(onClick = onSearchClicked) { +// Icon( +// painter = painterResource(id = R.drawable.tag), +// contentDescription = null +// ) +// } +// IconButton(onClick = onSearchClicked) { +// Icon( +// imageVector = Icons.Outlined.Delete, +// contentDescription = null +// ) +// } +// IconButton(onClick = onSettingsIconClick) { +// Icon( +// imageVector = Icons.Default.MoreVert, +// contentDescription = null +// ) +// } +// } ?: run { +// IconButton(onClick = onSearchClicked) { +// Icon( +// imageVector = Icons.Filled.Search, +// contentDescription = null +// ) +// } +// +// IconButton(onClick = onSettingsIconClick) { +// Icon( +// imageVector = Icons.Default.MoreVert, +// contentDescription = null +// ) +// } +// } +// } +// ) +//} + @OptIn(ExperimentalComposeUiApi::class, ExperimentalMaterial3Api::class) diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/library/LibraryView.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/library/LibraryView.kt index 516883d48..2b293e348 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/library/LibraryView.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/library/LibraryView.kt @@ -2,30 +2,44 @@ package app.omnivore.omnivore.ui.library import android.content.Intent import android.util.Log +import androidx.activity.compose.BackHandler import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.ModalBottomSheetLayout +import androidx.compose.material.ModalBottomSheetValue +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.MoreVert +import androidx.compose.material.icons.outlined.Delete import androidx.compose.material.pullrefresh.PullRefreshIndicator import androidx.compose.material.pullrefresh.pullRefresh import androidx.compose.material.pullrefresh.rememberPullRefreshState +import androidx.compose.material.rememberModalBottomSheetState 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.draw.clip +import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import androidx.navigation.NavHostController +import app.omnivore.omnivore.R import app.omnivore.omnivore.Routes +import app.omnivore.omnivore.persistence.entities.SavedItemCardData import app.omnivore.omnivore.persistence.entities.SavedItemCardDataWithLabels import app.omnivore.omnivore.ui.components.LabelsSelectionSheet import app.omnivore.omnivore.ui.savedItemViews.SavedItemCard import app.omnivore.omnivore.ui.reader.PDFReaderActivity import app.omnivore.omnivore.ui.reader.WebReaderLoadingContainerActivity +import app.omnivore.omnivore.ui.save.SaveSheetActivityBase import kotlinx.coroutines.flow.distinctUntilChanged @@ -35,6 +49,9 @@ fun LibraryView( libraryViewModel: LibraryViewModel, navController: NavHostController ) { + + val actionsMenuItem: SavedItemCardData? by libraryViewModel.actionsMenuItemLiveData.observeAsState(null) + Scaffold( topBar = { LibraryNavigationBar( @@ -43,19 +60,19 @@ fun LibraryView( onSettingsIconClick = { navController.navigate(Routes.Settings.route) } ) - } + }, ) { paddingValues -> - LibraryViewContent( - libraryViewModel, - modifier = Modifier - .padding( - top = paddingValues.calculateTopPadding() - ) - ) + LibraryViewContent( + libraryViewModel, + modifier = Modifier + .padding( + top = paddingValues.calculateTopPadding() + ) + ) } } -@OptIn(ExperimentalMaterialApi::class) +@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class) @Composable fun LibraryViewContent(libraryViewModel: LibraryViewModel, modifier: Modifier) { val context = LocalContext.current @@ -66,7 +83,9 @@ fun LibraryViewContent(libraryViewModel: LibraryViewModel, modifier: Modifier) { onRefresh = { libraryViewModel.refresh() } ) - val cardsData: List by libraryViewModel.itemsLiveData.observeAsState(listOf()) + val cardsData: List by libraryViewModel.itemsLiveData.observeAsState( + listOf() + ) Box( modifier = Modifier @@ -91,12 +110,18 @@ fun LibraryViewContent(libraryViewModel: LibraryViewModel, modifier: Modifier) { cardData = cardDataWithLabels.cardData, labels = cardDataWithLabels.labels, onClickHandler = { - val activityClass = if (cardDataWithLabels.cardData.isPDF()) PDFReaderActivity::class.java else WebReaderLoadingContainerActivity::class.java + val activityClass = + if (cardDataWithLabels.cardData.isPDF()) PDFReaderActivity::class.java else WebReaderLoadingContainerActivity::class.java val intent = Intent(context, activityClass) intent.putExtra("SAVED_ITEM_SLUG", cardDataWithLabels.cardData.slug) context.startActivity(intent) }, - actionHandler = { libraryViewModel.handleSavedItemAction(cardDataWithLabels.cardData.savedItemId, it) } + actionHandler = { + libraryViewModel.handleSavedItemAction( + cardDataWithLabels.cardData.savedItemId, + it + ) + } ) } } @@ -110,7 +135,7 @@ fun LibraryViewContent(libraryViewModel: LibraryViewModel, modifier: Modifier) { libraryViewModel.loadUsingSearchAPI() } } - + PullRefreshIndicator( refreshing = libraryViewModel.isRefreshing, state = pullRefreshState, 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 6dd2f60da..363d8442b 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 @@ -248,7 +248,7 @@ class LibraryViewModel @Inject constructor( } } - fun handleSavedItemAction(itemID: String, action: SavedItemAction) { + override fun handleSavedItemAction(itemID: String, action: SavedItemAction) { when (action) { SavedItemAction.Delete -> { viewModelScope.launch { @@ -270,6 +270,7 @@ class LibraryViewModel @Inject constructor( showLabelsSelectionSheetLiveData.value = true } } + actionsMenuItemLiveData.postValue(null) } fun updateSavedItemLabels(savedItemID: String, labels: List) { diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/library/SavedItemViewModel.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/library/SavedItemViewModel.kt index fc0f9917c..2c51fdb5b 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/library/SavedItemViewModel.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/library/SavedItemViewModel.kt @@ -8,4 +8,5 @@ interface SavedItemViewModel { val actionsMenuItemLiveData: MutableLiveData get() = MutableLiveData(null) + fun handleSavedItemAction(itemID: String, action: SavedItemAction) } diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/library/SearchViewModel.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/library/SearchViewModel.kt index bbaf0444d..47dea3c67 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/library/SearchViewModel.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/library/SearchViewModel.kt @@ -144,28 +144,28 @@ class SearchViewModel @Inject constructor( isRefreshing.postValue(false) } - fun handleSavedItemAction(itemID: String, action: SavedItemAction) { -// when (action) { -// SavedItemAction.Delete -> { -// viewModelScope.launch { -// dataService.deleteSavedItem(itemID) -// } -// } -// SavedItemAction.Archive -> { -// viewModelScope.launch { -// dataService.archiveSavedItem(itemID) -// } -// } -// SavedItemAction.Unarchive -> { -// viewModelScope.launch { -// dataService.unarchiveSavedItem(itemID) -// } -// } -// SavedItemAction.EditLabels -> { -// labelsSelectionCurrentItemLiveData.value = itemID -// showLabelsSelectionSheetLiveData.value = true -// } -// } + override fun handleSavedItemAction(itemID: String, action: SavedItemAction) { + when (action) { + SavedItemAction.Delete -> { + viewModelScope.launch { + dataService.deleteSavedItem(itemID) + } + } + SavedItemAction.Archive -> { + viewModelScope.launch { + dataService.archiveSavedItem(itemID) + } + } + SavedItemAction.Unarchive -> { + viewModelScope.launch { + dataService.unarchiveSavedItem(itemID) + } + } + SavedItemAction.EditLabels -> { + // TODO + } + } + actionsMenuItemLiveData.postValue(null) } fun updateSavedItemLabels(savedItemID: String, labels: List) { diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/savedItemViews/SavedItemCard.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/savedItemViews/SavedItemCard.kt index f5b7cc8bb..4db46edd1 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/savedItemViews/SavedItemCard.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/savedItemViews/SavedItemCard.kt @@ -41,13 +41,14 @@ fun SavedItemCard(savedItemViewModel: SavedItemViewModel, cardData: SavedItemCar onClick = onClickHandler, onLongClick = { savedItemViewModel.actionsMenuItemLiveData.postValue(cardData) } ) + .fillMaxWidth() ) { Row( horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.Top, modifier = Modifier .fillMaxWidth() - .padding(15.dp) + .padding(20.dp) .background(if (isMenuExpanded) Color.LightGray else Color.Transparent) ) { Column( diff --git a/android/Omnivore/app/src/main/res/drawable/unarchive.xml b/android/Omnivore/app/src/main/res/drawable/unarchive.xml new file mode 100644 index 000000000..922a5617d --- /dev/null +++ b/android/Omnivore/app/src/main/res/drawable/unarchive.xml @@ -0,0 +1,9 @@ + + +