Rework Fever read/star state management

This commit is contained in:
Shinokuni 2024-08-08 19:42:13 +02:00
parent f7cd8ac998
commit 0c3c3d8ddb
5 changed files with 91 additions and 41 deletions

View File

@ -6,7 +6,6 @@ import com.readrops.api.services.fever.FeverDataSource
import com.readrops.api.services.fever.FeverSyncData import com.readrops.api.services.fever.FeverSyncData
import com.readrops.api.services.fever.ItemAction import com.readrops.api.services.fever.ItemAction
import com.readrops.api.services.fever.adapters.FeverFeeds import com.readrops.api.services.fever.adapters.FeverFeeds
import com.readrops.api.utils.ApiUtils
import com.readrops.api.utils.exceptions.LoginFailedException import com.readrops.api.utils.exceptions.LoginFailedException
import com.readrops.app.util.Utils import com.readrops.app.util.Utils
import com.readrops.db.Database import com.readrops.db.Database
@ -14,8 +13,8 @@ import com.readrops.db.entities.Feed
import com.readrops.db.entities.Folder import com.readrops.db.entities.Folder
import com.readrops.db.entities.Item import com.readrops.db.entities.Item
import com.readrops.db.entities.ItemState import com.readrops.db.entities.ItemState
import com.readrops.db.entities.ItemStateChange
import com.readrops.db.entities.account.Account import com.readrops.db.entities.account.Account
import okhttp3.MultipartBody
class FeverRepository( class FeverRepository(
database: Database, database: Database,
@ -89,20 +88,40 @@ class FeverRepository(
override suspend fun deleteFolder(folder: Folder) {} override suspend fun deleteFolder(folder: Folder) {}
override suspend fun setItemReadState(item: Item) { override suspend fun setItemReadState(item: Item) {
val action = val action = if (item.isRead) {
if (item.isRead) ItemAction.ReadStateAction.ReadAction else ItemAction.ReadStateAction.UnreadAction ItemAction.ReadStateAction.ReadAction
} else {
ItemAction.ReadStateAction.UnreadAction
}
return setItemState(item, action) return setItemState(item, action)
} }
override suspend fun setItemStarState(item: Item) { override suspend fun setItemStarState(item: Item) {
val action = val action = if (item.isStarred) {
if (item.isStarred) ItemAction.StarStateAction.StarAction else ItemAction.StarStateAction.UnstarAction ItemAction.StarStateAction.StarAction
} else {
ItemAction.StarStateAction.UnstarAction
}
return setItemState(item, action) return setItemState(item, action)
} }
private suspend fun setItemState(item: Item, action: ItemAction) { private suspend fun setItemState(item: Item, action: ItemAction) {
try { try {
feverDataSource.setItemState(account.login!!, account.password!!, action.value, item.remoteId!!) val currentState = database.itemStateDao().selectItemState(account.id, item.remoteId!!)
// if new state the same as the current one, do nothing
if (action is ItemAction.ReadStateAction) {
if (item.isRead == currentState.read) {
return
}
} else {
if (item.isStarred == currentState.starred) {
return
}
}
val itemState = ItemState( val itemState = ItemState(
read = item.isRead, read = item.isRead,
starred = item.isStarred, starred = item.isStarred,
@ -110,21 +129,32 @@ class FeverRepository(
accountId = account.id, accountId = account.id,
) )
val completable = if (action is ItemAction.ReadStateAction) { // local state change
if (action is ItemAction.ReadStateAction) {
database.itemStateDao().upsertItemReadState(itemState) database.itemStateDao().upsertItemReadState(itemState)
} else { } else {
database.itemStateDao().upsertItemStarState(itemState) database.itemStateDao().upsertItemStarState(itemState)
} }
// remote state change
feverDataSource.setItemState(
account.login!!,
account.password!!,
action.value,
item.remoteId!!
)
// time to process item state changes which couldn't be sent previously (no network for example)
sendPreviousItemStateChanges()
} catch (e: Exception) { } catch (e: Exception) {
val completable = if (action is ItemAction.ReadStateAction) { // error occurred, probably network error, so we keep this change until the next state change
if (action is ItemAction.ReadStateAction) {
super.setItemReadState(item) super.setItemReadState(item)
} else { } else {
super.setItemStarState(item) super.setItemStarState(item)
} }
Log.e(TAG, "setItemStarState: ${e.message}") Log.e(TAG, "setItemStarState: ${e.message}")
error(e.message!!)
} }
} }
@ -134,12 +164,26 @@ class FeverRepository(
for (stateChange in stateChanges) { for (stateChange in stateChanges) {
val action = if (stateChange.readChange) { val action = if (stateChange.readChange) {
if (stateChange.read) ItemAction.ReadStateAction.ReadAction else ItemAction.ReadStateAction.UnreadAction when {
} else { // star change stateChange.read -> ItemAction.ReadStateAction.ReadAction
if (stateChange.starred) ItemAction.StarStateAction.StarAction else ItemAction.StarStateAction.UnstarAction else -> ItemAction.ReadStateAction.UnreadAction
}
} else {
when {
stateChange.starred -> ItemAction.StarStateAction.StarAction
else -> ItemAction.StarStateAction.UnstarAction
}
} }
feverDataSource.setItemState(account.login!!, account.password!!, action.value, stateChange.remoteId) feverDataSource.setItemState(
account.login!!,
account.password!!,
action.value,
stateChange.remoteId
)
database.itemStateChangeDao()
.delete(ItemStateChange(id = stateChange.id, accountId = account.id))
} }
} }
@ -195,7 +239,9 @@ class FeverRepository(
database.itemStateDao().insert(unreadIds.map { unreadId -> database.itemStateDao().insert(unreadIds.map { unreadId ->
val starred = starredIds.any { starredId -> starredId == unreadId } val starred = starredIds.any { starredId -> starredId == unreadId }
if (starred) starredIds.remove(unreadId) if (starred) {
starredIds.remove(unreadId)
}
ItemState( ItemState(
id = 0, id = 0,
@ -219,14 +265,6 @@ class FeverRepository(
} }
} }
private fun getFeverRequestBody(): MultipartBody {
val credentials = ApiUtils.md5hash("${account.login}:${account.password}")
return MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("api_key", credentials)
.build()
}
companion object { companion object {
val TAG: String = FeverRepository::class.java.simpleName val TAG: String = FeverRepository::class.java.simpleName
} }

View File

@ -12,11 +12,11 @@ interface ItemStateChangeDao: BaseDao<ItemStateChange> {
@Query("Delete From ItemStateChange Where account_id = :accountId") @Query("Delete From ItemStateChange Where account_id = :accountId")
suspend fun resetStateChanges(accountId: Int) suspend fun resetStateChanges(accountId: Int)
@Query("Select case When ItemState.remote_id is NULL Or ItemState.read = 1 Then 1 else 0 End read, " + @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," + case When ItemState.remote_id is NULL Or ItemState.starred = 1 Then 1 else 0 End starred,
"ItemStateChange.read_change, ItemStateChange.star_change, Item.remote_id " + ItemStateChange.id as id, ItemStateChange.read_change, ItemStateChange.star_change, Item.remote_id From ItemStateChange
"From ItemStateChange Inner Join Item On ItemStateChange.id = Item.id " + Inner Join Item On ItemStateChange.id = Item.id Left Join ItemState On ItemState.remote_id = Item.remote_id
"Left Join ItemState On ItemState.remote_id = Item.remote_id Where ItemStateChange.account_id = :accountId") Where ItemStateChange.account_id = :accountId""")
suspend fun selectItemStateChanges(accountId: Int): List<ItemReadStarState> suspend fun selectItemStateChanges(accountId: Int): List<ItemReadStarState>
@Query("Select Case When :itemId In (Select id From ItemStateChange Where read_change = 1) Then 1 Else 0 End") @Query("Select Case When :itemId In (Select id From ItemStateChange Where read_change = 1) Then 1 Else 0 End")

View File

@ -7,6 +7,9 @@ import com.readrops.db.entities.ItemState
@Dao @Dao
interface ItemStateDao : BaseDao<ItemState> { interface ItemStateDao : BaseDao<ItemState> {
@Query("Select * From ItemState Where account_id = :accountId And remote_id = :remoteId")
suspend fun selectItemState(accountId: Int, remoteId: String): ItemState
@Query("Delete From ItemState Where account_id = :accountId") @Query("Delete From ItemState Where account_id = :accountId")
suspend fun deleteItemStates(accountId: Int) suspend fun deleteItemStates(accountId: Int)

View File

@ -8,10 +8,14 @@ import androidx.room.PrimaryKey
import com.readrops.db.entities.account.Account import com.readrops.db.entities.account.Account
@Entity( @Entity(
foreignKeys = [ForeignKey( foreignKeys = [
entity = Account::class, parentColumns = ["id"], ForeignKey(
childColumns = ["account_id"], onDelete = ForeignKey.CASCADE entity = Account::class,
)] parentColumns = ["id"],
childColumns = ["account_id"],
onDelete = ForeignKey.CASCADE
)
]
) )
data class ItemStateChange( data class ItemStateChange(
@PrimaryKey val id: Int = 0, @PrimaryKey val id: Int = 0,
@ -21,10 +25,14 @@ data class ItemStateChange(
) )
@Entity( @Entity(
foreignKeys = [ForeignKey( foreignKeys = [
entity = Account::class, parentColumns = ["id"], ForeignKey(
childColumns = ["account_id"], onDelete = ForeignKey.CASCADE entity = Account::class,
)], parentColumns = ["id"],
childColumns = ["account_id"],
onDelete = ForeignKey.CASCADE
)
],
indices = [Index( // TODO check potential performance regression at insertion when synchronizing indices = [Index( // TODO check potential performance regression at insertion when synchronizing
value = ["remote_id", "account_id"], value = ["remote_id", "account_id"],
unique = true unique = true

View File

@ -3,6 +3,7 @@ package com.readrops.db.pojo
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
data class ItemReadStarState( data class ItemReadStarState(
val id: Int,
@ColumnInfo(name = "remote_id") val remoteId: String, @ColumnInfo(name = "remote_id") val remoteId: String,
val read: Boolean, val read: Boolean,
val starred: Boolean, val starred: Boolean,