This commit is contained in:
aminecmi 2022-09-24 14:43:24 +02:00
parent 4184bbb900
commit 6d11dfb80c
3 changed files with 130 additions and 87 deletions

View File

@ -105,4 +105,16 @@ class SelfossModel {
return this return this
} }
} }
class StatusAndData<T>(val success: Boolean, val data: T? = null) {
companion object {
fun <T> succes(d: T): StatusAndData<T> {
return StatusAndData(true, d)
}
fun <T> error(): StatusAndData<T> {
return StatusAndData(false)
}
}
}
} }

View File

@ -47,7 +47,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
suspend fun getNewerItems(): ArrayList<SelfossModel.Item> { suspend fun getNewerItems(): ArrayList<SelfossModel.Item> {
// TODO: Use the updatedSince parameter // TODO: Use the updatedSince parameter
var fetchedItems: List<SelfossModel.Item>? = null var fetchedItems: SelfossModel.StatusAndData<List<SelfossModel.Item>> = SelfossModel.StatusAndData.error()
if (isNetworkAvailable()) { if (isNetworkAvailable()) {
fetchedItems = api.getItems( fetchedItems = api.getItems(
displayedItems.type, displayedItems.type,
@ -59,23 +59,25 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
) )
} else { } else {
if (appSettingsService.isItemCachingEnabled()) { if (appSettingsService.isItemCachingEnabled()) {
fetchedItems = getDBItems().filter { fetchedItems = SelfossModel.StatusAndData.succes(
getDBItems().filter {
displayedItems == ItemType.ALL || displayedItems == ItemType.ALL ||
(it.unread && displayedItems == ItemType.UNREAD) || (it.unread && displayedItems == ItemType.UNREAD) ||
(it.starred && displayedItems == ItemType.STARRED) (it.starred && displayedItems == ItemType.STARRED)
}.map { it.toView() } }.map { it.toView() }
)
} }
} }
if (fetchedItems != null) { if (fetchedItems.success && fetchedItems.data != null) {
items = ArrayList(fetchedItems) items = ArrayList(fetchedItems.data!!)
sortItems() sortItems()
} }
return items return items
} }
suspend fun getOlderItems(): ArrayList<SelfossModel.Item> { suspend fun getOlderItems(): ArrayList<SelfossModel.Item> {
var fetchedItems: List<SelfossModel.Item>? = null var fetchedItems: SelfossModel.StatusAndData<List<SelfossModel.Item>> = SelfossModel.StatusAndData.error()
if (isNetworkAvailable()) { if (isNetworkAvailable()) {
val offset = items.size val offset = items.size
fetchedItems = api.getItems( fetchedItems = api.getItems(
@ -88,16 +90,16 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
) )
} // When using the db cache, we load everything the first time, so there should be nothing more to load. } // When using the db cache, we load everything the first time, so there should be nothing more to load.
if (fetchedItems != null) { if (fetchedItems.success && fetchedItems.data != null) {
items.addAll(fetchedItems) items.addAll(fetchedItems.data!!)
sortItems() sortItems()
} }
return items return items
} }
private suspend fun getMaxItemsForBackground(itemType: ItemType): List<SelfossModel.Item>? { private suspend fun getMaxItemsForBackground(itemType: ItemType): List<SelfossModel.Item> {
return if (isNetworkAvailable()) { return if (isNetworkAvailable()) {
api.getItems( val items = api.getItems(
itemType.type, itemType.type,
0, 0,
tagFilter?.tag, tagFilter?.tag,
@ -106,6 +108,11 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
null, null,
200 200
) )
return if (items.success && items.data != null) {
items.data
} else {
emptyList()
}
} else { } else {
emptyList() emptyList()
} }
@ -119,10 +126,10 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
var success = false var success = false
if (isNetworkAvailable()) { if (isNetworkAvailable()) {
val response = api.stats() val response = api.stats()
if (response != null) { if (response.success && response.data != null) {
badgeUnread = response.unread badgeUnread = response.data.unread
badgeAll = response.total badgeAll = response.data.total
badgeStarred = response.starred badgeStarred = response.data.starred
success = true success = true
} }
} else { } else {
@ -138,10 +145,10 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
suspend fun getTags(): List<SelfossModel.Tag>? { suspend fun getTags(): List<SelfossModel.Tag>? {
return if (isNetworkAvailable()) { return if (isNetworkAvailable()) {
val apiTags = api.tags() val apiTags = api.tags()
if (apiTags != null && (appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled())) { if (apiTags.success && apiTags.data != null && (appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled())) {
resetDBTagsWithData(apiTags) resetDBTagsWithData(apiTags.data)
} }
apiTags apiTags.data
} else { } else {
getDBTags().map { it.toView() } getDBTags().map { it.toView() }
} }
@ -149,7 +156,12 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
suspend fun getSpouts(): Map<String, SelfossModel.Spout>? { suspend fun getSpouts(): Map<String, SelfossModel.Spout>? {
return if (isNetworkAvailable()) { return if (isNetworkAvailable()) {
api.spouts() val spouts = api.spouts()
return if (spouts.success && spouts.data != null) {
spouts.data
} else {
emptyMap() // TODO: do something here
}
} else { } else {
throw NetworkUnavailableException() throw NetworkUnavailableException()
} }
@ -158,10 +170,10 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
suspend fun getSources(): ArrayList<SelfossModel.Source>? { suspend fun getSources(): ArrayList<SelfossModel.Source>? {
return if (isNetworkAvailable()) { return if (isNetworkAvailable()) {
val apiSources = api.sources() val apiSources = api.sources()
if (apiSources != null && (appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled())) { if (apiSources.success && apiSources.data != null && (appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled())) {
resetDBSourcesWithData(apiSources) resetDBSourcesWithData(apiSources.data)
} }
apiSources apiSources.data
} else { } else {
ArrayList(getDBSources().map { it.toView() }) ArrayList(getDBSources().map { it.toView() })
} }
@ -178,7 +190,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
private suspend fun markAsReadById(id: Int): Boolean { private suspend fun markAsReadById(id: Int): Boolean {
return if (isNetworkAvailable()) { return if (isNetworkAvailable()) {
api.markAsRead(id.toString())?.isSuccess == true api.markAsRead(id.toString())?.isSuccess
} else { } else {
insertDBAction(id.toString(), read = true) insertDBAction(id.toString(), read = true)
true true
@ -197,7 +209,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
private suspend fun unmarkAsReadById(id: Int): Boolean { private suspend fun unmarkAsReadById(id: Int): Boolean {
return if (isNetworkAvailable()) { return if (isNetworkAvailable()) {
api.unmarkAsRead(id.toString())?.isSuccess == true api.unmarkAsRead(id.toString())?.isSuccess
} else { } else {
insertDBAction(id.toString(), unread = true) insertDBAction(id.toString(), unread = true)
true true
@ -215,7 +227,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
private suspend fun starrById(id: Int): Boolean { private suspend fun starrById(id: Int): Boolean {
return if (isNetworkAvailable()) { return if (isNetworkAvailable()) {
api.starr(id.toString())?.isSuccess == true api.starr(id.toString())?.isSuccess
} else { } else {
insertDBAction(id.toString(), starred = true) insertDBAction(id.toString(), starred = true)
true true
@ -233,7 +245,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
private suspend fun unstarrById(id: Int): Boolean { private suspend fun unstarrById(id: Int): Boolean {
return if (isNetworkAvailable()) { return if (isNetworkAvailable()) {
api.unstarr(id.toString())?.isSuccess == true api.unstarr(id.toString())?.isSuccess
} else { } else {
insertDBAction(id.toString(), starred = true) insertDBAction(id.toString(), starred = true)
true true
@ -243,7 +255,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
suspend fun markAllAsRead(items: ArrayList<SelfossModel.Item>): Boolean { suspend fun markAllAsRead(items: ArrayList<SelfossModel.Item>): Boolean {
var success = false var success = false
if (isNetworkAvailable() && api.markAllAsRead(items.map { it.id.toString() })?.isSuccess == true) { if (isNetworkAvailable() && api.markAllAsRead(items.map { it.id.toString() })?.isSuccess) {
success = true success = true
for (item in items) { for (item in items) {
markAsReadLocally(item) markAsReadLocally(item)
@ -332,7 +344,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
suspend fun updateRemote(): Boolean { suspend fun updateRemote(): Boolean {
return if (isNetworkAvailable()) { return if (isNetworkAvailable()) {
api.update()?.equals("finished") ?: false api.update()?.equals("finished")
} else { } else {
false false
} }
@ -365,8 +377,8 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
if (isNetworkAvailable()) { if (isNetworkAvailable()) {
val fetchedVersion = api.version() val fetchedVersion = api.version()
if (fetchedVersion != null && fetchedVersion.getApiMajorVersion() != apiMajorVersion) { if (fetchedVersion.success && fetchedVersion.data != null && fetchedVersion.data.getApiMajorVersion() != apiMajorVersion) {
appSettingsService.updateApiVersion(fetchedVersion.getApiMajorVersion()) appSettingsService.updateApiVersion(fetchedVersion.data.getApiMajorVersion())
} }
} }
} }
@ -383,7 +395,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
fun getDBSources(): List<SOURCE> = db.sourcesQueries.sources().executeAsList() fun getDBSources(): List<SOURCE> = db.sourcesQueries.sources().executeAsList()
fun resetDBTagsWithData(tagEntities: List<SelfossModel.Tag>) { private fun resetDBTagsWithData(tagEntities: List<SelfossModel.Tag>) {
db.tagsQueries.deleteAllTags() db.tagsQueries.deleteAllTags()
db.tagsQueries.transaction { db.tagsQueries.transaction {
@ -393,7 +405,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
} }
} }
fun resetDBSourcesWithData(sources: List<SelfossModel.Source>) { private fun resetDBSourcesWithData(sources: List<SelfossModel.Source>) {
db.sourcesQueries.deleteAllSources() db.sourcesQueries.deleteAllSources()
db.sourcesQueries.transaction { db.sourcesQueries.transaction {
@ -425,7 +437,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
val newItems = getMaxItemsForBackground(ItemType.UNREAD) val newItems = getMaxItemsForBackground(ItemType.UNREAD)
val allItems = getMaxItemsForBackground(ItemType.ALL) val allItems = getMaxItemsForBackground(ItemType.ALL)
val starredItems = getMaxItemsForBackground(ItemType.STARRED) val starredItems = getMaxItemsForBackground(ItemType.STARRED)
insertDBItems(newItems.orEmpty() + allItems.orEmpty() + starredItems.orEmpty()) insertDBItems(newItems + allItems + starredItems)
return newItems return newItems
} catch (e: Throwable) { } catch (e: Throwable) {
// We do nothing // We do nothing

View File

@ -10,6 +10,7 @@ import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.plugins.logging.* import io.ktor.client.plugins.logging.*
import io.ktor.client.request.* import io.ktor.client.request.*
import io.ktor.client.request.forms.* import io.ktor.client.request.forms.*
import io.ktor.client.statement.*
import io.ktor.http.* import io.ktor.http.*
import io.ktor.serialization.kotlinx.json.* import io.ktor.serialization.kotlinx.json.*
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
@ -34,7 +35,7 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
appSettingsService.logApiCalls(message) appSettingsService.logApiCalls(message)
} }
} }
level = LogLevel.ALL level = LogLevel.INFO
} }
install(HttpTimeout) { install(HttpTimeout) {
requestTimeoutMillis = appSettingsService.getApiTimeout() requestTimeoutMillis = appSettingsService.getApiTimeout()
@ -65,11 +66,11 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
client = createHttpClient() client = createHttpClient()
} }
suspend fun login(): SelfossModel.SuccessResponse? = suspend fun login(): SelfossModel.SuccessResponse =
client.get(url("/login")) { maybeResponse(client.get(url("/login")) {
parameter("username", appSettingsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword()) parameter("password", appSettingsService.getPassword())
}.body() })
suspend fun getItems( suspend fun getItems(
type: String, type: String,
@ -79,8 +80,8 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
search: String?, search: String?,
updatedSince: String?, updatedSince: String?,
items: Int? = null items: Int? = null
): List<SelfossModel.Item>? = ): SelfossModel.StatusAndData<List<SelfossModel.Item>> =
client.get(url("/items")) { bodyOrFailure(client.get(url("/items")) {
parameter("username", appSettingsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword()) parameter("password", appSettingsService.getPassword())
parameter("type", type) parameter("type", type)
@ -90,74 +91,74 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
parameter("updatedsince", updatedSince) parameter("updatedsince", updatedSince)
parameter("items", items ?: appSettingsService.getItemsNumber()) parameter("items", items ?: appSettingsService.getItemsNumber())
parameter("offset", offset) parameter("offset", offset)
}.body() })
suspend fun stats(): SelfossModel.Stats? = suspend fun stats(): SelfossModel.StatusAndData<SelfossModel.Stats> =
client.get(url("/stats")) { bodyOrFailure(client.get(url("/stats")) {
parameter("username", appSettingsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword()) parameter("password", appSettingsService.getPassword())
}.body() })
suspend fun tags(): List<SelfossModel.Tag>? = suspend fun tags(): SelfossModel.StatusAndData<List<SelfossModel.Tag>> =
client.get(url("/tags")) { bodyOrFailure(client.get(url("/tags")) {
parameter("username", appSettingsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword()) parameter("password", appSettingsService.getPassword())
}.body() })
suspend fun update(): String? = suspend fun update(): SelfossModel.StatusAndData<String> =
client.get(url("/update")) { bodyOrFailure(client.get(url("/update")) {
parameter("username", appSettingsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword()) parameter("password", appSettingsService.getPassword())
}.body() })
suspend fun spouts(): Map<String, SelfossModel.Spout>? = suspend fun spouts(): SelfossModel.StatusAndData<Map<String, SelfossModel.Spout>> =
client.get(url("/sources/spouts")) { bodyOrFailure(client.get(url("/sources/spouts")) {
parameter("username", appSettingsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword()) parameter("password", appSettingsService.getPassword())
}.body() })
suspend fun sources(): ArrayList<SelfossModel.Source>? = suspend fun sources(): SelfossModel.StatusAndData<ArrayList<SelfossModel.Source>> =
client.get(url("/sources/list")) { bodyOrFailure(client.get(url("/sources/list")) {
parameter("username", appSettingsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword()) parameter("password", appSettingsService.getPassword())
}.body() })
suspend fun version(): SelfossModel.ApiVersion? = suspend fun version(): SelfossModel.StatusAndData<SelfossModel.ApiVersion> =
client.get(url("/api/about")).body() bodyOrFailure(client.get(url("/api/about")))
suspend fun markAsRead(id: String): SelfossModel.SuccessResponse? = suspend fun markAsRead(id: String): SelfossModel.SuccessResponse =
client.post(url("/mark/$id")) { maybeResponse(client.post(url("/mark/$id")) {
parameter("username", appSettingsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword()) parameter("password", appSettingsService.getPassword())
}.body() })
suspend fun unmarkAsRead(id: String): SelfossModel.SuccessResponse? = suspend fun unmarkAsRead(id: String): SelfossModel.SuccessResponse =
client.post(url("/unmark/$id")) { maybeResponse(client.post(url("/unmark/$id")) {
parameter("username", appSettingsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword()) parameter("password", appSettingsService.getPassword())
}.body() })
suspend fun starr(id: String): SelfossModel.SuccessResponse? = suspend fun starr(id: String): SelfossModel.SuccessResponse =
client.post(url("/starr/$id")) { maybeResponse(client.post(url("/starr/$id")) {
parameter("username", appSettingsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword()) parameter("password", appSettingsService.getPassword())
}.body() })
suspend fun unstarr(id: String): SelfossModel.SuccessResponse? = suspend fun unstarr(id: String): SelfossModel.SuccessResponse =
client.post(url("/unstarr/$id")) { maybeResponse(client.post(url("/unstarr/$id")) {
parameter("username", appSettingsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword()) parameter("password", appSettingsService.getPassword())
}.body() })
suspend fun markAllAsRead(ids: List<String>): SelfossModel.SuccessResponse? = suspend fun markAllAsRead(ids: List<String>): SelfossModel.SuccessResponse =
client.submitForm( maybeResponse(client.submitForm(
url = url("/mark"), url = url("/mark"),
formParameters = Parameters.build { formParameters = Parameters.build {
append("username", appSettingsService.getUserName()) append("username", appSettingsService.getUserName())
append("password", appSettingsService.getPassword()) append("password", appSettingsService.getPassword())
ids.map { append("ids[]", it) } ids.map { append("ids[]", it) }
} }
).body() ))
suspend fun createSourceForVersion( suspend fun createSourceForVersion(
title: String, title: String,
@ -166,12 +167,14 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
tags: String, tags: String,
filter: String, filter: String,
version: Int version: Int
): SelfossModel.SuccessResponse? = ): SelfossModel.SuccessResponse =
maybeResponse(
if (version > 1) { if (version > 1) {
createSource2(title, url, spout, tags, filter) createSource2(title, url, spout, tags, filter)
} else { } else {
createSource(title, url, spout, tags, filter) createSource(title, url, spout, tags, filter)
} }
)
suspend fun createSource( suspend fun createSource(
title: String, title: String,
@ -179,7 +182,7 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
spout: String, spout: String,
tags: String, tags: String,
filter: String filter: String
): SelfossModel.SuccessResponse? = ): HttpResponse =
client.submitForm( client.submitForm(
url = url("/source?username=${appSettingsService.getUserName()}&password=${appSettingsService.getPassword()}"), url = url("/source?username=${appSettingsService.getUserName()}&password=${appSettingsService.getPassword()}"),
formParameters = Parameters.build { formParameters = Parameters.build {
@ -189,7 +192,7 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
append("tags", tags) append("tags", tags)
append("filter", filter) append("filter", filter)
} }
).body() )
suspend fun createSource2( suspend fun createSource2(
title: String, title: String,
@ -197,7 +200,7 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
spout: String, spout: String,
tags: String, tags: String,
filter: String filter: String
): SelfossModel.SuccessResponse? = ): HttpResponse =
client.submitForm( client.submitForm(
url = url("/source?username=${appSettingsService.getUserName()}&password=${appSettingsService.getPassword()}"), url = url("/source?username=${appSettingsService.getUserName()}&password=${appSettingsService.getPassword()}"),
formParameters = Parameters.build { formParameters = Parameters.build {
@ -207,11 +210,27 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
append("tags[]", tags) append("tags[]", tags)
append("filter", filter) append("filter", filter)
} }
).body() )
suspend fun deleteSource(id: Int): SelfossModel.SuccessResponse? = suspend fun deleteSource(id: Int): SelfossModel.SuccessResponse =
client.delete(url("/source/$id")) { maybeResponse(client.delete(url("/source/$id")) {
parameter("username", appSettingsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword()) parameter("password", appSettingsService.getPassword())
}.body() })
suspend fun maybeResponse(r: HttpResponse): SelfossModel.SuccessResponse {
return if (r.status.isSuccess()) {
r.body()
} else {
SelfossModel.SuccessResponse(false)
}
}
suspend inline fun <reified T> bodyOrFailure(r: HttpResponse): SelfossModel.StatusAndData<T> {
return if (r.status.isSuccess()) {
SelfossModel.StatusAndData.succes(r.body())
} else {
SelfossModel.StatusAndData.error()
}
}
} }