mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
load article content from sql
This commit is contained in:
parent
ffeacab409
commit
8319fb67ca
7 changed files with 119 additions and 23 deletions
|
|
@ -1,12 +1,14 @@
|
|||
package app.omnivore.omnivore
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.room.Room
|
||||
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.SavedItemAndHighlightCrossRef
|
||||
import app.omnivore.omnivore.persistence.entities.SavedItemAndSavedItemLabelCrossRef
|
||||
import app.omnivore.omnivore.persistence.entities.SavedItemLabel
|
||||
import javax.inject.Inject
|
||||
|
|
@ -89,7 +91,28 @@ suspend fun DataService.sync(since: String, cursor: String?, limit: Int = 15): S
|
|||
suspend fun DataService.syncSavedItemContent(slug: String) {
|
||||
val syncResult = networker.savedItem(slug)
|
||||
|
||||
// syncResult.item
|
||||
val savedItem = syncResult.item ?: return
|
||||
db.savedItemDao().insert(savedItem)
|
||||
|
||||
// Persist Labels
|
||||
db.savedItemLabelDao().insertAll(syncResult.labels)
|
||||
|
||||
val labelCrossRefs = syncResult.labels.map {
|
||||
SavedItemAndSavedItemLabelCrossRef(savedItemLabelId = it.savedItemLabelId, savedItemId = savedItem.savedItemId)
|
||||
}
|
||||
|
||||
db.savedItemAndSavedItemLabelCrossRefDao().insertAll(labelCrossRefs)
|
||||
|
||||
// Persist Highlights
|
||||
db.highlightDao().insertAll(syncResult.highlights)
|
||||
|
||||
val highlightCrossRefs = syncResult.highlights.map {
|
||||
SavedItemAndHighlightCrossRef(highlightId = it.highlightId, savedItemId = savedItem.savedItemId)
|
||||
}
|
||||
|
||||
db.savedItemAndHighlightCrossRefDao().insertAll(highlightCrossRefs)
|
||||
|
||||
Log.d("sync", "saved content for item with id: ${savedItem.savedItemId}")
|
||||
}
|
||||
|
||||
suspend fun DataService.syncOfflineItemsWithServerIfNeeded() {
|
||||
|
|
|
|||
|
|
@ -9,13 +9,17 @@ import app.omnivore.omnivore.persistence.entities.*
|
|||
Viewer::class,
|
||||
SavedItem::class,
|
||||
SavedItemLabel::class,
|
||||
SavedItemAndSavedItemLabelCrossRef::class
|
||||
Highlight::class,
|
||||
SavedItemAndSavedItemLabelCrossRef::class,
|
||||
SavedItemAndHighlightCrossRef::class
|
||||
],
|
||||
version = 2
|
||||
)
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
abstract fun viewerDao(): ViewerDao
|
||||
abstract fun savedItemDao(): SavedItemDao
|
||||
abstract fun highlightDao(): HighlightDao
|
||||
abstract fun savedItemLabelDao(): SavedItemLabelDao
|
||||
abstract fun savedItemAndSavedItemLabelCrossRefDao(): SavedItemAndSavedItemLabelCrossRefDao
|
||||
abstract fun savedItemAndHighlightCrossRefDao(): SavedItemAndHighlightCrossRefDao
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package app.omnivore.omnivore.persistence.entities
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import androidx.room.*
|
||||
import java.time.LocalDate
|
||||
import java.util.Date
|
||||
|
||||
|
|
@ -9,7 +8,7 @@ import java.util.Date
|
|||
data class Highlight(
|
||||
@PrimaryKey val highlightId: String,
|
||||
val annotation: String?,
|
||||
val createdAt: Date?,
|
||||
val createdAt: String?,
|
||||
val createdByMe: Boolean,
|
||||
val markedForDeletion: Boolean, // default false
|
||||
val patch: String,
|
||||
|
|
@ -18,9 +17,45 @@ data class Highlight(
|
|||
val serverSyncStatus: Int, // default 0
|
||||
val shortId: String,
|
||||
val suffix: String?,
|
||||
val updatedAt: LocalDate?
|
||||
val updatedAt: String?
|
||||
|
||||
// has many SavedItemLabels (inverse: labels have many highlights)
|
||||
// has one savedItem (inverse: savedItem has many highlights
|
||||
// has a UserProfile (no inverse)
|
||||
)
|
||||
|
||||
@Entity(primaryKeys = ["highlightId", "savedItemId"])
|
||||
data class SavedItemAndHighlightCrossRef(
|
||||
val highlightId: String,
|
||||
val savedItemId: String
|
||||
)
|
||||
|
||||
@Dao
|
||||
interface SavedItemAndHighlightCrossRefDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insertAll(items: List<SavedItemAndHighlightCrossRef>)
|
||||
}
|
||||
|
||||
data class SavedItemWithLabelsAndHighlights(
|
||||
@Embedded val savedItem: SavedItem,
|
||||
|
||||
@Relation(
|
||||
parentColumn = "savedItemId",
|
||||
entityColumn = "savedItemLabelId",
|
||||
associateBy = Junction(SavedItemAndSavedItemLabelCrossRef::class)
|
||||
)
|
||||
val labels: List<SavedItemLabel>,
|
||||
|
||||
@Relation(
|
||||
parentColumn = "savedItemId",
|
||||
entityColumn = "highlightId",
|
||||
associateBy = Junction(SavedItemAndHighlightCrossRef::class)
|
||||
)
|
||||
val highlights: List<Highlight>
|
||||
)
|
||||
|
||||
@Dao
|
||||
interface HighlightDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insertAll(items: List<Highlight>)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,9 +92,15 @@ interface SavedItemDao {
|
|||
@Query("SELECT * FROM savedItem")
|
||||
fun getAll(): List<SavedItem>
|
||||
|
||||
@Query("SELECT * FROM savedItem WHERE slug = :slug")
|
||||
fun getSavedItemWithLabelsAndHighlights(slug: String): SavedItemWithLabelsAndHighlights?
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insertAll(items: List<SavedItem>)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insert(item: SavedItem)
|
||||
|
||||
@Query("DELETE FROM savedItem WHERE savedItemId = :itemID")
|
||||
fun deleteById(itemID: String)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package app.omnivore.omnivore.persistence.entities
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.room.*
|
||||
|
||||
@Entity
|
||||
|
|
|
|||
|
|
@ -83,6 +83,12 @@ class LibraryViewModel @Inject constructor(
|
|||
private suspend fun performItemSync(cursor: String?, since: String, count: Int, startTime: String) {
|
||||
dataService.syncOfflineItemsWithServerIfNeeded()
|
||||
val result = dataService.sync(since = since, cursor = cursor)
|
||||
|
||||
// TODO: Defer this until later?
|
||||
for (slug in result.savedItemSlugs) {
|
||||
dataService.syncSavedItemContent(slug)
|
||||
}
|
||||
|
||||
val totalCount = count + result.count
|
||||
|
||||
Log.d("sync", "fetched ${result.count} items")
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import androidx.compose.foundation.ScrollState
|
|||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import app.omnivore.omnivore.DataService
|
||||
import app.omnivore.omnivore.DatastoreKeys
|
||||
import app.omnivore.omnivore.DatastoreRepository
|
||||
import app.omnivore.omnivore.persistence.entities.SavedItem
|
||||
|
|
@ -12,10 +13,7 @@ import app.omnivore.omnivore.networking.*
|
|||
import app.omnivore.omnivore.ui.library.SavedItemAction
|
||||
import com.google.gson.Gson
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.*
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -31,6 +29,7 @@ data class AnnotationWebViewMessage(
|
|||
@HiltViewModel
|
||||
class WebReaderViewModel @Inject constructor(
|
||||
private val datastoreRepo: DatastoreRepository,
|
||||
private val dataService: DataService,
|
||||
private val networker: Networker
|
||||
): ViewModel() {
|
||||
var lastJavascriptActionLoopUUID: UUID = UUID.randomUUID()
|
||||
|
|
@ -47,23 +46,47 @@ class WebReaderViewModel @Inject constructor(
|
|||
|
||||
fun loadItem(slug: String) {
|
||||
viewModelScope.launch {
|
||||
val articleQueryResult = networker.savedItem(slug)
|
||||
// Attempt to load from db first
|
||||
withContext(Dispatchers.IO) {
|
||||
val persistedItem = dataService.db.savedItemDao().getSavedItemWithLabelsAndHighlights(slug)
|
||||
|
||||
val article = articleQueryResult.item ?: return@launch
|
||||
if (persistedItem?.savedItem != null) {
|
||||
val articleContent = ArticleContent(
|
||||
title = persistedItem.savedItem.title,
|
||||
htmlContent = persistedItem.savedItem.content ?: "",
|
||||
highlights = persistedItem.highlights,
|
||||
contentStatus = "SUCCEEDED",
|
||||
objectID = "",
|
||||
labelsJSONString = Gson().toJson(persistedItem.labels)
|
||||
)
|
||||
|
||||
val articleContent = ArticleContent(
|
||||
title = article.title,
|
||||
htmlContent = article.content ?: "",
|
||||
highlights = articleQueryResult.highlights,
|
||||
contentStatus = "SUCCEEDED",
|
||||
objectID = "",
|
||||
labelsJSONString = Gson().toJson(articleQueryResult.labels)
|
||||
)
|
||||
|
||||
webReaderParamsLiveData.value = WebReaderParams(article, articleContent)
|
||||
Log.d("sync", "data loaded from db")
|
||||
webReaderParamsLiveData.postValue(WebReaderParams(persistedItem.savedItem, articleContent))
|
||||
} else {
|
||||
loadItemFromServer(slug)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun loadItemFromServer(slug: String) {
|
||||
val articleQueryResult = networker.savedItem(slug)
|
||||
|
||||
val article = articleQueryResult.item ?: return
|
||||
|
||||
val articleContent = ArticleContent(
|
||||
title = article.title,
|
||||
htmlContent = article.content ?: "",
|
||||
highlights = articleQueryResult.highlights,
|
||||
contentStatus = "SUCCEEDED",
|
||||
objectID = "",
|
||||
labelsJSONString = Gson().toJson(articleQueryResult.labels)
|
||||
)
|
||||
|
||||
Log.d("sync", "data loaded from server")
|
||||
webReaderParamsLiveData.postValue(WebReaderParams(article, articleContent))
|
||||
}
|
||||
|
||||
fun handleSavedItemAction(itemID: String, action: SavedItemAction) {
|
||||
when (action) {
|
||||
SavedItemAction.Delete -> {
|
||||
|
|
|
|||
Loading…
Reference in a new issue