mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
save labels when saving items
This commit is contained in:
parent
8b20a0b0be
commit
ffeacab409
6 changed files with 99 additions and 47 deletions
|
|
@ -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 }
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue