add dao and repository modules

This commit is contained in:
Stefano Sansone
2024-02-19 23:36:07 +00:00
parent 4a285bd44f
commit 9d800366c2
20 changed files with 486 additions and 435 deletions

View File

@ -1,38 +1,31 @@
package app.omnivore.omnivore.core.data
import android.content.Context
import androidx.room.Room
import app.omnivore.omnivore.core.network.Networker
import app.omnivore.omnivore.core.database.AppDatabase
import app.omnivore.omnivore.core.database.entities.Highlight
import app.omnivore.omnivore.core.database.entities.SavedItem
import kotlinx.coroutines.*
import app.omnivore.omnivore.core.network.Networker
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
import javax.inject.Inject
class DataService @Inject constructor(
context: Context,
val networker: Networker
val networker: Networker,
appDatabase: AppDatabase
) {
val savedItemSyncChannel = Channel<SavedItem>(capacity = Channel.UNLIMITED)
val highlightSyncChannel = Channel<Highlight>(capacity = Channel.UNLIMITED)
val savedItemSyncChannel = Channel<SavedItem>(capacity = Channel.UNLIMITED)
val db = Room.databaseBuilder(
context,
AppDatabase::class.java, "omnivore-database"
)
.fallbackToDestructiveMigration()
.build()
val db = appDatabase
init {
CoroutineScope(Dispatchers.IO).launch {
startSyncChannels()
init {
CoroutineScope(Dispatchers.IO).launch {
startSyncChannels()
}
}
}
fun clearDatabase() {
CoroutineScope(Dispatchers.IO).launch {
db.clearAllTables()
fun clearDatabase() {
CoroutineScope(Dispatchers.IO).launch {
db.clearAllTables()
}
}
}
}

View File

@ -1,28 +1,34 @@
package app.omnivore.omnivore.core.data
import app.omnivore.omnivore.core.data.model.ServerSyncStatus
import app.omnivore.omnivore.core.database.dao.SavedItemDao
import app.omnivore.omnivore.core.network.ReadingProgressParams
import app.omnivore.omnivore.core.network.updateReadingProgress
import com.google.gson.Gson
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
suspend fun DataService.updateWebReadingProgress(jsonString: String) {
suspend fun DataService.updateWebReadingProgress(
jsonString: String,
savedItemDao: SavedItemDao
) {
val readingProgressParams = Gson().fromJson(jsonString, ReadingProgressParams::class.java)
val savedItemId = readingProgressParams.id ?: return
withContext(Dispatchers.IO) {
val savedItem = db.savedItemDao().findById(savedItemId) ?: return@withContext
savedItem.readingProgress = readingProgressParams.readingProgressPercent ?: 0.0
savedItem.readingProgressAnchor = readingProgressParams.readingProgressAnchorIndex ?: 0
savedItem.serverSyncStatus = ServerSyncStatus.NEEDS_UPDATE.rawValue
db.savedItemDao().update(savedItem)
val savedItem = savedItemDao.findById(savedItemId) ?: return@withContext
val updatedItem = savedItem.copy(
readingProgress = readingProgressParams.readingProgressPercent ?: 0.0,
readingProgressAnchor = readingProgressParams.readingProgressAnchorIndex ?: 0,
serverSyncStatus = ServerSyncStatus.NEEDS_UPDATE.rawValue
)
savedItemDao.update(updatedItem)
val isUpdatedOnServer = networker.updateReadingProgress(readingProgressParams)
if (isUpdatedOnServer) {
savedItem.serverSyncStatus = ServerSyncStatus.IS_SYNCED.rawValue
db.savedItemDao().update(savedItem)
updatedItem.serverSyncStatus = ServerSyncStatus.IS_SYNCED.rawValue
savedItemDao.update(updatedItem)
}
}
}

View File

@ -1,6 +1,10 @@
package app.omnivore.omnivore.core.data
import android.util.Log
import app.omnivore.omnivore.core.data.model.ServerSyncStatus
import app.omnivore.omnivore.core.database.entities.HighlightChange
import app.omnivore.omnivore.core.database.entities.SavedItem
import app.omnivore.omnivore.core.database.entities.highlightChangeToHighlight
import app.omnivore.omnivore.core.network.ReadingProgressParams
import app.omnivore.omnivore.core.network.createHighlight
import app.omnivore.omnivore.core.network.deleteHighlights
@ -13,10 +17,6 @@ import app.omnivore.omnivore.graphql.generated.type.CreateHighlightInput
import app.omnivore.omnivore.graphql.generated.type.HighlightType
import app.omnivore.omnivore.graphql.generated.type.MergeHighlightInput
import app.omnivore.omnivore.graphql.generated.type.UpdateHighlightInput
import app.omnivore.omnivore.core.data.model.ServerSyncStatus
import app.omnivore.omnivore.core.database.entities.HighlightChange
import app.omnivore.omnivore.core.database.entities.SavedItem
import app.omnivore.omnivore.core.database.entities.highlightChangeToHighlight
import com.apollographql.apollo3.api.Optional
import kotlinx.coroutines.delay
@ -50,7 +50,7 @@ suspend fun DataService.syncOfflineItemsWithServerIfNeeded() {
}
private suspend fun DataService.syncSavedItem(item: SavedItem) {
fun updateSyncStatus(status: ServerSyncStatus) {
suspend fun updateSyncStatus(status: ServerSyncStatus) {
item.serverSyncStatus = status.rawValue
db.savedItemDao().update(item)
}

View File

@ -1,21 +1,16 @@
package app.omnivore.omnivore.core.data.repository
import app.omnivore.omnivore.core.data.DataService
import app.omnivore.omnivore.core.data.model.LibraryQuery
import app.omnivore.omnivore.core.database.entities.SavedItemWithLabelsAndHighlights
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
class LibraryRepository @Inject constructor(
private val dataService: DataService,
) {
fun getSavedItems(query: LibraryQuery): Flow<List<SavedItemWithLabelsAndHighlights>> =
dataService.db.savedItemDao().filteredLibraryData(
query.allowedArchiveStates,
query.sortKey,
query.requiredLabels,
query.excludedLabels,
query.allowedContentReaders
)
interface LibraryRepository {
fun getSavedItems(query: LibraryQuery): Flow<List<SavedItemWithLabelsAndHighlights>>
suspend fun updateReadingProgress(
itemId: String,
readingProgressPercentage: Double,
readingProgressAnchorIndex: Int
)
}

View File

@ -0,0 +1,66 @@
package app.omnivore.omnivore.core.data.repository.impl
import app.omnivore.omnivore.core.data.model.LibraryQuery
import app.omnivore.omnivore.core.data.model.ServerSyncStatus
import app.omnivore.omnivore.core.data.repository.LibraryRepository
import app.omnivore.omnivore.core.database.dao.SavedItemDao
import app.omnivore.omnivore.core.database.entities.SavedItemWithLabelsAndHighlights
import app.omnivore.omnivore.core.network.Networker
import app.omnivore.omnivore.core.network.ReadingProgressParams
import app.omnivore.omnivore.core.network.updateReadingProgress
import com.google.gson.Gson
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
class LibraryRepositoryImpl @Inject constructor(
private val savedItemDao: SavedItemDao,
private val networker: Networker
): LibraryRepository {
override fun getSavedItems(query: LibraryQuery): Flow<List<SavedItemWithLabelsAndHighlights>> =
savedItemDao.filteredLibraryData(
query.allowedArchiveStates,
query.sortKey,
hasRequiredLabels = query.requiredLabels.size,
hasExcludedLabels = query.excludedLabels.size,
query.requiredLabels,
query.excludedLabels,
query.allowedContentReaders
)
override suspend fun updateReadingProgress(
itemId: String,
readingProgressPercentage: Double,
readingProgressAnchorIndex: Int
) {
val jsonString = Gson().toJson(
mapOf(
"id" to itemId,
"readingProgressPercent" to readingProgressPercentage,
"readingProgressAnchorIndex" to readingProgressAnchorIndex,
"force" to true
)
)
val readingProgressParams = Gson().fromJson(jsonString, ReadingProgressParams::class.java)
val savedItemId = readingProgressParams.id ?: return
val savedItem = savedItemDao.findById(savedItemId)
val updatedItem = savedItem?.copy(
readingProgress = readingProgressParams.readingProgressPercent ?: 0.0,
readingProgressAnchor = readingProgressParams.readingProgressAnchorIndex ?: 0,
serverSyncStatus = ServerSyncStatus.NEEDS_UPDATE.rawValue
)
updatedItem?.let { savedItemDao.update(updatedItem) }
val isUpdatedOnServer = networker.updateReadingProgress(readingProgressParams)
if (isUpdatedOnServer) {
updatedItem?.serverSyncStatus = ServerSyncStatus.IS_SYNCED.rawValue
updatedItem?.let { savedItemDao.update(updatedItem) }
}
}
}

View File

@ -2,6 +2,7 @@ package app.omnivore.omnivore.core.database
import androidx.room.Database
import androidx.room.RoomDatabase
import app.omnivore.omnivore.core.database.dao.SavedItemDao
import app.omnivore.omnivore.core.database.entities.Highlight
import app.omnivore.omnivore.core.database.entities.HighlightChange
import app.omnivore.omnivore.core.database.entities.HighlightChangesDao
@ -11,7 +12,6 @@ import app.omnivore.omnivore.core.database.entities.SavedItemAndHighlightCrossRe
import app.omnivore.omnivore.core.database.entities.SavedItemAndHighlightCrossRefDao
import app.omnivore.omnivore.core.database.entities.SavedItemAndSavedItemLabelCrossRef
import app.omnivore.omnivore.core.database.entities.SavedItemAndSavedItemLabelCrossRefDao
import app.omnivore.omnivore.core.database.entities.SavedItemDao
import app.omnivore.omnivore.core.database.entities.SavedItemLabel
import app.omnivore.omnivore.core.database.entities.SavedItemLabelDao
import app.omnivore.omnivore.core.database.entities.SavedItemWithLabelsAndHighlightsDao
@ -28,7 +28,8 @@ import app.omnivore.omnivore.core.database.entities.ViewerDao
SavedItemAndSavedItemLabelCrossRef::class,
SavedItemAndHighlightCrossRef::class
],
version = 24
version = 24,
exportSchema = false
)
abstract class AppDatabase : RoomDatabase() {
abstract fun viewerDao(): ViewerDao

View File

@ -1,42 +0,0 @@
package app.omnivore.omnivore.core.database
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Update
interface BaseDao<T> {
/**
* Insert an object in the database.
*
* @param obj the object to be inserted.
*/
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(obj: T)
/**
* Insert an array of objects in the database.
*
* @param obj the objects to be inserted.
*/
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(vararg obj: T)
/**
* Update an object from the database.
*
* @param obj the object to be updated
*/
@Update
fun update(obj: T)
/**
* Delete an object from the database
*
* @param obj the object to be deleted
*/
@Delete
fun delete(obj: T)
}

View File

@ -0,0 +1,104 @@
package app.omnivore.omnivore.core.database.dao
import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Query
import androidx.room.Transaction
import androidx.room.Update
import app.omnivore.omnivore.core.database.entities.SavedItem
import app.omnivore.omnivore.core.database.entities.SavedItemQueryConstants
import app.omnivore.omnivore.core.database.entities.SavedItemWithLabelsAndHighlights
import kotlinx.coroutines.flow.Flow
@Dao
interface SavedItemDao {
@Transaction
@Query("SELECT * FROM savedItem")
fun getAll(): Flow<List<SavedItem>>
@Query("SELECT * FROM savedItem WHERE savedItemId = :itemID")
suspend fun findById(itemID: String): SavedItem?
@Query("SELECT * FROM savedItem WHERE serverSyncStatus != 0")
fun getUnSynced(): List<SavedItem>
@Query("SELECT * FROM savedItem WHERE slug = :slug")
fun getSavedItemWithLabelsAndHighlights(slug: String): SavedItemWithLabelsAndHighlights?
@Query("DELETE FROM savedItem WHERE savedItemId = :itemID")
fun deleteById(itemID: String)
@Query("DELETE FROM savedItem WHERE savedItemId in (:itemIDs)")
fun deleteByIds(itemIDs: List<String>)
@Update
suspend fun update(savedItem: SavedItem)
@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.savedItemId = :savedItemId " +
"GROUP BY SavedItem.savedItemId "
)
fun getLibraryItemById(savedItemId: String): LiveData<SavedItemWithLabelsAndHighlights>
@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.savedItemId = :savedItemId " +
"GROUP BY SavedItem.savedItemId "
)
suspend fun getById(savedItemId: String): SavedItemWithLabelsAndHighlights?
@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 IN (:allowedArchiveStates) " +
"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(
allowedArchiveStates: List<Int>,
sortKey: String,
hasRequiredLabels: Int,
hasExcludedLabels: Int,
requiredLabels: List<String>,
excludedLabels: List<String>,
allowedContentReaders: List<String>
): Flow<List<SavedItemWithLabelsAndHighlights>>
}

View File

@ -1,9 +1,13 @@
package app.omnivore.omnivore.core.database.entities
import androidx.core.net.toUri
import androidx.lifecycle.LiveData
import androidx.room.*
import kotlinx.coroutines.flow.Flow
import androidx.room.ColumnInfo
import androidx.room.Dao
import androidx.room.Entity
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.PrimaryKey
import androidx.room.Transaction
@Entity
data class SavedItem(
@ -128,114 +132,7 @@ abstract class SavedItemWithLabelsAndHighlightsDao {
}
}
@Dao
interface SavedItemDao {
@Query("SELECT * FROM savedItem")
fun getAll(): List<SavedItem>
@Query("SELECT * FROM savedItem WHERE savedItemId = :itemID")
fun findById(itemID: String): SavedItem?
@Query("SELECT * FROM savedItem WHERE serverSyncStatus != 0")
fun getUnSynced(): List<SavedItem>
@Query("SELECT * FROM savedItem WHERE slug = :slug")
fun getSavedItemWithLabelsAndHighlights(slug: String): SavedItemWithLabelsAndHighlights?
@Query("DELETE FROM savedItem WHERE savedItemId = :itemID")
fun deleteById(itemID: String)
@Query("DELETE FROM savedItem WHERE savedItemId in (:itemIDs)")
fun deleteByIds(itemIDs: List<String>)
@Update
fun update(savedItem: SavedItem)
@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.savedItemId = :savedItemId " +
"GROUP BY SavedItem.savedItemId "
)
fun getLibraryItemById(savedItemId: String): LiveData<SavedItemWithLabelsAndHighlights>
@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.savedItemId = :savedItemId " +
"GROUP BY SavedItem.savedItemId "
)
suspend fun getById(savedItemId: String): SavedItemWithLabelsAndHighlights?
@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 IN (:allowedArchiveStates) " +
"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(
allowedArchiveStates: List<Int>,
sortKey: String,
hasRequiredLabels: Int,
hasExcludedLabels: Int,
requiredLabels: List<String>,
excludedLabels: List<String>,
allowedContentReaders: List<String>
): Flow<List<SavedItemWithLabelsAndHighlights>>
fun filteredLibraryData(
allowedArchiveStates: List<Int>,
sortKey: String,
requiredLabels: List<String>,
excludedLabels: List<String>,
allowedContentReaders: List<String>
): Flow<List<SavedItemWithLabelsAndHighlights>> {
return _filteredLibraryData(
allowedArchiveStates = allowedArchiveStates,
sortKey = sortKey,
hasRequiredLabels = requiredLabels.size,
hasExcludedLabels = excludedLabels.size,
requiredLabels = requiredLabels,
excludedLabels = excludedLabels,
allowedContentReaders = allowedContentReaders
)
}
}
object SavedItemQueryConstants {

View File

@ -1,66 +1,70 @@
package app.omnivore.omnivore.core.database.entities
import androidx.lifecycle.LiveData
import androidx.room.*
import androidx.room.Dao
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.PrimaryKey
import androidx.room.Query
import androidx.room.Transaction
import app.omnivore.omnivore.core.data.model.ServerSyncStatus
@Entity
data class SavedItemLabel(
@PrimaryKey val savedItemLabelId: String,
val name: String,
val color: String,
val createdAt: String?,
val labelDescription: String?,
val serverSyncStatus: Int = 0
@PrimaryKey val savedItemLabelId: String,
val name: String,
val color: String,
val createdAt: String?,
val labelDescription: String?,
val serverSyncStatus: Int = 0
)
@Dao
interface SavedItemLabelDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(items: List<SavedItemLabel>)
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(items: List<SavedItemLabel>)
@Transaction
@Query("SELECT * FROM SavedItemLabel WHERE serverSyncStatus != 2 ORDER BY name ASC")
fun getSavedItemLabelsLiveData(): LiveData<List<SavedItemLabel>>
@Transaction
@Query("SELECT * FROM SavedItemLabel WHERE serverSyncStatus != 2 ORDER BY name ASC")
fun getSavedItemLabelsLiveData(): LiveData<List<SavedItemLabel>>
@Transaction
@Query("UPDATE SavedItemLabel set savedItemLabelId = :permanentId, serverSyncStatus = :status WHERE savedItemLabelId = :tempId")
fun updateTempLabel(tempId: String, permanentId: String, status: ServerSyncStatus = ServerSyncStatus.IS_SYNCED)
@Transaction
@Query("UPDATE SavedItemLabel set savedItemLabelId = :permanentId, serverSyncStatus = :status WHERE savedItemLabelId = :tempId")
fun updateTempLabel(
tempId: String, permanentId: String, status: ServerSyncStatus = ServerSyncStatus.IS_SYNCED
)
@Transaction
@Query("SELECT * FROM SavedItemLabel WHERE name in (:names) ORDER BY name ASC")
fun namedLabels(names: List<String>): List<SavedItemLabel>
@Transaction
@Query("SELECT * FROM SavedItemLabel WHERE name in (:names) ORDER BY name ASC")
fun namedLabels(names: List<String>): List<SavedItemLabel>
}
@Entity(
primaryKeys = ["savedItemLabelId", "savedItemId"],
foreignKeys = [
ForeignKey(
entity = SavedItem::class,
parentColumns = arrayOf("savedItemId"),
childColumns = arrayOf("savedItemId"),
onDelete = ForeignKey.CASCADE
),
ForeignKey(
entity = SavedItemLabel::class,
parentColumns = arrayOf("savedItemLabelId"),
childColumns = arrayOf("savedItemLabelId")
)
]
primaryKeys = ["savedItemLabelId", "savedItemId"], foreignKeys = [ForeignKey(
entity = SavedItem::class,
parentColumns = arrayOf("savedItemId"),
childColumns = arrayOf("savedItemId"),
onDelete = ForeignKey.CASCADE
), ForeignKey(
entity = SavedItemLabel::class,
parentColumns = arrayOf("savedItemLabelId"),
childColumns = arrayOf("savedItemLabelId")
)]
)
data class SavedItemAndSavedItemLabelCrossRef(
val savedItemLabelId: String,
val savedItemId: String
val savedItemLabelId: String, val savedItemId: String
)
@Dao
interface SavedItemAndSavedItemLabelCrossRefDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(items: List<SavedItemAndSavedItemLabelCrossRef>)
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(items: List<SavedItemAndSavedItemLabelCrossRef>)
@Query("DELETE FROM savedItemAndSavedItemLabelCrossRef WHERE savedItemId = :savedItemId")
fun deleteRefsBySavedItemId(savedItemId: String)
@Query("DELETE FROM savedItemAndSavedItemLabelCrossRef WHERE savedItemId = :savedItemId")
fun deleteRefsBySavedItemId(savedItemId: String)
}
// has many highlights

View File

@ -1,25 +1,20 @@
package app.omnivore.omnivore.core.network
import app.omnivore.omnivore.core.datastore.DatastoreRepository
import app.omnivore.omnivore.utils.Constants
import app.omnivore.omnivore.utils.DatastoreKeys
import app.omnivore.omnivore.core.datastore.DatastoreRepository
import com.apollographql.apollo3.ApolloClient
import javax.inject.Inject
class Networker @Inject constructor(
private val datastoreRepo: DatastoreRepository
private val datastoreRepo: DatastoreRepository
) {
suspend fun baseUrl() = datastoreRepo.getString(DatastoreKeys.omnivoreSelfHostedAPIServer) ?: Constants.apiURL
suspend fun baseUrl() =
datastoreRepo.getString(DatastoreKeys.omnivoreSelfHostedAPIServer) ?: Constants.apiURL
suspend fun serverUrl() = "${baseUrl()}/api/graphql"
private suspend fun authToken() = datastoreRepo.getString(DatastoreKeys.omnivoreAuthToken) ?: ""
private suspend fun serverUrl() = "${baseUrl()}/api/graphql"
private suspend fun authToken() = datastoreRepo.getString(DatastoreKeys.omnivoreAuthToken) ?: ""
suspend fun publicApolloClient() = ApolloClient.Builder()
.serverUrl(serverUrl())
.build()
suspend fun authenticatedApolloClient() = ApolloClient.Builder()
.serverUrl(serverUrl())
.addHttpHeader("Authorization", value = authToken())
.build()
suspend fun authenticatedApolloClient() = ApolloClient.Builder().serverUrl(serverUrl())
.addHttpHeader("Authorization", value = authToken()).build()
}

