From 6d11dfb80cf4a60614b91f94ca3f8b32500c5611 Mon Sep 17 00:00:00 2001 From: aminecmi Date: Sat, 24 Sep 2022 14:43:24 +0200 Subject: [PATCH] Fixing #59. --- .../readerforselfossv2/model/SelfossModel.kt | 12 ++ .../repository/RepositoryImpl.kt | 82 +++++++----- .../readerforselfossv2/rest/SelfossApi.kt | 123 ++++++++++-------- 3 files changed, 130 insertions(+), 87 deletions(-) diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/SelfossModel.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/SelfossModel.kt index 15decfb..e392786 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/SelfossModel.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/SelfossModel.kt @@ -105,4 +105,16 @@ class SelfossModel { return this } } + + class StatusAndData(val success: Boolean, val data: T? = null) { + companion object { + fun succes(d: T): StatusAndData { + return StatusAndData(true, d) + } + + fun error(): StatusAndData { + return StatusAndData(false) + } + } + } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/repository/RepositoryImpl.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/repository/RepositoryImpl.kt index c970507..db9a789 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/repository/RepositoryImpl.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/repository/RepositoryImpl.kt @@ -47,7 +47,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap suspend fun getNewerItems(): ArrayList { // TODO: Use the updatedSince parameter - var fetchedItems: List? = null + var fetchedItems: SelfossModel.StatusAndData> = SelfossModel.StatusAndData.error() if (isNetworkAvailable()) { fetchedItems = api.getItems( displayedItems.type, @@ -59,23 +59,25 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap ) } else { if (appSettingsService.isItemCachingEnabled()) { - fetchedItems = getDBItems().filter { - displayedItems == ItemType.ALL || - (it.unread && displayedItems == ItemType.UNREAD) || - (it.starred && displayedItems == ItemType.STARRED) - }.map { it.toView() } + fetchedItems = SelfossModel.StatusAndData.succes( + getDBItems().filter { + displayedItems == ItemType.ALL || + (it.unread && displayedItems == ItemType.UNREAD) || + (it.starred && displayedItems == ItemType.STARRED) + }.map { it.toView() } + ) } } - if (fetchedItems != null) { - items = ArrayList(fetchedItems) + if (fetchedItems.success && fetchedItems.data != null) { + items = ArrayList(fetchedItems.data!!) sortItems() } return items } suspend fun getOlderItems(): ArrayList { - var fetchedItems: List? = null + var fetchedItems: SelfossModel.StatusAndData> = SelfossModel.StatusAndData.error() if (isNetworkAvailable()) { val offset = items.size 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. - if (fetchedItems != null) { - items.addAll(fetchedItems) + if (fetchedItems.success && fetchedItems.data != null) { + items.addAll(fetchedItems.data!!) sortItems() } return items } - private suspend fun getMaxItemsForBackground(itemType: ItemType): List? { + private suspend fun getMaxItemsForBackground(itemType: ItemType): List { return if (isNetworkAvailable()) { - api.getItems( + val items = api.getItems( itemType.type, 0, tagFilter?.tag, @@ -106,6 +108,11 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap null, 200 ) + return if (items.success && items.data != null) { + items.data + } else { + emptyList() + } } else { emptyList() } @@ -119,10 +126,10 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap var success = false if (isNetworkAvailable()) { val response = api.stats() - if (response != null) { - badgeUnread = response.unread - badgeAll = response.total - badgeStarred = response.starred + if (response.success && response.data != null) { + badgeUnread = response.data.unread + badgeAll = response.data.total + badgeStarred = response.data.starred success = true } } else { @@ -138,10 +145,10 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap suspend fun getTags(): List? { return if (isNetworkAvailable()) { val apiTags = api.tags() - if (apiTags != null && (appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled())) { - resetDBTagsWithData(apiTags) + if (apiTags.success && apiTags.data != null && (appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled())) { + resetDBTagsWithData(apiTags.data) } - apiTags + apiTags.data } else { getDBTags().map { it.toView() } } @@ -149,7 +156,12 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap suspend fun getSpouts(): Map? { 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 { throw NetworkUnavailableException() } @@ -158,10 +170,10 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap suspend fun getSources(): ArrayList? { return if (isNetworkAvailable()) { val apiSources = api.sources() - if (apiSources != null && (appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled())) { - resetDBSourcesWithData(apiSources) + if (apiSources.success && apiSources.data != null && (appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled())) { + resetDBSourcesWithData(apiSources.data) } - apiSources + apiSources.data } else { 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 { return if (isNetworkAvailable()) { - api.markAsRead(id.toString())?.isSuccess == true + api.markAsRead(id.toString())?.isSuccess } else { insertDBAction(id.toString(), read = true) true @@ -197,7 +209,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap private suspend fun unmarkAsReadById(id: Int): Boolean { return if (isNetworkAvailable()) { - api.unmarkAsRead(id.toString())?.isSuccess == true + api.unmarkAsRead(id.toString())?.isSuccess } else { insertDBAction(id.toString(), unread = true) true @@ -215,7 +227,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap private suspend fun starrById(id: Int): Boolean { return if (isNetworkAvailable()) { - api.starr(id.toString())?.isSuccess == true + api.starr(id.toString())?.isSuccess } else { insertDBAction(id.toString(), starred = true) true @@ -233,7 +245,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap private suspend fun unstarrById(id: Int): Boolean { return if (isNetworkAvailable()) { - api.unstarr(id.toString())?.isSuccess == true + api.unstarr(id.toString())?.isSuccess } else { insertDBAction(id.toString(), starred = true) true @@ -243,7 +255,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap suspend fun markAllAsRead(items: ArrayList): Boolean { 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 for (item in items) { markAsReadLocally(item) @@ -332,7 +344,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap suspend fun updateRemote(): Boolean { return if (isNetworkAvailable()) { - api.update()?.equals("finished") ?: false + api.update()?.equals("finished") } else { false } @@ -365,8 +377,8 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap if (isNetworkAvailable()) { val fetchedVersion = api.version() - if (fetchedVersion != null && fetchedVersion.getApiMajorVersion() != apiMajorVersion) { - appSettingsService.updateApiVersion(fetchedVersion.getApiMajorVersion()) + if (fetchedVersion.success && fetchedVersion.data != null && fetchedVersion.data.getApiMajorVersion() != apiMajorVersion) { + appSettingsService.updateApiVersion(fetchedVersion.data.getApiMajorVersion()) } } } @@ -383,7 +395,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap fun getDBSources(): List = db.sourcesQueries.sources().executeAsList() - fun resetDBTagsWithData(tagEntities: List) { + private fun resetDBTagsWithData(tagEntities: List) { db.tagsQueries.deleteAllTags() db.tagsQueries.transaction { @@ -393,7 +405,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap } } - fun resetDBSourcesWithData(sources: List) { + private fun resetDBSourcesWithData(sources: List) { db.sourcesQueries.deleteAllSources() db.sourcesQueries.transaction { @@ -425,7 +437,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap val newItems = getMaxItemsForBackground(ItemType.UNREAD) val allItems = getMaxItemsForBackground(ItemType.ALL) val starredItems = getMaxItemsForBackground(ItemType.STARRED) - insertDBItems(newItems.orEmpty() + allItems.orEmpty() + starredItems.orEmpty()) + insertDBItems(newItems + allItems + starredItems) return newItems } catch (e: Throwable) { // We do nothing diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossApi.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossApi.kt index c094532..c9f5c9a 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossApi.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossApi.kt @@ -10,6 +10,7 @@ import io.ktor.client.plugins.contentnegotiation.* import io.ktor.client.plugins.logging.* import io.ktor.client.request.* import io.ktor.client.request.forms.* +import io.ktor.client.statement.* import io.ktor.http.* import io.ktor.serialization.kotlinx.json.* import kotlinx.serialization.json.Json @@ -34,7 +35,7 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { appSettingsService.logApiCalls(message) } } - level = LogLevel.ALL + level = LogLevel.INFO } install(HttpTimeout) { requestTimeoutMillis = appSettingsService.getApiTimeout() @@ -65,11 +66,11 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { client = createHttpClient() } - suspend fun login(): SelfossModel.SuccessResponse? = - client.get(url("/login")) { + suspend fun login(): SelfossModel.SuccessResponse = + maybeResponse(client.get(url("/login")) { parameter("username", appSettingsService.getUserName()) parameter("password", appSettingsService.getPassword()) - }.body() + }) suspend fun getItems( type: String, @@ -79,8 +80,8 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { search: String?, updatedSince: String?, items: Int? = null - ): List? = - client.get(url("/items")) { + ): SelfossModel.StatusAndData> = + bodyOrFailure(client.get(url("/items")) { parameter("username", appSettingsService.getUserName()) parameter("password", appSettingsService.getPassword()) parameter("type", type) @@ -90,74 +91,74 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { parameter("updatedsince", updatedSince) parameter("items", items ?: appSettingsService.getItemsNumber()) parameter("offset", offset) - }.body() + }) - suspend fun stats(): SelfossModel.Stats? = - client.get(url("/stats")) { + suspend fun stats(): SelfossModel.StatusAndData = + bodyOrFailure(client.get(url("/stats")) { parameter("username", appSettingsService.getUserName()) parameter("password", appSettingsService.getPassword()) - }.body() + }) - suspend fun tags(): List? = - client.get(url("/tags")) { + suspend fun tags(): SelfossModel.StatusAndData> = + bodyOrFailure(client.get(url("/tags")) { parameter("username", appSettingsService.getUserName()) parameter("password", appSettingsService.getPassword()) - }.body() + }) - suspend fun update(): String? = - client.get(url("/update")) { + suspend fun update(): SelfossModel.StatusAndData = + bodyOrFailure(client.get(url("/update")) { parameter("username", appSettingsService.getUserName()) parameter("password", appSettingsService.getPassword()) - }.body() + }) - suspend fun spouts(): Map? = - client.get(url("/sources/spouts")) { + suspend fun spouts(): SelfossModel.StatusAndData> = + bodyOrFailure(client.get(url("/sources/spouts")) { parameter("username", appSettingsService.getUserName()) parameter("password", appSettingsService.getPassword()) - }.body() + }) - suspend fun sources(): ArrayList? = - client.get(url("/sources/list")) { + suspend fun sources(): SelfossModel.StatusAndData> = + bodyOrFailure(client.get(url("/sources/list")) { parameter("username", appSettingsService.getUserName()) parameter("password", appSettingsService.getPassword()) - }.body() + }) - suspend fun version(): SelfossModel.ApiVersion? = - client.get(url("/api/about")).body() + suspend fun version(): SelfossModel.StatusAndData = + bodyOrFailure(client.get(url("/api/about"))) - suspend fun markAsRead(id: String): SelfossModel.SuccessResponse? = - client.post(url("/mark/$id")) { + suspend fun markAsRead(id: String): SelfossModel.SuccessResponse = + maybeResponse(client.post(url("/mark/$id")) { parameter("username", appSettingsService.getUserName()) parameter("password", appSettingsService.getPassword()) - }.body() + }) - suspend fun unmarkAsRead(id: String): SelfossModel.SuccessResponse? = - client.post(url("/unmark/$id")) { + suspend fun unmarkAsRead(id: String): SelfossModel.SuccessResponse = + maybeResponse(client.post(url("/unmark/$id")) { parameter("username", appSettingsService.getUserName()) parameter("password", appSettingsService.getPassword()) - }.body() + }) - suspend fun starr(id: String): SelfossModel.SuccessResponse? = - client.post(url("/starr/$id")) { + suspend fun starr(id: String): SelfossModel.SuccessResponse = + maybeResponse(client.post(url("/starr/$id")) { parameter("username", appSettingsService.getUserName()) parameter("password", appSettingsService.getPassword()) - }.body() + }) - suspend fun unstarr(id: String): SelfossModel.SuccessResponse? = - client.post(url("/unstarr/$id")) { + suspend fun unstarr(id: String): SelfossModel.SuccessResponse = + maybeResponse(client.post(url("/unstarr/$id")) { parameter("username", appSettingsService.getUserName()) parameter("password", appSettingsService.getPassword()) - }.body() + }) - suspend fun markAllAsRead(ids: List): SelfossModel.SuccessResponse? = - client.submitForm( + suspend fun markAllAsRead(ids: List): SelfossModel.SuccessResponse = + maybeResponse(client.submitForm( url = url("/mark"), formParameters = Parameters.build { append("username", appSettingsService.getUserName()) append("password", appSettingsService.getPassword()) ids.map { append("ids[]", it) } } - ).body() + )) suspend fun createSourceForVersion( title: String, @@ -166,12 +167,14 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { tags: String, filter: String, version: Int - ): SelfossModel.SuccessResponse? = - if (version > 1) { - createSource2(title, url, spout, tags, filter) - } else { - createSource(title, url, spout, tags, filter) - } + ): SelfossModel.SuccessResponse = + maybeResponse( + if (version > 1) { + createSource2(title, url, spout, tags, filter) + } else { + createSource(title, url, spout, tags, filter) + } + ) suspend fun createSource( title: String, @@ -179,7 +182,7 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { spout: String, tags: String, filter: String - ): SelfossModel.SuccessResponse? = + ): HttpResponse = client.submitForm( url = url("/source?username=${appSettingsService.getUserName()}&password=${appSettingsService.getPassword()}"), formParameters = Parameters.build { @@ -189,7 +192,7 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { append("tags", tags) append("filter", filter) } - ).body() + ) suspend fun createSource2( title: String, @@ -197,7 +200,7 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { spout: String, tags: String, filter: String - ): SelfossModel.SuccessResponse? = + ): HttpResponse = client.submitForm( url = url("/source?username=${appSettingsService.getUserName()}&password=${appSettingsService.getPassword()}"), formParameters = Parameters.build { @@ -207,11 +210,27 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { append("tags[]", tags) append("filter", filter) } - ).body() + ) - suspend fun deleteSource(id: Int): SelfossModel.SuccessResponse? = - client.delete(url("/source/$id")) { + suspend fun deleteSource(id: Int): SelfossModel.SuccessResponse = + maybeResponse(client.delete(url("/source/$id")) { parameter("username", appSettingsService.getUserName()) 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 bodyOrFailure(r: HttpResponse): SelfossModel.StatusAndData { + return if (r.status.isSuccess()) { + SelfossModel.StatusAndData.succes(r.body()) + } else { + SelfossModel.StatusAndData.error() + } + } } \ No newline at end of file