diff --git a/api/src/main/java/com/readrops/api/services/SyncResult.kt b/api/src/main/java/com/readrops/api/services/SyncResult.kt index ebf0962b..20386345 100644 --- a/api/src/main/java/com/readrops/api/services/SyncResult.kt +++ b/api/src/main/java/com/readrops/api/services/SyncResult.kt @@ -9,8 +9,8 @@ data class SyncResult( var starredItems: List = mutableListOf(), var feeds: List = listOf(), var folders: List = listOf(), - var unreadIds: List? = null, - var readIds: List? = null, - var starredIds: List? = null, + var unreadIds: List = listOf(), + var readIds: List = listOf(), + var starredIds: List = listOf(), var isError: Boolean = false ) diff --git a/api/src/main/java/com/readrops/api/services/freshrss/NewFreshRSSDataSource.kt b/api/src/main/java/com/readrops/api/services/freshrss/NewFreshRSSDataSource.kt index efee1495..3d0ec4b5 100644 --- a/api/src/main/java/com/readrops/api/services/freshrss/NewFreshRSSDataSource.kt +++ b/api/src/main/java/com/readrops/api/services/freshrss/NewFreshRSSDataSource.kt @@ -40,8 +40,8 @@ class NewFreshRSSDataSource(private val service: NewFreshRSSService) { feeds = async { getFeeds() }.await() items = async { getItems(listOf(GOOGLE_READ, GOOGLE_STARRED), MAX_ITEMS, null) }.await() starredItems = async { getStarredItems(MAX_STARRED_ITEMS) }.await() - //unreadIds = async { getItemsIds(GOOGLE_READ, GOOGLE_READING_LIST, MAX_ITEMS) }.await() - // starredIds = async { getItemsIds(null, GOOGLE_STARRED, MAX_STARRED_ITEMS) }.await() + unreadIds = async { getItemsIds(GOOGLE_READ, GOOGLE_READING_LIST, MAX_ITEMS) }.await() + starredIds = async { getItemsIds(null, GOOGLE_STARRED, MAX_STARRED_ITEMS) }.await() } } else { SyncResult() @@ -59,7 +59,7 @@ class NewFreshRSSDataSource(private val service: NewFreshRSSService) { suspend fun getStarredItems(max: Int) = service.getStarredItems(max) - suspend fun getItemsIds(excludeTarget: String, includeTarget: String, max: Int): List { + suspend fun getItemsIds(excludeTarget: String?, includeTarget: String, max: Int): List { return service.getItemsIds(excludeTarget, includeTarget, max) } diff --git a/appcompose/src/main/java/com/readrops/app/compose/repositories/FreshRSSRepository.kt b/appcompose/src/main/java/com/readrops/app/compose/repositories/FreshRSSRepository.kt index 60d88e22..1a7c3a26 100644 --- a/appcompose/src/main/java/com/readrops/app/compose/repositories/FreshRSSRepository.kt +++ b/appcompose/src/main/java/com/readrops/app/compose/repositories/FreshRSSRepository.kt @@ -10,6 +10,7 @@ import com.readrops.db.Database import com.readrops.db.entities.Feed import com.readrops.db.entities.Folder import com.readrops.db.entities.Item +import com.readrops.db.entities.ItemState import com.readrops.db.entities.account.Account import org.koin.core.component.KoinComponent @@ -46,6 +47,8 @@ class FreshRSSRepository( insertItems(items, false) insertItems(starredItems, true) + + insertItemsIds(unreadIds, readIds, starredIds.toMutableList()) } return syncResult @@ -103,4 +106,56 @@ class FreshRSSRepository( database.itemDao().insert(itemsToInsert) } } + + private suspend fun insertItemsIds( + unreadIds: List, + readIds: List, + starredIds: MutableList // TODO is it performance wise? + ) { + database.newItemStateDao().deleteItemStates(account.id) + + database.newItemStateDao().insert(unreadIds.map { id -> + val starred = starredIds.count { starredId -> starredId == id } == 1 + + if (starred) { + starredIds.remove(id) + } + + ItemState( + id = 0, + read = false, + starred = starred, + remoteId = id, + accountId = account.id + ) + }) + + database.newItemStateDao().insert(readIds.map { id -> + val starred = starredIds.count { starredId -> starredId == id } == 1 + if (starred) { + starredIds.remove(id) + } + + ItemState( + id = 0, + read = true, + starred = starred, + remoteId = id, + accountId = account.id + ) + }) + + // insert starred items ids which are read + if (starredIds.isNotEmpty()) { + database.newItemStateDao().insert(starredIds.map { id -> + ItemState( + 0, + read = true, + starred = true, + remoteId = id, + accountId = account.id + ) + }) + } + } } \ No newline at end of file diff --git a/db/src/main/java/com/readrops/db/Database.kt b/db/src/main/java/com/readrops/db/Database.kt index 02ce75ab..94cc9ee4 100644 --- a/db/src/main/java/com/readrops/db/Database.kt +++ b/db/src/main/java/com/readrops/db/Database.kt @@ -3,12 +3,23 @@ package com.readrops.db import androidx.room.Database import androidx.room.RoomDatabase import androidx.room.TypeConverters -import com.readrops.db.dao.* +import com.readrops.db.dao.AccountDao +import com.readrops.db.dao.FeedDao +import com.readrops.db.dao.FolderDao +import com.readrops.db.dao.ItemDao +import com.readrops.db.dao.ItemStateChangeDao +import com.readrops.db.dao.ItemStateDao import com.readrops.db.dao.newdao.NewAccountDao import com.readrops.db.dao.newdao.NewFeedDao import com.readrops.db.dao.newdao.NewFolderDao import com.readrops.db.dao.newdao.NewItemDao -import com.readrops.db.entities.* +import com.readrops.db.dao.newdao.NewItemStateChangeDao +import com.readrops.db.dao.newdao.NewItemStateDao +import com.readrops.db.entities.Feed +import com.readrops.db.entities.Folder +import com.readrops.db.entities.Item +import com.readrops.db.entities.ItemState +import com.readrops.db.entities.ItemStateChange import com.readrops.db.entities.account.Account import dev.matrix.roomigrant.GenerateRoomMigrations @@ -38,4 +49,8 @@ abstract class Database : RoomDatabase() { abstract fun newAccountDao(): NewAccountDao abstract fun newFolderDao(): NewFolderDao + + abstract fun newItemStateDao(): NewItemStateDao + + abstract fun newItemStateChangeDao(): NewItemStateChangeDao } \ No newline at end of file diff --git a/db/src/main/java/com/readrops/db/dao/newdao/NewItemStateChangeDao.kt b/db/src/main/java/com/readrops/db/dao/newdao/NewItemStateChangeDao.kt new file mode 100644 index 00000000..cf94bb26 --- /dev/null +++ b/db/src/main/java/com/readrops/db/dao/newdao/NewItemStateChangeDao.kt @@ -0,0 +1,105 @@ +package com.readrops.db.dao.newdao + +import androidx.room.Dao +import androidx.room.Query +import com.readrops.db.entities.Item +import com.readrops.db.entities.ItemStateChange +import com.readrops.db.pojo.ItemReadStarState + +@Dao +interface NewItemStateChangeDao: NewBaseDao { + + @Query("Delete From ItemStateChange Where account_id = :accountId") + suspend fun resetStateChanges(accountId: Int) + + @Query("Select case When ItemState.remote_id is NULL Or ItemState.read = 1 Then 1 else 0 End read, " + + "case When ItemState.remote_id is NULL Or ItemState.starred = 1 Then 1 else 0 End starred," + + "ItemStateChange.read_change, ItemStateChange.star_change, Item.remoteId " + + "From ItemStateChange Inner Join Item On ItemStateChange.id = Item.id " + + "Left Join ItemState On ItemState.remote_id = Item.remoteId Where ItemStateChange.account_id = :accountId") + suspend fun getItemStateChanges(accountId: Int): List + + @Query("Select Item.read, Item.starred," + + "ItemStateChange.read_change, ItemStateChange.star_change, Item.remoteId " + + "From ItemStateChange Inner Join Item On ItemStateChange.id = Item.id " + + "Where ItemStateChange.account_id = :accountId") + suspend fun getNextcloudNewsStateChanges(accountId: Int): List + + @Query("Select Case When :itemId In (Select id From ItemStateChange Where read_change = 1) Then 1 Else 0 End") + suspend fun readStateChangeExists(itemId: Int): Boolean + + @Query("Select Case When :itemId In (Select id From ItemStateChange Where star_change = 1) Then 1 Else 0 End") + suspend fun starStateChangeExists(itemId: Int): Boolean + + suspend fun upsertItemReadStateChange(item: Item, accountId: Int, useSeparateState: Boolean) { + if (itemStateChangeExists(item.id, accountId)) { + val oldItemReadState = if (useSeparateState) + getItemReadState(item.remoteId!!, accountId) + else + getStandardItemReadState(item.remoteId!!, accountId) + + val readChange = item.isRead != oldItemReadState + + if (readChange) { + val oldItemStateChange = selectItemStateChange(item.id) + val newReadChange = !oldItemStateChange.readChange + + if (!newReadChange && !oldItemStateChange.starChange) { + delete(oldItemStateChange) + } else { + updateItemReadStateChange(newReadChange, oldItemStateChange.id) + } + } + } else { + insert(ItemStateChange(id = item.id, readChange = true, accountId = accountId)) + } + } + + suspend fun upsertItemStarStateChange(item: Item, accountId: Int, useSeparateState: Boolean) { + if (itemStateChangeExists(item.id, accountId)) { + val oldItemStarState = if (useSeparateState) + getItemStarState(item.remoteId!!, accountId) + else + getStandardItemStarState(item.remoteId!!, accountId) + + val starChange = item.isStarred != oldItemStarState + + if (starChange) { + val oldItemStateChange = selectItemStateChange(item.id) + val newStarChange = !oldItemStateChange.starChange + + if (!newStarChange && !oldItemStateChange.readChange) { + delete(oldItemStateChange) + } else { + updateItemStarStateChange(newStarChange, oldItemStateChange.id) + } + } + } else { + insert(ItemStateChange(id = item.id, starChange = true, accountId = accountId)) + } + } + + @Query("Select * From ItemStateChange Where id = :id") + fun selectItemStateChange(id: Int): ItemStateChange + + @Query("Select case When Exists (Select id, account_id From ItemStateChange Where id = :id And account_id = :accountId) Then 1 else 0 End") + fun itemStateChangeExists(id: Int, accountId: Int): Boolean + + @Query("Select read From ItemState Where remote_id = :remoteId And account_id = :accountId") + fun getItemReadState(remoteId: String, accountId: Int): Boolean + + @Query("Select read From Item Inner Join Feed On Item.feed_id = Feed.id Where Item.remoteId = :remoteId And account_id = :accountId") + fun getStandardItemReadState(remoteId: String, accountId: Int): Boolean + + @Query("Select starred From ItemState Where remote_id = :remoteId And account_id = :accountId") + fun getItemStarState(remoteId: String, accountId: Int): Boolean + + @Query("Select starred From Item Inner Join Feed On Item.feed_id = Feed.id Where Item.remoteId = :remoteId And account_id = :accountId") + fun getStandardItemStarState(remoteId: String, accountId: Int): Boolean + + @Query("Update ItemStateChange set read_change = :readChange Where id = :id") + fun updateItemReadStateChange(readChange: Boolean, id: Int) + + @Query("Update ItemStateChange set star_change = :starChange Where id = :id") + fun updateItemStarStateChange(starChange: Boolean, id: Int) +} \ No newline at end of file diff --git a/db/src/main/java/com/readrops/db/dao/newdao/NewItemStateDao.kt b/db/src/main/java/com/readrops/db/dao/newdao/NewItemStateDao.kt new file mode 100644 index 00000000..209456d2 --- /dev/null +++ b/db/src/main/java/com/readrops/db/dao/newdao/NewItemStateDao.kt @@ -0,0 +1,37 @@ +package com.readrops.db.dao.newdao + +import androidx.room.Dao +import androidx.room.Query +import com.readrops.db.entities.ItemState + +@Dao +interface NewItemStateDao : NewBaseDao { + + @Query("Delete From ItemState Where account_id = :accountId") + suspend fun deleteItemStates(accountId: Int) + + @Query("Update ItemState set read = :read Where remote_id = :remoteId And account_id = :accountId") + suspend fun updateItemReadState(read: Boolean, remoteId: String, accountId: Int) + + @Query("Update ItemState set starred = :star Where remote_id = :remoteId And account_id = :accountId") + suspend fun updateItemStarState(star: Boolean, remoteId: String, accountId: Int) + + @Query("Select case When Exists (Select remote_id, account_id From ItemState Where remote_id = :remoteId And account_id = :accountId) Then 1 else 0 End") + suspend fun itemStateExists(remoteId: String, accountId: Int): Boolean + + suspend fun upsertItemReadState(itemState: ItemState) { + if (itemStateExists(itemState.remoteId, itemState.accountId)) { + updateItemReadState(itemState.read, itemState.remoteId, itemState.accountId) + } else { + insert(itemState) + } + } + + suspend fun upsertItemStarState(itemState: ItemState) { + if (itemStateExists(itemState.remoteId, itemState.accountId)) { + updateItemStarState(itemState.starred, itemState.remoteId, itemState.accountId) + } else { + insert(itemState) + } + } +} \ No newline at end of file