View File

@ -3,6 +3,7 @@ package app.omnivore.omnivore.di
import android.content.Context
import app.omnivore.omnivore.core.analytics.EventTracker
import app.omnivore.omnivore.core.data.DataService
import app.omnivore.omnivore.core.database.AppDatabase
import app.omnivore.omnivore.core.datastore.DatastoreRepository
import app.omnivore.omnivore.core.datastore.OmnivoreDatastore
import app.omnivore.omnivore.core.network.Networker
@ -34,8 +35,8 @@ object AppModule {
@Singleton
@Provides
fun provideDataService(
@ApplicationContext app: Context,
networker: Networker
) = DataService(app, networker)
networker: Networker,
appDatabase: AppDatabase
) = DataService(networker, appDatabase)
}

View File

@ -0,0 +1,17 @@
package app.omnivore.omnivore.di
import app.omnivore.omnivore.core.database.AppDatabase
import app.omnivore.omnivore.core.database.dao.SavedItemDao
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
@Module
@InstallIn(SingletonComponent::class)
object DaosModule {
@Provides
fun providesTopicsDao(
database: AppDatabase,
): SavedItemDao = database.savedItemDao()
}

View File

@ -0,0 +1,18 @@
package app.omnivore.omnivore.di
import app.omnivore.omnivore.core.data.repository.LibraryRepository
import app.omnivore.omnivore.core.data.repository.impl.LibraryRepositoryImpl
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
@Module
@InstallIn(SingletonComponent::class)
interface DataModule {
@Binds
fun bindsLibraryRepository(
libraryRepository: LibraryRepositoryImpl,
): LibraryRepository
}

