diff --git a/android/Omnivore/app/build.gradle b/android/Omnivore/app/build.gradle index a864109ad..67db8bfc6 100644 --- a/android/Omnivore/app/build.gradle +++ b/android/Omnivore/app/build.gradle @@ -17,8 +17,8 @@ android { applicationId "app.omnivore.omnivore" minSdk 26 targetSdk 33 - versionCode 33 - versionName "0.0.33" + versionCode 44 + versionName "0.0.44" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/persistence/entities/SavedItem.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/persistence/entities/SavedItem.kt index f61165863..38cd552d6 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/persistence/entities/SavedItem.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/persistence/entities/SavedItem.kt @@ -2,6 +2,7 @@ package app.omnivore.omnivore.persistence.entities import androidx.core.net.toUri import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData import androidx.room.* import app.omnivore.omnivore.BuildConfig import app.omnivore.omnivore.graphql.generated.SearchQuery @@ -160,9 +161,73 @@ interface SavedItemDao { "ORDER BY publishDate DESC" ) fun getLibraryLiveDataSortedByRecentlyPublished(archiveFilter: Int): LiveData> + + @Transaction + @Query( + "SELECT ${SavedItemQueryConstants.libraryColumns} " + + "FROM SavedItem " + + "LEFT OUTER JOIN SavedItemAndSavedItemLabelCrossRef on SavedItem.savedItemId = SavedItemAndSavedItemLabelCrossRef.savedItemId " + + "LEFT OUTER JOIN SavedItemAndHighlightCrossRef on SavedItem.savedItemId = SavedItemAndHighlightCrossRef.savedItemId " + + + "LEFT OUTER JOIN SavedItemLabel on SavedItemLabel.savedItemLabelId = SavedItemAndSavedItemLabelCrossRef.savedItemLabelId " + + "LEFT OUTER JOIN Highlight on highlight.highlightId = SavedItemAndHighlightCrossRef.highlightId " + + + "WHERE SavedItem.serverSyncStatus != 2 " + + "AND SavedItem.isArchived != :archiveFilter " + + "AND SavedItem.contentReader IN (:allowedContentReaders) " + + "AND CASE WHEN :hasRequiredLabels THEN SavedItemLabel.name in (:requiredLabels) ELSE 1 END " + + "AND CASE WHEN :hasExcludedLabels THEN SavedItemLabel.name is NULL OR SavedItemLabel.name not in (:excludedLabels) ELSE 1 END " + + + "GROUP BY SavedItem.savedItemId " + + + "ORDER BY \n" + + "CASE WHEN :sortKey = 'newest' THEN SavedItem.savedAt END DESC,\n" + + "CASE WHEN :sortKey = 'oldest' THEN SavedItem.savedAt END ASC,\n" + + + "CASE WHEN :sortKey = 'recentlyRead' THEN SavedItem.readAt END DESC,\n" + + "CASE WHEN :sortKey = 'recentlyPublished' THEN SavedItem.publishDate END DESC" + ) + fun _filteredLibraryData(archiveFilter: Int, sortKey: String, hasRequiredLabels: Int, hasExcludedLabels: Int, requiredLabels: List, excludedLabels: List, allowedContentReaders: List): LiveData> + + fun filteredLibraryData(archiveFilter: Int, sortKey: String, requiredLabels: List, excludedLabels: List, allowedContentReaders: List): LiveData> { + return _filteredLibraryData( + archiveFilter = archiveFilter, + sortKey = sortKey, + hasRequiredLabels = requiredLabels.size, + hasExcludedLabels = excludedLabels.size, + requiredLabels = requiredLabels, + excludedLabels = excludedLabels, + allowedContentReaders = allowedContentReaders + ) + } } + + object SavedItemQueryConstants { const val columns = "savedItemId, slug, publisherURLString, title, author, imageURLString, isArchived, pageURLString, contentReader, savedAt, readingProgress, wordsCount" + const val libraryColumns = "SavedItem.savedItemId, " + + "SavedItem.slug, " + + "SavedItem.createdAt, " + + + "SavedItem.publisherURLString, " + + "SavedItem.title, " + + "SavedItem.author, " + + "SavedItem.imageURLString, " + + "SavedItem.isArchived, " + + "SavedItem.pageURLString, " + + "SavedItem.contentReader, " + + "SavedItem.savedAt, " + + "SavedItem.readingProgress, " + + "SavedItem.readingProgressAnchor, " + + "SavedItem.serverSyncStatus, " + + + "SavedItem.wordsCount, " + + "SavedItemLabel.savedItemLabelId, " + + "SavedItemLabel.name, " + + "SavedItemLabel.color, " + + "Highlight.highlightId, " + + "Highlight.shortId, " + + "Highlight.createdByMe " } 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 cf0ab9623..f441e0172 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 @@ -76,7 +76,7 @@ fun LabelsSelectionSheet(viewModel: LibraryViewModel) { isLibraryMode = false, onSave = { if (it != labels) { - viewModel.updateSavedItemLabels(savedItemID = currentSavedItemData.cardData.savedItemId, labels = it) + viewModel.updateSavedItemLabels(savedItemID = currentSavedItemData.savedItem.savedItemId, labels = it) } viewModel.labelsSelectionCurrentItemLiveData.value = null viewModel.showLabelsSelectionSheetLiveData.value = false 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 acdff92e0..afd43845b 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 @@ -27,6 +27,7 @@ import androidx.navigation.NavHostController import app.omnivore.omnivore.R import app.omnivore.omnivore.persistence.entities.SavedItemCardData import app.omnivore.omnivore.persistence.entities.SavedItemCardDataWithLabels +import app.omnivore.omnivore.persistence.entities.SavedItemWithLabelsAndHighlights @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -35,7 +36,7 @@ fun LibraryNavigationBar( onSearchClicked: () -> Unit, onSettingsIconClick: () -> Unit ) { - val actionsMenuItem: SavedItemCardData? by savedItemViewModel.actionsMenuItemLiveData.observeAsState(null) + val actionsMenuItem: SavedItemWithLabelsAndHighlights? by savedItemViewModel.actionsMenuItemLiveData.observeAsState(null) TopAppBar( title = { @@ -62,10 +63,10 @@ fun LibraryNavigationBar( actionsMenuItem?.let { IconButton(onClick = { savedItemViewModel.handleSavedItemAction( - it.savedItemId, - if (it.isArchived) SavedItemAction.Unarchive else SavedItemAction.Archive + it.savedItem.savedItemId, + if (it.savedItem.isArchived) SavedItemAction.Unarchive else SavedItemAction.Archive ) }) { - if (it.isArchived) { + if (it.savedItem.isArchived) { Icon( painter = painterResource(id = R.drawable.unarchive), contentDescription = null @@ -77,13 +78,13 @@ fun LibraryNavigationBar( ) } } - IconButton(onClick = { savedItemViewModel.handleSavedItemAction(it.savedItemId, SavedItemAction.EditLabels) }) { + IconButton(onClick = { savedItemViewModel.handleSavedItemAction(it.savedItem.savedItemId, SavedItemAction.EditLabels) }) { Icon( painter = painterResource(id = R.drawable.tag), contentDescription = null ) } - IconButton(onClick = { savedItemViewModel.handleSavedItemAction(it.savedItemId, SavedItemAction.Delete) }) { + IconButton(onClick = { savedItemViewModel.handleSavedItemAction(it.savedItem.savedItemId, SavedItemAction.Delete) }) { Icon( imageVector = Icons.Outlined.Delete, contentDescription = null 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 2b293e348..3d9596578 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 @@ -35,6 +35,7 @@ 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.persistence.entities.SavedItemWithLabelsAndHighlights import app.omnivore.omnivore.ui.components.LabelsSelectionSheet import app.omnivore.omnivore.ui.savedItemViews.SavedItemCard import app.omnivore.omnivore.ui.reader.PDFReaderActivity @@ -49,9 +50,6 @@ fun LibraryView( libraryViewModel: LibraryViewModel, navController: NavHostController ) { - - val actionsMenuItem: SavedItemCardData? by libraryViewModel.actionsMenuItemLiveData.observeAsState(null) - Scaffold( topBar = { LibraryNavigationBar( @@ -83,7 +81,7 @@ fun LibraryViewContent(libraryViewModel: LibraryViewModel, modifier: Modifier) { onRefresh = { libraryViewModel.refresh() } ) - val cardsData: List by libraryViewModel.itemsLiveData.observeAsState( + val cardsData: List by libraryViewModel.itemsLiveData.observeAsState( listOf() ) @@ -107,18 +105,17 @@ fun LibraryViewContent(libraryViewModel: LibraryViewModel, modifier: Modifier) { items(cardsData) { cardDataWithLabels -> SavedItemCard( savedItemViewModel = libraryViewModel, - cardData = cardDataWithLabels.cardData, - labels = cardDataWithLabels.labels, + savedItem = cardDataWithLabels, onClickHandler = { val activityClass = - if (cardDataWithLabels.cardData.isPDF()) PDFReaderActivity::class.java else WebReaderLoadingContainerActivity::class.java + if (cardDataWithLabels.savedItem.contentReader == "PDF") PDFReaderActivity::class.java else WebReaderLoadingContainerActivity::class.java val intent = Intent(context, activityClass) - intent.putExtra("SAVED_ITEM_SLUG", cardDataWithLabels.cardData.slug) + intent.putExtra("SAVED_ITEM_SLUG", cardDataWithLabels.savedItem.slug) context.startActivity(intent) }, actionHandler = { libraryViewModel.handleSavedItemAction( - cardDataWithLabels.cardData.savedItemId, + cardDataWithLabels.savedItem.savedItemId, 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 dd2313693..cc4bb85ed 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 @@ -4,10 +4,7 @@ import android.util.Log import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue -import androidx.lifecycle.MediatorLiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import androidx.lifecycle.* import app.omnivore.omnivore.* import app.omnivore.omnivore.dataService.* import app.omnivore.omnivore.graphql.generated.type.CreateLabelInput @@ -38,8 +35,14 @@ class LibraryViewModel @Inject constructor( private var receivedIdx = 0 // Live Data - private var itemsLiveDataInternal = dataService.libraryLiveData(SavedItemFilter.INBOX, SavedItemSortFilter.NEWEST, listOf()) - val itemsLiveData = MediatorLiveData>() + private var itemsLiveDataInternal = dataService.db.savedItemDao().filteredLibraryData( + archiveFilter = 1, + sortKey = "newest", + requiredLabels = listOf(), + excludedLabels = listOf(), + allowedContentReaders = listOf("WEB", "PDF", "EPUB") + ) + val itemsLiveData = MediatorLiveData>() val appliedFilterLiveData = MutableLiveData(SavedItemFilter.INBOX) val appliedSortFilterLiveData = MutableLiveData(SavedItemSortFilter.NEWEST) val showLabelsSelectionSheetLiveData = MutableLiveData(false) @@ -47,7 +50,7 @@ class LibraryViewModel @Inject constructor( val savedItemLabelsLiveData = dataService.db.savedItemLabelDao().getSavedItemLabelsLiveData() val activeLabelsLiveData = MutableLiveData>(listOf()) - override val actionsMenuItemLiveData = MutableLiveData(null) + override val actionsMenuItemLiveData = MutableLiveData(null) var isRefreshing by mutableStateOf(false) var hasLoadedInitialFilters = false @@ -62,25 +65,27 @@ class LibraryViewModel @Inject constructor( } } - 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") - } - } - } +// 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") +// } +// +// handleFilterChanges() +// } +// } +// } viewModelScope.launch { handleFilterChanges() @@ -175,11 +180,56 @@ class LibraryViewModel @Inject constructor( } } - suspend fun handleFilterChanges() { + fun sortKey(appliedSortKey: String) { + when(appliedSortKey) { + + } + } + fun handleFilterChanges() { if (appliedSortFilterLiveData.value != null && appliedFilterLiveData.value != null) { - println("PERFORMING A FILTER CHANGE") - itemsLiveDataInternal = dataService.libraryLiveData(appliedFilterLiveData.value!!, appliedSortFilterLiveData.value!!, activeLabelsLiveData.value ?: listOf()) - itemsLiveData.removeSource(itemsLiveDataInternal) + val applied = appliedFilterLiveData.value + val sortKey = when (appliedSortFilterLiveData.value) { + SavedItemSortFilter.NEWEST -> "newest" + SavedItemSortFilter.OLDEST -> "oldest" + SavedItemSortFilter.RECENTLY_READ -> "recentlyRead" + SavedItemSortFilter.RECENTLY_PUBLISHED -> "recentlyPublished" + else -> "newest" + } + + val archiveFilter = when (appliedFilterLiveData.value) { + SavedItemFilter.ARCHIVED -> 0 + else -> 1 + } + + val allowedContentReaders = when(appliedFilterLiveData.value) { + SavedItemFilter.FILES -> listOf("PDF", "EPUB") + else -> listOf("WEB", "PDF", "EPUB") + } + + var requiredLabels = when(appliedFilterLiveData.value) { + SavedItemFilter.NEWSLETTERS -> listOf("Newsletter") + else -> (activeLabelsLiveData.value ?: listOf()).map { it.name } + } + activeLabelsLiveData.value?.let { + requiredLabels = requiredLabels + it.map { it.name } + } + + + val excludeLabels = when(appliedFilterLiveData.value) { + SavedItemFilter.READ_LATER -> listOf("Newsletter") + else -> listOf() + } + + val newData = dataService.db.savedItemDao().filteredLibraryData( + archiveFilter = archiveFilter, + sortKey = sortKey, + requiredLabels = requiredLabels, + excludedLabels = excludeLabels, + allowedContentReaders = allowedContentReaders + ) + + itemsLiveData.removeSource(itemsLiveDataInternal) + itemsLiveDataInternal = newData itemsLiveData.addSource(itemsLiveDataInternal, itemsLiveData::setValue) } } @@ -295,9 +345,9 @@ class LibraryViewModel @Inject constructor( } } - fun currentSavedItemUnderEdit(): SavedItemCardDataWithLabels? { + fun currentSavedItemUnderEdit(): SavedItemWithLabelsAndHighlights? { labelsSelectionCurrentItemLiveData.value?.let { itemID -> - return itemsLiveData.value?.first { it.cardData.savedItemId == itemID } + return itemsLiveData.value?.first { it.savedItem.savedItemId == itemID } } return null 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 2c51fdb5b..1d2728d8a 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 @@ -2,11 +2,12 @@ package app.omnivore.omnivore.ui.library import androidx.lifecycle.MutableLiveData import app.omnivore.omnivore.persistence.entities.SavedItemCardData +import app.omnivore.omnivore.persistence.entities.SavedItemWithLabelsAndHighlights interface SavedItemViewModel { - val actionsMenuItemLiveData: MutableLiveData - get() = MutableLiveData(null) + 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/SearchView.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/library/SearchView.kt index 7a95a492f..f2aaa71d6 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/library/SearchView.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/library/SearchView.kt @@ -24,6 +24,7 @@ import androidx.navigation.NavHostController import app.omnivore.omnivore.R import app.omnivore.omnivore.persistence.entities.SavedItemCardData import app.omnivore.omnivore.persistence.entities.SavedItemCardDataWithLabels +import app.omnivore.omnivore.persistence.entities.SavedItemWithLabelsAndHighlights import app.omnivore.omnivore.ui.reader.WebReaderLoadingContainerActivity import app.omnivore.omnivore.persistence.entities.TypeaheadCardData import app.omnivore.omnivore.ui.reader.PDFReaderActivity @@ -39,7 +40,7 @@ fun SearchView( val isRefreshing: Boolean by viewModel.isRefreshing.observeAsState(false) val typeaheadMode: Boolean by viewModel.typeaheadMode.observeAsState(true) val searchText: String by viewModel.searchTextLiveData.observeAsState("") - val actionsMenuItem: SavedItemCardData? by viewModel.actionsMenuItemLiveData.observeAsState(null) + val actionsMenuItem: SavedItemWithLabelsAndHighlights? by viewModel.actionsMenuItemLiveData.observeAsState(null) Scaffold( topBar = { @@ -174,7 +175,7 @@ fun SearchViewContent(viewModel: SearchViewModel, modifier: Modifier) { val context = LocalContext.current val listState = rememberLazyListState() - val cardsData: List by viewModel.itemsLiveData.observeAsState(listOf()) + val cardsData: List by viewModel.itemsLiveData.observeAsState(listOf()) LazyColumn( state = listState, @@ -188,15 +189,14 @@ fun SearchViewContent(viewModel: SearchViewModel, modifier: Modifier) { items(cardsData) { cardDataWithLabels -> SavedItemCard( savedItemViewModel = viewModel, - cardData = cardDataWithLabels.cardData, - labels = cardDataWithLabels.labels, + savedItem = cardDataWithLabels, onClickHandler = { - val activityClass = if (cardDataWithLabels.cardData.isPDF()) PDFReaderActivity::class.java else WebReaderLoadingContainerActivity::class.java + val activityClass = if (cardDataWithLabels.savedItem.contentReader == "PDF") PDFReaderActivity::class.java else WebReaderLoadingContainerActivity::class.java val intent = Intent(context, activityClass) - intent.putExtra("SAVED_ITEM_SLUG", cardDataWithLabels.cardData.slug) + intent.putExtra("SAVED_ITEM_SLUG", cardDataWithLabels.savedItem.slug) context.startActivity(intent) }, - actionHandler = { viewModel.handleSavedItemAction(cardDataWithLabels.cardData.savedItemId, it) } + actionHandler = { viewModel.handleSavedItemAction(cardDataWithLabels.savedItem.savedItemId, it) } ) } } 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 02978e380..8db931e1a 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 @@ -42,9 +42,9 @@ class SearchViewModel @Inject constructor( val typeaheadMode = MutableLiveData(true) val searchTextLiveData = MutableLiveData("") val searchItemsLiveData = MutableLiveData>(listOf()) - val itemsLiveData = MediatorLiveData>() + val itemsLiveData = MediatorLiveData>() - override val actionsMenuItemLiveData = MutableLiveData(null) + override val actionsMenuItemLiveData = MutableLiveData(null) fun updateSearchText(text: String) { typeaheadMode.postValue(true) @@ -88,7 +88,9 @@ class SearchViewModel @Inject constructor( } } - val newItems = result.savedItems.map { + val newItems = result.savedItems + /* + .map { SavedItemCardDataWithLabels( cardData = SavedItemCardData( savedItemId = it.savedItem.savedItemId, @@ -107,6 +109,7 @@ class SearchViewModel @Inject constructor( labels = listOf() ) } + */ itemsLiveData.value?.let{ itemsLiveData.postValue(newItems + it) 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 2c79a56c8..2cfc42db2 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 @@ -22,6 +22,7 @@ import androidx.compose.ui.unit.* import app.omnivore.omnivore.R import app.omnivore.omnivore.persistence.entities.SavedItemCardData import app.omnivore.omnivore.persistence.entities.SavedItemLabel +import app.omnivore.omnivore.persistence.entities.SavedItemWithLabelsAndHighlights import app.omnivore.omnivore.ui.components.LabelChipColors import app.omnivore.omnivore.ui.library.LibraryViewModel import app.omnivore.omnivore.ui.library.SavedItemAction @@ -31,18 +32,16 @@ import coil.compose.rememberAsyncImagePainter @OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class, ) @Composable -fun SavedItemCard(savedItemViewModel: SavedItemViewModel, cardData: SavedItemCardData, labels: List, onClickHandler: () -> Unit, actionHandler: (SavedItemAction) -> Unit) { +fun SavedItemCard(savedItemViewModel: SavedItemViewModel, savedItem: SavedItemWithLabelsAndHighlights, onClickHandler: () -> Unit, actionHandler: (SavedItemAction) -> Unit) { val listState = rememberLazyListState() - val actionsMenuItem: SavedItemCardData? by savedItemViewModel.actionsMenuItemLiveData.observeAsState(null) - var isFocused = actionsMenuItem?.savedItemId == cardData.savedItemId - + val actionsMenuItem: SavedItemWithLabelsAndHighlights? by savedItemViewModel.actionsMenuItemLiveData.observeAsState(null) Column( modifier = Modifier .combinedClickable( onClick = onClickHandler, - onLongClick = { savedItemViewModel.actionsMenuItemLiveData.postValue(cardData) } + onLongClick = { savedItemViewModel.actionsMenuItemLiveData.postValue(savedItem) } ) .fillMaxWidth() ) { @@ -61,10 +60,10 @@ fun SavedItemCard(savedItemViewModel: SavedItemViewModel, cardData: SavedItemCar .padding(end = 20.dp) .defaultMinSize(minHeight = 55.dp) ) { - readInfo(item = cardData) + readInfo(item = savedItem) Text( - text = cardData.title, + text = savedItem.savedItem.title, style = TextStyle( fontSize = 18.sp, fontWeight = FontWeight.SemiBold @@ -73,9 +72,9 @@ fun SavedItemCard(savedItemViewModel: SavedItemViewModel, cardData: SavedItemCar lineHeight = 20.sp ) - if (cardData.author != null && cardData.author != "") { + if (savedItem.savedItem.author != null && savedItem.savedItem.author != "") { Text( - text = byline(cardData), + text = byline(savedItem), style = TextStyle( fontSize = 15.sp, fontWeight = FontWeight.Normal, @@ -88,7 +87,7 @@ fun SavedItemCard(savedItemViewModel: SavedItemViewModel, cardData: SavedItemCar } Image( - painter = rememberAsyncImagePainter(cardData.imageURLString), + painter = rememberAsyncImagePainter(savedItem.savedItem.imageURLString), contentDescription = "Image associated with saved item", modifier = Modifier .size(55.dp, 73.dp) @@ -105,7 +104,7 @@ fun SavedItemCard(savedItemViewModel: SavedItemViewModel, cardData: SavedItemCar modifier = Modifier .padding(start = 10.dp, bottom = 5.dp, end = 10.dp) ) { - items(labels.sortedBy { it.name }) { label -> + items(savedItem.labels.sortedBy { it.name }) { label -> val chipColors = LabelChipColors.fromHex(label.color) SuggestionChip( @@ -127,12 +126,12 @@ fun SavedItemCard(savedItemViewModel: SavedItemViewModel, cardData: SavedItemCar } } -fun byline(item: SavedItemCardData): String { - item.author?.let { - return item.author +fun byline(item: SavedItemWithLabelsAndHighlights): String { + item.savedItem.author?.let { + return item.savedItem.author } - val publisherDisplayName = item.publisherDisplayName() + val publisherDisplayName = item.savedItem.publisherDisplayName() publisherDisplayName?.let { return publisherDisplayName } @@ -149,8 +148,8 @@ fun byline(item: SavedItemCardData): String { // return Int64(result) //} -fun estimatedReadingTime(item: SavedItemCardData): String { - item.wordsCount?.let { +fun estimatedReadingTime(item: SavedItemWithLabelsAndHighlights): String { + item.savedItem.wordsCount?.let { if (it > 0) { val readLen = Math.max(1, it / 235) return "$readLen MIN READ • " @@ -159,11 +158,11 @@ fun estimatedReadingTime(item: SavedItemCardData): String { return "" } -fun readingProgress(item: SavedItemCardData): String { +fun readingProgress(item: SavedItemWithLabelsAndHighlights): String { // If there is no wordsCount don't show progress because it will make no sense - item.wordsCount?.let { + item.savedItem.wordsCount?.let { if (it > 0) { - val intVal = item.readingProgress.toInt() + val intVal = item.savedItem.readingProgress.toInt() return "$intVal%" } } @@ -201,7 +200,7 @@ fun readingProgress(item: SavedItemCardData): String { //} @Composable -fun readInfo(item: SavedItemCardData) { +fun readInfo(item: SavedItemWithLabelsAndHighlights) { Row( modifier = Modifier .fillMaxWidth() @@ -223,7 +222,7 @@ fun readInfo(item: SavedItemCardData) { style = TextStyle( fontSize = 11.sp, fontWeight = FontWeight.Medium, - color = if (item.readingProgress > 1) colorResource(R.color.green_55B938) else colorResource(R.color.gray_898989) + color = if (item.savedItem.readingProgress > 1) colorResource(R.color.green_55B938) else colorResource(R.color.gray_898989) ), maxLines = 1, overflow = TextOverflow.Ellipsis