diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/DataService.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/DataService.kt index aadf5ddf9..5e1656982 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/DataService.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/DataService.kt @@ -6,6 +6,9 @@ import app.omnivore.omnivore.networking.Networker import app.omnivore.omnivore.networking.savedItem import app.omnivore.omnivore.networking.savedItemUpdates import app.omnivore.omnivore.persistence.AppDatabase +import app.omnivore.omnivore.persistence.entities.SavedItem +import app.omnivore.omnivore.persistence.entities.SavedItemAndSavedItemLabelCrossRef +import app.omnivore.omnivore.persistence.entities.SavedItemLabel import javax.inject.Inject class DataService @Inject constructor( @@ -21,14 +24,65 @@ class DataService @Inject constructor( suspend fun DataService.sync(since: String, cursor: String?, limit: Int = 15): SavedItemSyncResult { val syncResult = networker.savedItemUpdates(cursor = cursor, limit = limit, since = since) ?: return SavedItemSyncResult.errorResult - db.savedItemDao().insertAll(syncResult.items) + val savedItems = syncResult.items.map { + SavedItem( + savedItemId = it.id, + title = it.title, + createdAt = it.createdAt as String, + savedAt = it.savedAt as String, + readAt = it.readAt as String?, + updatedAt = it.updatedAt as String?, + readingProgress = it.readingProgressPercent, + readingProgressAnchor = it.readingProgressAnchorIndex, + imageURLString = it.image, + pageURLString = it.url, + descriptionText = it.description, + publisherURLString = it.originalArticleUrl, + siteName = it.siteName, + author = it.author, + publishDate = it.publishedAt as String?, + slug = it.slug, + isArchived = it.isArchived, + contentReader = it.contentReader.rawValue, + content = null + ) + } + + db.savedItemDao().insertAll(savedItems) + + val labels: MutableList = mutableListOf() + val crossRefs: MutableList = mutableListOf() + + // save labels + for (item in syncResult.items) { + val itemLabels = (item.labels ?: listOf()).map { + SavedItemLabel( + savedItemLabelId = it.id, + name = it.name, + color = it.color, + createdAt = null, + labelDescription = null + ) + } + + labels.addAll(itemLabels) + + val newCrossRefs = itemLabels.map { + SavedItemAndSavedItemLabelCrossRef(savedItemLabelId = it.savedItemLabelId, savedItemId = item.id) + } + + crossRefs.addAll(newCrossRefs) + } + + db.savedItemLabelDao().insertAll(labels) + db.savedItemAndSavedItemLabelCrossRefDao().insertAll(crossRefs) return SavedItemSyncResult( hasError = false, hasMoreItems = syncResult.hasMoreItems, cursor = syncResult.cursor, count = syncResult.items.size, - savedItemSlugs = syncResult.items.map { it.savedItemId } + savedItemSlugs = syncResult.items.map { it.slug } ) } diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/networking/SavedItemUpdatesQuery.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/networking/SavedItemUpdatesQuery.kt index 01c42f9ea..a50c71ce7 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/networking/SavedItemUpdatesQuery.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/networking/SavedItemUpdatesQuery.kt @@ -8,9 +8,8 @@ import com.apollographql.apollo3.api.Optional data class SavedItemUpdatesQueryResponse( val cursor: String?, val hasMoreItems: Boolean, - val totalCount: Int, val deletedItemIDs: List, - val items: List + val items: List ) suspend fun Networker.savedItemUpdates( @@ -39,36 +38,11 @@ suspend fun Networker.savedItemUpdates( } } - val savedItems = itemNodes.map { - SavedItem( - savedItemId = it.id, - title = it.title, - createdAt = it.createdAt as String, - savedAt = it.savedAt as String, - readAt = it.readAt as String?, - updatedAt = it.updatedAt as String?, - readingProgress = it.readingProgressPercent, - readingProgressAnchor = it.readingProgressAnchorIndex, - imageURLString = it.image, - pageURLString = it.url, - descriptionText = it.description, - publisherURLString = it.originalArticleUrl, - siteName = it.siteName, - author = it.author, - publishDate = it.publishedAt as String?, - slug = it.slug, - isArchived = it.isArchived, - contentReader = it.contentReader.rawValue, - content = null - ) - } - return SavedItemUpdatesQueryResponse( cursor = payload.pageInfo.endCursor, hasMoreItems = payload.pageInfo.hasNextPage, - totalCount = savedItems.size, deletedItemIDs = deletedItemIDs, - items = savedItems + items = itemNodes ) } catch (e: java.lang.Exception) { return null diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/persistence/AppDatabase.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/persistence/AppDatabase.kt index 6062c45a2..2130a2f9a 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/persistence/AppDatabase.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/persistence/AppDatabase.kt @@ -2,12 +2,7 @@ package app.omnivore.omnivore.persistence import androidx.room.Database import androidx.room.RoomDatabase -import app.omnivore.omnivore.persistence.entities.SavedItem -import app.omnivore.omnivore.persistence.entities.SavedItemLabel -import app.omnivore.omnivore.persistence.entities.SavedItemAndSavedItemLabelCrossRef -import app.omnivore.omnivore.persistence.entities.SavedItemDao -import app.omnivore.omnivore.persistence.entities.Viewer -import app.omnivore.omnivore.persistence.entities.ViewerDao +import app.omnivore.omnivore.persistence.entities.* @Database( entities = [ @@ -21,4 +16,6 @@ import app.omnivore.omnivore.persistence.entities.ViewerDao abstract class AppDatabase : RoomDatabase() { abstract fun viewerDao(): ViewerDao abstract fun savedItemDao(): SavedItemDao + abstract fun savedItemLabelDao(): SavedItemLabelDao + abstract fun savedItemAndSavedItemLabelCrossRefDao(): SavedItemAndSavedItemLabelCrossRefDao } diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/persistence/entities/SavedItemLabel.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/persistence/entities/SavedItemLabel.kt index 6979aeb90..26ac468a5 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/persistence/entities/SavedItemLabel.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/persistence/entities/SavedItemLabel.kt @@ -1,5 +1,6 @@ package app.omnivore.omnivore.persistence.entities +import androidx.lifecycle.LiveData import androidx.room.* @Entity @@ -12,6 +13,12 @@ data class SavedItemLabel( val serverSyncStatus: Int = 0 ) +@Dao +interface SavedItemLabelDao { + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insertAll(items: List) +} + @Entity(primaryKeys = ["savedItemLabelId", "savedItemId"]) data class SavedItemAndSavedItemLabelCrossRef( val savedItemLabelId: String, @@ -38,5 +45,11 @@ data class SavedItemCardDataWithLabels( val labels: List ) +@Dao +interface SavedItemAndSavedItemLabelCrossRefDao { + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insertAll(items: List) +} + // has many highlights // has many savedItems 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 51dd4d74a..01ef87ea2 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 @@ -18,9 +18,12 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import androidx.navigation.NavHostController import app.omnivore.omnivore.Routes +import app.omnivore.omnivore.persistence.entities.SavedItemAndSavedItemLabelCrossRef import app.omnivore.omnivore.persistence.entities.SavedItemCardData +import app.omnivore.omnivore.persistence.entities.SavedItemCardDataWithLabels import app.omnivore.omnivore.ui.savedItemViews.SavedItemCard import app.omnivore.omnivore.ui.reader.PDFReaderActivity import kotlinx.coroutines.flow.distinctUntilChanged @@ -70,8 +73,8 @@ fun LibraryViewContent( onRefresh = { libraryViewModel.refresh() } ) - val cardsData: List by libraryViewModel.itemsLiveData.observeAsState(listOf()) - val searchedCardsData: List by libraryViewModel.searchItemsLiveData.observeAsState(listOf()) + val cardsData: List by libraryViewModel.itemsLiveData.observeAsState(listOf()) + val searchedCardsData: List by libraryViewModel.searchItemsLiveData.observeAsState(listOf()) val searchText: String by libraryViewModel.searchTextLiveData.observeAsState("") Box( @@ -88,19 +91,25 @@ fun LibraryViewContent( .fillMaxSize() .padding(horizontal = 6.dp) ) { - items(if (searchText.isNotEmpty()) searchedCardsData else cardsData) { cardData -> + items(if (searchText.isNotEmpty()) searchedCardsData else cardsData) { cardDataWithLabels -> SavedItemCard( - cardData = cardData, + cardData = cardDataWithLabels.cardData, onClickHandler = { - if (cardData.isPDF()) { + if (cardDataWithLabels.cardData.isPDF()) { val intent = Intent(context, PDFReaderActivity::class.java) - intent.putExtra("SAVED_ITEM_SLUG", cardData.slug) + intent.putExtra("SAVED_ITEM_SLUG", cardDataWithLabels.cardData.slug) context.startActivity(intent) } else { - navController.navigate("WebReader/${cardData.slug}") + navController.navigate("WebReader/${cardDataWithLabels.cardData.slug}") } }, - actionHandler = { libraryViewModel.handleSavedItemAction(cardData.savedItemId, it) } + actionHandler = { libraryViewModel.handleSavedItemAction(cardDataWithLabels.cardData.savedItemId, it) } + ) + + Text( + text = "${cardDataWithLabels.labels.size.toString()} labels", + style = MaterialTheme.typography.titleMedium, + lineHeight = 20.sp ) } } 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 31226c063..3dc93ad44 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,6 +10,7 @@ import androidx.lifecycle.viewModelScope import app.omnivore.omnivore.* import app.omnivore.omnivore.networking.* import app.omnivore.omnivore.persistence.entities.SavedItemCardData +import app.omnivore.omnivore.persistence.entities.SavedItemCardDataWithLabels import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.* import java.time.LocalDateTime @@ -30,8 +31,8 @@ class LibraryViewModel @Inject constructor( // Live Data val searchTextLiveData = MutableLiveData("") - val searchItemsLiveData = MutableLiveData>(listOf()) - val itemsLiveData = dataService.db.savedItemDao().getLibraryLiveData() + val searchItemsLiveData = MutableLiveData>(listOf()) + val itemsLiveData = dataService.db.savedItemDao().getLibraryLiveDataWithLabels() var isRefreshing by mutableStateOf(false) @@ -113,7 +114,11 @@ class LibraryViewModel @Inject constructor( return } - searchItemsLiveData.postValue(searchResult.cardsData) + val cardsDataWithLabels = searchResult.cardsData.map { + SavedItemCardDataWithLabels(cardData = it, labels = listOf()) + } + + searchItemsLiveData.postValue(cardsDataWithLabels) CoroutineScope(Dispatchers.Main).launch { isRefreshing = false