View File

@ -0,0 +1,25 @@
package app.omnivore.omnivore.di
import android.content.Context
import androidx.room.Room
import app.omnivore.omnivore.core.database.AppDatabase
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
object DatabaseModule {
@Provides
@Singleton
fun providesOmnivoreDatabase(
@ApplicationContext context: Context,
): AppDatabase = Room.databaseBuilder(
context,
AppDatabase::class.java,
"omnivore-database",
).build()
}

View File

@ -1,15 +1,25 @@
package app.omnivore.omnivore.feature.library
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.material3.AssistChip
import androidx.compose.material3.Icon
import androidx.compose.material3.SuggestionChipDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
@ -23,98 +33,92 @@ import app.omnivore.omnivore.feature.components.LabelChipColors
@Composable
fun LibraryFilterBar(
viewModel: LibraryViewModel = hiltViewModel()
viewModel: LibraryViewModel = hiltViewModel()
) {
var isSavedItemFilterMenuExpanded by remember { mutableStateOf(false) }
val activeSavedItemFilter: SavedItemFilter by viewModel.appliedFilterLiveData.observeAsState(SavedItemFilter.INBOX)
val activeLabels: List<SavedItemLabel> by viewModel.activeLabelsLiveData.observeAsState(listOf())
var isSavedItemFilterMenuExpanded by remember { mutableStateOf(false) }
val activeSavedItemFilter: SavedItemFilter by viewModel.appliedFilterLiveData.observeAsState(
SavedItemFilter.INBOX
)
val activeLabels: List<SavedItemLabel> by viewModel.activeLabelsLiveData.observeAsState(listOf())
var isSavedItemSortFilterMenuExpanded by remember { mutableStateOf(false) }
val activeSavedItemSortFilter: SavedItemSortFilter by viewModel.appliedSortFilterLiveData.observeAsState(SavedItemSortFilter.NEWEST)
val listState = rememberLazyListState()
var isSavedItemSortFilterMenuExpanded by remember { mutableStateOf(false) }
val activeSavedItemSortFilter: SavedItemSortFilter by viewModel.appliedSortFilterLiveData.observeAsState(
SavedItemSortFilter.NEWEST
)
val listState = rememberLazyListState()
Column {
LazyRow(
state = listState,
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.padding(start = 6.dp)
.fillMaxWidth()
) {
item {
AssistChip(
onClick = { isSavedItemFilterMenuExpanded = true },
label = { Text(activeSavedItemFilter.displayText) },
trailingIcon = {
Icon(
Icons.Default.ArrowDropDown,
contentDescription = "drop down button to change primary library filter"
)
},
modifier = Modifier.padding(end = 6.dp)
)
AssistChip(
onClick = { isSavedItemSortFilterMenuExpanded = true },
label = { Text(activeSavedItemSortFilter.displayText) },
trailingIcon = {
Icon(
Icons.Default.ArrowDropDown,
contentDescription = "drop down button to change library sort order"
)
},
modifier = Modifier.padding(end = 6.dp)
)
AssistChip(
onClick = { viewModel.bottomSheetState.value = LibraryBottomSheetState.LABEL },
label = { Text(stringResource(R.string.library_filter_bar_label_labels)) },
trailingIcon = {
Icon(
Icons.Default.ArrowDropDown,
contentDescription = "drop down button to open label selection sheet"
)
},
modifier = Modifier.padding(end = 6.dp)
)
}
items(activeLabels.sortedWith(compareBy { it.name.toLowerCase(Locale.current) })) { label ->
val chipColors = LabelChipColors.fromHex(label.color)
Column {
LazyRow(
state = listState,
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.padding(start = 6.dp)
.fillMaxWidth()
) {
item {
AssistChip(onClick = { isSavedItemFilterMenuExpanded = true },
label = { Text(activeSavedItemFilter.displayText) },
trailingIcon = {
Icon(
Icons.Default.ArrowDropDown,
contentDescription = "drop down button to change primary library filter"
)
},
modifier = Modifier.padding(end = 6.dp)
)
AssistChip(onClick = { isSavedItemSortFilterMenuExpanded = true },
label = { Text(activeSavedItemSortFilter.displayText) },
trailingIcon = {
Icon(
Icons.Default.ArrowDropDown,
contentDescription = "drop down button to change library sort order"
)
},
modifier = Modifier.padding(end = 6.dp)
)
AssistChip(
onClick = { viewModel.bottomSheetState.value = LibraryBottomSheetState.LABEL },
label = { Text(stringResource(R.string.library_filter_bar_label_labels)) },
trailingIcon = {
Icon(
Icons.Default.ArrowDropDown,
contentDescription = "drop down button to open label selection sheet"
)
},
modifier = Modifier.padding(end = 6.dp)
)
}
items(activeLabels.sortedWith(compareBy { it.name.toLowerCase(Locale.current) })) { label ->
val chipColors = LabelChipColors.fromHex(label.color)
AssistChip(
onClick = {
viewModel.updateAppliedLabels(
(viewModel.activeLabelsLiveData.value ?: listOf()).filter { it.savedItemLabelId != label.savedItemLabelId }
)
},
label = { Text(label.name) },
border = null,
colors = SuggestionChipDefaults.elevatedSuggestionChipColors(
containerColor = chipColors.containerColor,
labelColor = chipColors.textColor,
iconContentColor = chipColors.textColor
),
trailingIcon = {
Icon(
Icons.Default.Close,
contentDescription = "close icon to remove label"
)
},
modifier = Modifier
.padding(horizontal = 4.dp)
)
}
AssistChip(onClick = {
viewModel.updateAppliedLabels((viewModel.activeLabelsLiveData.value
?: listOf()).filter { it.savedItemLabelId != label.savedItemLabelId })
},
label = { Text(label.name) },
border = null,
colors = SuggestionChipDefaults.elevatedSuggestionChipColors(
containerColor = chipColors.containerColor,
labelColor = chipColors.textColor,
iconContentColor = chipColors.textColor
),
trailingIcon = {
Icon(
Icons.Default.Close, contentDescription = "close icon to remove label"
)
},
modifier = Modifier.padding(horizontal = 4.dp)
)
}
}
SavedItemFilterContextMenu(isExpanded = isSavedItemFilterMenuExpanded,
onDismiss = { isSavedItemFilterMenuExpanded = false },
actionHandler = { viewModel.updateSavedItemFilter(it) })
SavedItemSortFilterContextMenu(isExpanded = isSavedItemSortFilterMenuExpanded,
onDismiss = { isSavedItemSortFilterMenuExpanded = false },
actionHandler = { viewModel.updateSavedItemSortFilter(it) })
}
SavedItemFilterContextMenu(
isExpanded = isSavedItemFilterMenuExpanded,
onDismiss = { isSavedItemFilterMenuExpanded = false },
actionHandler = { viewModel.updateSavedItemFilter(it) }
)
SavedItemSortFilterContextMenu(
isExpanded = isSavedItemSortFilterMenuExpanded,
onDismiss = { isSavedItemSortFilterMenuExpanded = false },
actionHandler = { viewModel.updateSavedItemSortFilter(it) }
)
}
}

View File

@ -32,6 +32,7 @@ import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material.rememberDismissState
import androidx.compose.material.rememberScaffoldState
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
@ -44,12 +45,12 @@ import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
@ -72,52 +73,52 @@ import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.launch
@Composable
fun LibraryView(
libraryViewModel: LibraryViewModel = hiltViewModel(),
internal fun LibraryView(
labelsViewModel: LabelsViewModel,
saveViewModel: SaveViewModel,
editInfoViewModel: EditInfoViewModel,
navController: NavHostController
navController: NavHostController,
viewModel: LibraryViewModel = hiltViewModel()
) {
val scaffoldState: ScaffoldState = rememberScaffoldState()
val coroutineScope = rememberCoroutineScope()
val uiState by libraryViewModel.uiState.collectAsStateWithLifecycle()
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
val showBottomSheet: LibraryBottomSheetState by libraryViewModel.bottomSheetState.observeAsState(
val showBottomSheet: LibraryBottomSheetState by viewModel.bottomSheetState.observeAsState(
LibraryBottomSheetState.HIDDEN
)
libraryViewModel.snackbarMessage?.let {
viewModel.snackbarMessage?.let {
coroutineScope.launch {
scaffoldState.snackbarHostState.showSnackbar(it)
libraryViewModel.clearSnackbarMessage()
viewModel.clearSnackbarMessage()
}
}
when (showBottomSheet) {
LibraryBottomSheetState.ADD_LINK -> {
AddLinkBottomSheet(saveViewModel) {
libraryViewModel.bottomSheetState.value = LibraryBottomSheetState.HIDDEN
viewModel.bottomSheetState.value = LibraryBottomSheetState.HIDDEN
}
}
LibraryBottomSheetState.LABEL -> {
LabelBottomSheet(
libraryViewModel,
viewModel,
labelsViewModel
) {
libraryViewModel.bottomSheetState.value = LibraryBottomSheetState.HIDDEN
viewModel.bottomSheetState.value = LibraryBottomSheetState.HIDDEN
}
}
LibraryBottomSheetState.EDIT -> {
EditBottomSheet(
editInfoViewModel,
libraryViewModel
viewModel
) {
libraryViewModel.bottomSheetState.value = LibraryBottomSheetState.HIDDEN
viewModel.bottomSheetState.value = LibraryBottomSheetState.HIDDEN
}
}
@ -129,9 +130,9 @@ fun LibraryView(
scaffoldState = scaffoldState,
topBar = {
LibraryNavigationBar(
savedItemViewModel = libraryViewModel,
savedItemViewModel = viewModel,
onSearchClicked = { navController.navigate(Routes.Search.route) },
onAddLinkClicked = { showAddLinkBottomSheet(libraryViewModel) },
onAddLinkClicked = { showAddLinkBottomSheet(viewModel) },
onSettingsIconClick = { navController.navigate(Routes.Settings.route) }
)
},
@ -139,13 +140,25 @@ fun LibraryView(
when (uiState) {
is LibraryUiState.Success -> {
LibraryViewContent(
libraryViewModel,
viewModel,
modifier = Modifier
.padding(top = paddingValues.calculateTopPadding()),
cardsData = (uiState as LibraryUiState.Success).items
uiState = uiState
)
}
else -> {}
is LibraryUiState.Loading -> {
Box(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colorScheme.background),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator(strokeCap = StrokeCap.Round)
}
}
else -> {
// TODO
}
}
}
}
@ -285,7 +298,7 @@ fun EditBottomSheet(
fun LibraryViewContent(
libraryViewModel: LibraryViewModel,
modifier: Modifier,
cardsData: List<SavedItemWithLabelsAndHighlights>
uiState: LibraryUiState
) {
val context = LocalContext.current
val listState = rememberLazyListState()
@ -296,9 +309,6 @@ fun LibraryViewContent(
)
val selectedItem: SavedItemWithLabelsAndHighlights? by libraryViewModel.actionsMenuItemLiveData.observeAsState()
/* val cardsData: List<SavedItemWithLabelsAndHighlights> by libraryViewModel.itemsLiveData.observeAsState(
listOf()
)*/
Box(
modifier = Modifier
@ -318,13 +328,14 @@ fun LibraryViewContent(
LibraryFilterBar(libraryViewModel)
}
items(
items = cardsData,
items = (uiState as LibraryUiState.Success).items,
key = { item -> item.savedItem.savedItemId }
) { cardDataWithLabels ->
val swipeThreshold = 0.45f
val currentThresholdFraction = remember { mutableStateOf(0f) }
val currentItem by rememberUpdatedState(cardDataWithLabels.savedItem)
//val currentItem by rememberUpdatedState(cardDataWithLabels.savedItem)
val currentItem = cardDataWithLabels.savedItem
val swipeState = rememberDismissState(
confirmStateChange = {
when(it) {
@ -430,7 +441,7 @@ fun LibraryViewContent(
}
InfiniteListHandler(listState = listState) {
if (cardsData.isEmpty()) {
if ((uiState as LibraryUiState.Success).items.isEmpty()) {
Log.d("sync", "loading with load func")
libraryViewModel.initialLoad()
} else {

View File

@ -20,7 +20,6 @@ import app.omnivore.omnivore.core.data.sync
import app.omnivore.omnivore.core.data.syncLabels
import app.omnivore.omnivore.core.data.syncOfflineItemsWithServerIfNeeded
import app.omnivore.omnivore.core.data.unarchiveSavedItem
import app.omnivore.omnivore.core.data.updateWebReadingProgress
import app.omnivore.omnivore.core.database.entities.SavedItemLabel
import app.omnivore.omnivore.core.database.entities.SavedItemWithLabelsAndHighlights
import app.omnivore.omnivore.core.datastore.DatastoreRepository
@ -31,7 +30,6 @@ import app.omnivore.omnivore.feature.setSavedItemLabels
import app.omnivore.omnivore.graphql.generated.type.CreateLabelInput
import app.omnivore.omnivore.utils.DatastoreKeys
import com.apollographql.apollo3.api.Optional
import com.google.gson.Gson
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@ -79,12 +77,15 @@ class LibraryViewModel @Inject constructor(
val uiState: StateFlow<LibraryUiState> = _libraryQuery.flatMapLatest { query ->
libraryRepository.getSavedItems(query)
}.map(LibraryUiState::Success).stateIn(
}
.map(LibraryUiState::Success)
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000), // Adjust as needed
started = SharingStarted.Eagerly,
initialValue = LibraryUiState.Loading
)
private val itemsLiveData = MediatorLiveData<List<SavedItemWithLabelsAndHighlights>>()
val appliedFilterLiveData = MutableLiveData(SavedItemFilter.INBOX)
val appliedSortFilterLiveData = MutableLiveData(SavedItemSortFilter.NEWEST)
@ -99,6 +100,7 @@ class LibraryViewModel @Inject constructor(
private var hasLoadedInitialFilters = false
private fun loadInitialFilterValues() {
if (hasLoadedInitialFilters) {
return
}
@ -336,31 +338,13 @@ class LibraryViewModel @Inject constructor(
SavedItemAction.MarkRead -> {
viewModelScope.launch {
dataService.updateWebReadingProgress(
jsonString = Gson().toJson(
mapOf(
"id" to itemID,
"readingProgressPercent" to 100.0,
"readingProgressAnchorIndex" to 0,
"force" to true
)
)
)
libraryRepository.updateReadingProgress(itemID, 100.0, 0)
}
}
SavedItemAction.MarkUnread -> {
viewModelScope.launch {
dataService.updateWebReadingProgress(
jsonString = Gson().toJson(
mapOf(
"id" to itemID,
"readingProgressPercent" to 0,
"readingProgressAnchorIndex" to 0,
"force" to true
)
)
)
libraryRepository.updateReadingProgress(itemID, 0.0, 0)
}
}
}

View File

@ -13,10 +13,8 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import app.omnivore.omnivore.utils.DatastoreKeys
import app.omnivore.omnivore.core.datastore.DatastoreRepository
import app.omnivore.omnivore.core.analytics.EventTracker
import app.omnivore.omnivore.R
import app.omnivore.omnivore.core.analytics.EventTracker
import app.omnivore.omnivore.core.data.DataService
import app.omnivore.omnivore.core.data.archiveSavedItem
import app.omnivore.omnivore.core.data.createWebHighlight
@ -26,16 +24,19 @@ import app.omnivore.omnivore.core.data.mergeWebHighlights
import app.omnivore.omnivore.core.data.unarchiveSavedItem
import app.omnivore.omnivore.core.data.updateWebHighlight
import app.omnivore.omnivore.core.data.updateWebReadingProgress
import app.omnivore.omnivore.graphql.generated.type.CreateLabelInput
import app.omnivore.omnivore.core.database.dao.SavedItemDao
import app.omnivore.omnivore.core.database.entities.SavedItem
import app.omnivore.omnivore.core.database.entities.SavedItemLabel
import app.omnivore.omnivore.core.datastore.DatastoreRepository
import app.omnivore.omnivore.core.network.Networker
import app.omnivore.omnivore.core.network.createNewLabel
import app.omnivore.omnivore.core.network.saveUrl
import app.omnivore.omnivore.core.network.savedItem
import app.omnivore.omnivore.core.database.entities.SavedItem
import app.omnivore.omnivore.core.database.entities.SavedItemLabel
import app.omnivore.omnivore.feature.components.HighlightColor
import app.omnivore.omnivore.feature.library.SavedItemAction
import app.omnivore.omnivore.feature.setSavedItemLabels
import app.omnivore.omnivore.graphql.generated.type.CreateLabelInput
import app.omnivore.omnivore.utils.DatastoreKeys
import com.apollographql.apollo3.api.Optional.Companion.presentIfNotNull
import com.google.gson.Gson
import dagger.hilt.android.lifecycle.HiltViewModel
@ -80,6 +81,7 @@ class WebReaderViewModel @Inject constructor(
private val dataService: DataService,
private val networker: Networker,
private val eventTracker: EventTracker,
private val savedItemDao: SavedItemDao // TODO - Use repo
) : ViewModel() {
var lastJavascriptActionLoopUUID: UUID = UUID.randomUUID()
var javascriptDispatchQueue: MutableList<String> = mutableListOf()
@ -384,7 +386,7 @@ class WebReaderViewModel @Inject constructor(
"articleReadingProgress" -> {
viewModelScope.launch {
dataService.updateWebReadingProgress(jsonString)
dataService.updateWebReadingProgress(jsonString, savedItemDao)
}
}

View File

@ -160,36 +160,6 @@ fun readingProgress(item: SavedItemWithLabelsAndHighlights): String {
return ""
}
//var highlightsText: String {
// item.hig ?.let {
// if let highlights = item.highlights, highlights.count > 0 {
// let fmted = LocalText.pluralizedText(key: "number_of_highlights", count: highlights.count)
// if item.wordsCount > 0 {
// return " • \(fmted)"
// }
// return fmted
// }
// return ""
//}
//
//var notesText: String {
// let notes = item.highlights?.filter { item in
// if let highlight = item as? Highlight {
// return !(highlight.annotation ?? "").isEmpty
// }
// return false
// }
//
// if let notes = notes, notes.count > 0 {
// let fmted = LocalText.pluralizedText(key: "number_of_notes", count: notes.count)
// if item.wordsCount > 0 {
// return " • \(fmted)"
// }
// return fmted
// }
// return ""
//}
enum class FlairIcon(
val rawValue: String, val sortOrder: Int