save labels when saving items

This commit is contained in:
Satindar Dhillon 2023-01-11 22:57:23 -08:00
parent 8b20a0b0be
commit ffeacab409
6 changed files with 99 additions and 47 deletions

View file

@ -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<SavedItemLabel> = mutableListOf()
val crossRefs: MutableList<SavedItemAndSavedItemLabelCrossRef> = 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 }
)
}

View file

@ -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<String>,
val items: List<SavedItem>
val items: List<UpdatesSinceQuery.Node>
)
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

View file

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

View file

@ -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<SavedItemLabel>)
}
@Entity(primaryKeys = ["savedItemLabelId", "savedItemId"])
data class SavedItemAndSavedItemLabelCrossRef(
val savedItemLabelId: String,
@ -38,5 +45,11 @@ data class SavedItemCardDataWithLabels(
val labels: List<SavedItemLabel>
)
@Dao
interface SavedItemAndSavedItemLabelCrossRefDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(items: List<SavedItemAndSavedItemLabelCrossRef>)
}
// has many highlights
// has many savedItems

View file

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

View file

@ -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<List<SavedItemCardData>>(listOf())
val itemsLiveData = dataService.db.savedItemDao().getLibraryLiveData()
val searchItemsLiveData = MutableLiveData<List<SavedItemCardDataWithLabels>>(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