Improve error handling in NewFeedScreen

This commit is contained in:
Shinokuni 2024-11-12 17:15:59 +01:00
parent 99f068183e
commit 903f6c5427
7 changed files with 217 additions and 26 deletions

View File

@ -24,7 +24,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
@ -34,7 +33,6 @@ import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import com.readrops.app.R
import com.readrops.app.account.selection.adaptiveIconPainterResource
import com.readrops.app.util.ErrorMessage
import com.readrops.app.util.components.AndroidScreen
import com.readrops.app.util.components.DropdownBox
import com.readrops.app.util.components.DropdownBoxValue
@ -208,11 +206,11 @@ class NewFeedScreen(val url: String? = null) : AndroidScreen() {
}
}
if (state.exception != null) {
if (state.error != null) {
MediumSpacer()
Text(
text = ErrorMessage.get(state.exception!!, LocalContext.current),
text = state.error!!,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.error,
textAlign = TextAlign.Center,

View File

@ -11,7 +11,7 @@ import com.readrops.api.utils.AuthInterceptor
import com.readrops.api.utils.HtmlParser
import com.readrops.app.R
import com.readrops.app.repositories.BaseRepository
import com.readrops.app.util.ErrorMessage
import com.readrops.app.util.accounterror.AccountError
import com.readrops.app.util.components.TextFieldError
import com.readrops.db.Database
import com.readrops.db.entities.Feed
@ -27,7 +27,6 @@ import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent
import org.koin.core.component.get
import org.koin.core.parameter.parametersOf
import java.net.UnknownHostException
class NewFeedScreenModel(
private val database: Database,
@ -39,6 +38,8 @@ class NewFeedScreenModel(
private val selectedAccountState = MutableStateFlow(state.value.selectedAccount)
private lateinit var accountError: AccountError
init {
screenModelScope.launch(dispatcher) {
database.accountDao()
@ -47,6 +48,8 @@ class NewFeedScreenModel(
.collect { accounts ->
val selectedAccount = accounts.find { it.isCurrentAccount }
?: accounts.first()
accountError = AccountError.from(selectedAccount, context)
selectedAccountState.update { selectedAccount }
mutableState.update { newFeedState ->
@ -90,7 +93,7 @@ class NewFeedScreenModel(
if (state.value.selectedResultsCount > 0) {
mutableState.update {
it.copy(
exception = null,
error = null,
isLoading = true,
parsingResults = state.value.parsingResults.map { parsingResult ->
parsingResult.copy(error = null)
@ -132,7 +135,7 @@ class NewFeedScreenModel(
private fun loadFeeds() {
screenModelScope.launch(dispatcher) {
mutableState.update { it.copy(exception = null, isLoading = true) }
mutableState.update { it.copy(error = null, isLoading = true) }
val url = state.value.url
try {
@ -185,21 +188,11 @@ class NewFeedScreenModel(
}
}
} catch (e: Exception) {
// TODO improve error handling for all accounts
when (e) {
is UnknownHostException -> mutableState.update {
it.copy(
urlError = TextFieldError.UnreachableUrl,
isLoading = false
)
}
else -> mutableState.update {
it.copy(
urlError = TextFieldError.NoRSSFeed,
isLoading = false
)
}
mutableState.update {
it.copy(
error = accountError.newFeedMessage(e),
isLoading = false
)
}
}
}
@ -235,7 +228,7 @@ class NewFeedScreenModel(
if (feed != null) {
val error = errors[feed]
parsingResult.copy(error = ErrorMessage.get(error!!, context))
parsingResult.copy(error = accountError.newFeedMessage(error!!))
} else {
parsingResult
}
@ -250,7 +243,7 @@ class NewFeedScreenModel(
} else {
mutableState.update {
it.copy(
exception = errors.values.first(),
error = accountError.newFeedMessage(errors.values.first()),
isLoading = false
)
}
@ -337,7 +330,7 @@ data class State(
val isAccountDropdownExpanded: Boolean = false,
val isFoldersDropdownExpanded: Boolean = false,
val urlError: TextFieldError? = null,
val exception: Exception? = null,
val error: String? = null,
val isLoading: Boolean = false,
val popScreen: Boolean = false,
val parsingResults: List<ParsingResultState> = listOf()

View File

@ -0,0 +1,73 @@
package com.readrops.app.util.accounterror
import android.content.Context
import com.readrops.api.utils.exceptions.HttpException
import com.readrops.api.utils.exceptions.LoginFailedException
import com.readrops.api.utils.exceptions.ParseException
import com.readrops.api.utils.exceptions.UnknownFormatException
import com.readrops.app.R
import com.readrops.db.entities.account.Account
import com.readrops.db.entities.account.AccountType
import java.io.IOException
import java.net.UnknownHostException
abstract class AccountError(protected val context: Context) {
open fun newFeedMessage(exception: Exception): String = genericMessage(exception)
open fun updateFeedMessage(exception: Exception): String = genericMessage(exception)
open fun deleteFeedMessage(exception: Exception): String = genericMessage(exception)
open fun newFolderMessage(exception: Exception): String = genericMessage(exception)
open fun updateFolderMessage(exception: Exception): String = genericMessage(exception)
open fun deleteFolderMessage(exception: Exception): String = genericMessage(exception)
protected fun genericMessage(exception: Exception) = when (exception) {
is HttpException -> httpMessage(exception)
is UnknownHostException -> context.resources.getString(R.string.unreachable_url)
is NoSuchFileException -> context.resources.getString(R.string.unable_open_file)
is IOException -> context.resources.getString(
R.string.network_failure,
exception.message.orEmpty()
)
is ParseException, is UnknownFormatException -> context.resources.getString(R.string.processing_feed_error)
is LoginFailedException -> context.getString(R.string.login_failed)
else -> "${exception.javaClass.simpleName}: ${exception.message}"
}
protected fun httpMessage(exception: HttpException): String {
return when (exception.code) {
in 400..499 -> {
when (exception.code) {
400 -> context.resources.getString(R.string.http_error_400)
401 -> context.resources.getString(R.string.http_error_401)
403 -> context.resources.getString(R.string.http_error_403)
404 -> context.resources.getString(R.string.http_error_404)
else -> context.resources.getString(R.string.http_error_4XX, exception.code)
}
}
in 500..599 -> {
context.resources.getString(R.string.http_error_5XX, exception.code)
}
else -> context.resources.getString(R.string.http_error, exception.code)
}
}
companion object {
fun from(account: Account, context: Context): AccountError = when (account.type) {
AccountType.FRESHRSS -> FreshRSSError(context)
AccountType.NEXTCLOUD_NEWS -> NextcloudNewsError(context)
else -> DefaultAccountError(context)
}
class DefaultAccountError(context: Context) : AccountError(context)
}
}

View File

@ -0,0 +1,57 @@
package com.readrops.app.util.accounterror
import android.content.Context
import com.readrops.api.utils.exceptions.HttpException
import com.readrops.app.R
class FreshRSSError(context: Context) : AccountError(context) {
override fun newFeedMessage(exception: Exception): String = when (exception) {
is HttpException -> {
when (exception.code) {
400 -> context.getString(R.string.feed_already_exists)
else -> httpMessage(exception)
}
}
else -> genericMessage(exception)
}
override fun updateFeedMessage(exception: Exception): String {
return newFeedMessage(exception)
}
override fun deleteFeedMessage(exception: Exception): String = when (exception) {
is HttpException -> {
when (exception.code) {
400 -> context.resources.getString(R.string.feed_doesnt_exist)
else -> httpMessage(exception)
}
}
else -> genericMessage(exception)
}
override fun newFolderMessage(exception: Exception): String = when (exception) {
is HttpException -> {
when (exception.code) {
400 -> context.resources.getString(R.string.folder_already_exists)
else -> httpMessage(exception)
}
}
else -> genericMessage(exception)
}
override fun updateFolderMessage(exception: Exception): String {
return newFolderMessage(exception)
}
override fun deleteFolderMessage(exception: Exception): String = when (exception) {
is HttpException -> {
when (exception.code) {
400 -> context.resources.getString(R.string.folder_doesnt_exist)
else -> httpMessage(exception)
}
}
else -> genericMessage(exception)
}
}

View File

@ -0,0 +1,64 @@
package com.readrops.app.util.accounterror
import android.content.Context
import com.readrops.api.utils.exceptions.HttpException
import com.readrops.app.R
class NextcloudNewsError(context: Context) : AccountError(context) {
override fun newFeedMessage(exception: Exception): String = when (exception) {
is HttpException -> {
when (exception.code) {
409 -> context.resources.getString(R.string.feed_already_exists)
422 -> context.getString(R.string.invalid_feed)
else -> httpMessage(exception)
}
}
else -> genericMessage(exception)
}
override fun updateFeedMessage(exception: Exception): String = when (exception) {
is HttpException -> {
when (exception.code) {
404 -> context.resources.getString(R.string.feed_doesnt_exist)
else -> httpMessage(exception)
}
}
else -> genericMessage(exception)
}
override fun deleteFeedMessage(exception: Exception): String {
return updateFeedMessage(exception)
}
override fun newFolderMessage(exception: Exception): String = when (exception) {
is HttpException -> {
when (exception.code) {
409 -> context.resources.getString(R.string.folder_already_exists)
422 -> context.resources.getString(R.string.invalid_folder)
else -> httpMessage(exception)
}
}
else -> genericMessage(exception)
}
override fun updateFolderMessage(exception: Exception): String = when (exception) {
is HttpException -> {
when (exception.code) {
404 -> context.resources.getString(R.string.folder_doesnt_exist)
409 -> context.resources.getString(R.string.folder_already_exists)
422 -> context.getString(R.string.invalid_folder)
else -> httpMessage(exception)
}
}
else -> genericMessage(exception)
}
override fun deleteFolderMessage(exception: Exception): String {
return updateFolderMessage(exception)
}
}

View File

@ -182,4 +182,7 @@
<string name="selected">(%1$s sélectionnés)</string>
<string name="add_selected_feeds">Ajouter %1$s flux sélectionnés</string>
<string name="enter_url">Entrer une URL</string>
<string name="feed_already_exists">Le flux existe déjà</string>
<string name="invalid_folder">Dossier invalide</string>
<string name="invalid_feed">Flux invalide</string>
</resources>

View File

@ -191,4 +191,7 @@
<string name="selected">(%1$s selected)</string>
<string name="add_selected_feeds">Add %1$s selected feeds</string>
<string name="enter_url">Enter an URL</string>
<string name="feed_already_exists">Feed already exists</string>
<string name="invalid_folder">Invalid folder</string>
<string name="invalid_feed">Invalid feed</string>
</resources>