Implement article actions on the library

This commit is contained in:
Jackson Harper
2023-04-20 17:58:26 +08:00
parent 9f99b78f1e
commit 4b87f1dd20
7 changed files with 211 additions and 75 deletions

View File

@ -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)

View File

@ -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<SavedItemCardDataWithLabels> by libraryViewModel.itemsLiveData.observeAsState(listOf())
val cardsData: List<SavedItemCardDataWithLabels> 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,

View File

@ -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<SavedItemLabel>) {

View File

@ -8,4 +8,5 @@ interface SavedItemViewModel {
val actionsMenuItemLiveData: MutableLiveData<SavedItemCardData?>
get() = MutableLiveData<SavedItemCardData?>(null)
fun handleSavedItemAction(itemID: String, action: SavedItemAction)
}

View File

@ -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<SavedItemLabel>) {

View File

@ -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(

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="256"
android:viewportHeight="256">
<path
android:pathData="M208,32L48,32A16,16 0,0 0,32 48L32,208a16,16 0,0 0,16 16L208,224a16,16 0,0 0,16 -16L224,48A16,16 0,0 0,208 32ZM208,48L208,152h-28.7A15.86,15.86 0,0 0,168 156.69L148.69,176L107.31,176L88,156.69A15.86,15.86 0,0 0,76.69 152L48,152L48,48ZM208,208L48,208L48,168L76.69,168L96,187.31A15.86,15.86 0,0 0,107.31 192h41.38A15.86,15.86 0,0 0,160 187.31L179.31,168L208,168v40ZM90.34,125.66a8,8 0,0 1,11.32 -11.32L120,132.69L120,72a8,8 0,0 1,16 0v60.69l18.34,-18.35a8,8 0,0 1,11.32 11.32l-32,32a8,8 0,0 1,-11.32 0Z"
android:fillColor="#000000"/>
</vector>