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.ItemAction
import com.readrops.api.services.fever.adapters.FeverFeeds
import com.readrops.api.utils.ApiUtils
import com.readrops.api.utils.exceptions.LoginFailedException
import com.readrops.app.util.Utils
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.Item
import com.readrops.db.entities.ItemState
import com.readrops.db.entities.ItemStateChange
import com.readrops.db.entities.account.Account
import okhttp3.MultipartBody
class FeverRepository(
database: Database,
@ -89,20 +88,40 @@ class FeverRepository(
override suspend fun deleteFolder(folder: Folder) {}
override suspend fun setItemReadState(item: Item) {
val action =
if (item.isRead) ItemAction.ReadStateAction.ReadAction else ItemAction.ReadStateAction.UnreadAction
val action = if (item.isRead) {
ItemAction.ReadStateAction.ReadAction
} else {
ItemAction.ReadStateAction.UnreadAction
}
return setItemState(item, action)
}
override suspend fun setItemStarState(item: Item) {
val action =
if (item.isStarred) ItemAction.StarStateAction.StarAction else ItemAction.StarStateAction.UnstarAction
val action = if (item.isStarred) {
ItemAction.StarStateAction.StarAction
} else {
ItemAction.StarStateAction.UnstarAction
}
return setItemState(item, action)
}
private suspend fun setItemState(item: Item, action: ItemAction) {
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(
read = item.isRead,
starred = item.isStarred,
@ -110,21 +129,32 @@ class FeverRepository(
accountId = account.id,
)
val completable = if (action is ItemAction.ReadStateAction) {
// local state change
if (action is ItemAction.ReadStateAction) {
database.itemStateDao().upsertItemReadState(itemState)
} else {
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) {
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)
} else {
super.setItemStarState(item)
}
Log.e(TAG, "setItemStarState: ${e.message}")
error(e.message!!)
}
}
@ -134,12 +164,26 @@ class FeverRepository(
for (stateChange in stateChanges) {
val action = if (stateChange.readChange) {
if (stateChange.read) ItemAction.ReadStateAction.ReadAction else ItemAction.ReadStateAction.UnreadAction
} else { // star change
if (stateChange.starred) ItemAction.StarStateAction.StarAction else ItemAction.StarStateAction.UnstarAction
when {
stateChange.read -> ItemAction.ReadStateAction.ReadAction
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 ->
val starred = starredIds.any { starredId -> starredId == unreadId }
if (starred) starredIds.remove(unreadId)
if (starred) {
starredIds.remove(unreadId)
}
ItemState(
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 {
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")
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.remote_id " +
"From ItemStateChange Inner Join Item On ItemStateChange.id = Item.id " +
"Left Join ItemState On ItemState.remote_id = Item.remote_id Where ItemStateChange.account_id = :accountId")
@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.id as id, ItemStateChange.read_change, ItemStateChange.star_change, Item.remote_id From ItemStateChange
Inner Join Item On ItemStateChange.id = Item.id Left Join ItemState On ItemState.remote_id = Item.remote_id
Where ItemStateChange.account_id = :accountId""")
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")

View File

@ -7,6 +7,9 @@ import com.readrops.db.entities.ItemState
@Dao
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")
suspend fun deleteItemStates(accountId: Int)

View File

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

View File

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