diff --git a/appcompose/src/main/java/com/readrops/app/compose/account/credentials/AccountCredentialsScreen.kt b/appcompose/src/main/java/com/readrops/app/compose/account/credentials/AccountCredentialsScreen.kt index 18a6c15a..4089c468 100644 --- a/appcompose/src/main/java/com/readrops/app/compose/account/credentials/AccountCredentialsScreen.kt +++ b/appcompose/src/main/java/com/readrops/app/compose/account/credentials/AccountCredentialsScreen.kt @@ -23,6 +23,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction @@ -178,7 +179,7 @@ class AccountCredentialsScreen( ShortSpacer() Text( - text = ErrorMessage.get(exception = state.loginException!!), + text = ErrorMessage.get(state.loginException!!, LocalContext.current), style = MaterialTheme.typography.labelMedium, color = MaterialTheme.colorScheme.error ) diff --git a/appcompose/src/main/java/com/readrops/app/compose/timelime/ErrorListDialog.kt b/appcompose/src/main/java/com/readrops/app/compose/timelime/ErrorListDialog.kt index d3763452..f7606a91 100644 --- a/appcompose/src/main/java/com/readrops/app/compose/timelime/ErrorListDialog.kt +++ b/appcompose/src/main/java/com/readrops/app/compose/timelime/ErrorListDialog.kt @@ -7,6 +7,7 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.stringResource @@ -44,7 +45,7 @@ fun ErrorListDialog( modifier = Modifier.verticalScroll(scrollableState) ) { for (error in errorResult.entries) { - Text(text = "${error.key.name}: ${ErrorMessage.get(error.value)}") + Text(text = "${error.key.name}: ${ErrorMessage.get(error.value, LocalContext.current)}") ShortSpacer() } diff --git a/appcompose/src/main/java/com/readrops/app/compose/timelime/TimelineScreenModel.kt b/appcompose/src/main/java/com/readrops/app/compose/timelime/TimelineScreenModel.kt index 3fd89f21..36ecba6e 100644 --- a/appcompose/src/main/java/com/readrops/app/compose/timelime/TimelineScreenModel.kt +++ b/appcompose/src/main/java/com/readrops/app/compose/timelime/TimelineScreenModel.kt @@ -2,7 +2,6 @@ package com.readrops.app.compose.timelime import android.content.Context import android.content.Intent -import android.util.Log import androidx.compose.runtime.Stable import androidx.paging.Pager import androidx.paging.PagingConfig @@ -32,6 +31,7 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapConcat +import kotlinx.coroutines.flow.transformLatest import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch @@ -68,6 +68,11 @@ class TimelineScreenModel( database.newItemDao().selectAll(query) }, ).flow + .transformLatest { value -> + if (!timelineState.value.isRefreshing) { + emit(value) + } + } .cachedIn(screenModelScope), isAccountLocal = account.isLocal ) @@ -102,11 +107,11 @@ class TimelineScreenModel( } else { _timelineState.update { it.copy(isRefreshing = true) } - repository?.synchronize() try { + repository?.synchronize() } catch (e: Exception) { - // handle sync exceptions - Log.d("TimelineScreenModel", "refreshTimeline: ${e.message}") + _timelineState.update { it.copy(syncError = e, isRefreshing = false) } + return@launch } _timelineState.update { @@ -160,7 +165,7 @@ class TimelineScreenModel( it.copy( isRefreshing = false, endSynchronizing = true, - synchronizationErrors = if (results!!.second.isNotEmpty()) results.second else null + localSyncErrors = if (results!!.second.isNotEmpty()) results.second else null ) } } @@ -287,7 +292,7 @@ class TimelineScreenModel( fun closeDialog(dialog: DialogState? = null) { if (dialog is DialogState.ErrorList) { - _timelineState.update { it.copy(synchronizationErrors = null) } + _timelineState.update { it.copy(localSyncErrors = null) } } _timelineState.update { it.copy(dialog = null) } @@ -329,7 +334,8 @@ data class TimelineState( val feedCount: Int = 0, val feedMax: Int = 0, val endSynchronizing: Boolean = false, - val synchronizationErrors: ErrorResult? = null, + val localSyncErrors: ErrorResult? = null, + val syncError: Exception? = null, val filters: QueryFilters = QueryFilters(), val filterFeedName: String = "", val filterFolderName: String = "", diff --git a/appcompose/src/main/java/com/readrops/app/compose/timelime/TimelineTab.kt b/appcompose/src/main/java/com/readrops/app/compose/timelime/TimelineTab.kt index c04e9f45..1c4b2efc 100644 --- a/appcompose/src/main/java/com/readrops/app/compose/timelime/TimelineTab.kt +++ b/appcompose/src/main/java/com/readrops/app/compose/timelime/TimelineTab.kt @@ -52,6 +52,7 @@ import cafe.adriel.voyager.navigator.tab.TabOptions import com.readrops.app.compose.R import com.readrops.app.compose.item.ItemScreen import com.readrops.app.compose.timelime.drawer.TimelineDrawer +import com.readrops.app.compose.util.ErrorMessage import com.readrops.app.compose.util.components.CenteredProgressIndicator import com.readrops.app.compose.util.components.Placeholder import com.readrops.app.compose.util.components.RefreshScreen @@ -127,25 +128,31 @@ object TimelineTab : Tab { } } - LaunchedEffect(state.synchronizationErrors) { - if (state.synchronizationErrors != null) { + LaunchedEffect(state.localSyncErrors) { + if (state.localSyncErrors != null) { val action = snackbarHostState.showSnackbar( message = context.resources.getQuantityString( R.plurals.error_occurred, - state.synchronizationErrors!!.size + state.localSyncErrors!!.size ), actionLabel = context.getString(R.string.details), duration = SnackbarDuration.Short ) if (action == SnackbarResult.ActionPerformed) { - viewModel.openDialog(DialogState.ErrorList(state.synchronizationErrors!!)) + viewModel.openDialog(DialogState.ErrorList(state.localSyncErrors!!)) } else { // remove errors from state - viewModel.closeDialog(DialogState.ErrorList(state.synchronizationErrors!!)) + viewModel.closeDialog(DialogState.ErrorList(state.localSyncErrors!!)) } } } + + LaunchedEffect(state.syncError) { + if (state.syncError != null) { + snackbarHostState.showSnackbar(ErrorMessage.get(state.syncError!!, context)) + } + } when (val dialog = state.dialog) { is DialogState.ConfirmDialog -> { diff --git a/appcompose/src/main/java/com/readrops/app/compose/util/ErrorMessage.kt b/appcompose/src/main/java/com/readrops/app/compose/util/ErrorMessage.kt index f7ce9503..cee30fd9 100644 --- a/appcompose/src/main/java/com/readrops/app/compose/util/ErrorMessage.kt +++ b/appcompose/src/main/java/com/readrops/app/compose/util/ErrorMessage.kt @@ -1,7 +1,6 @@ package com.readrops.app.compose.util -import androidx.compose.runtime.Composable -import androidx.compose.ui.res.stringResource +import android.content.Context import com.readrops.api.utils.exceptions.HttpException import com.readrops.api.utils.exceptions.ParseException import com.readrops.api.utils.exceptions.UnknownFormatException @@ -11,33 +10,31 @@ import java.net.UnknownHostException object ErrorMessage { - @Composable - fun get(exception: Exception) = when (exception) { - is HttpException -> getHttpMessage(exception) - is UnknownHostException -> stringResource(R.string.unreachable_url) - is NoSuchFileException -> stringResource(R.string.unable_open_file) - is IOException -> stringResource(R.string.network_failure, exception.message.orEmpty()) - is ParseException, is UnknownFormatException -> stringResource(R.string.processing_feed_error) + fun get(exception: Exception, context: Context) = when (exception) { + is HttpException -> getHttpMessage(exception, context) + 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) else -> "${exception.javaClass.simpleName}: ${exception.message}" } - @Composable - private fun getHttpMessage(exception: HttpException): String { + private fun getHttpMessage(exception: HttpException, context: Context): String { return when (exception.code) { in 400..499 -> { when (exception.code) { - 400 -> stringResource(id = R.string.http_error_400) - 401 -> stringResource(id = R.string.http_error_401) - 403 -> stringResource(id = R.string.http_error_403) - 404 -> stringResource(id = R.string.http_error_404) - else -> stringResource(id = R.string.http_error_4XX, 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 -> { - stringResource(id = R.string.http_error_5XX, exception.code) + context.resources.getString(R.string.http_error_5XX, exception.code) } - else -> stringResource(id = R.string.http_error, exception.code) + else -> context.resources.getString(R.string.http_error, exception.code) } } } \ No newline at end of file diff --git a/appcompose/src/main/java/com/readrops/app/compose/util/components/ErrorDialog.kt b/appcompose/src/main/java/com/readrops/app/compose/util/components/ErrorDialog.kt index 53737e75..7bd3e770 100644 --- a/appcompose/src/main/java/com/readrops/app/compose/util/components/ErrorDialog.kt +++ b/appcompose/src/main/java/com/readrops/app/compose/util/components/ErrorDialog.kt @@ -2,6 +2,7 @@ package com.readrops.app.compose.util.components import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import com.readrops.app.compose.R @@ -17,7 +18,7 @@ fun ErrorDialog( icon = painterResource(id = R.drawable.ic_error), onDismiss = onDismiss ) { - Text(text = ErrorMessage.get(exception = exception)) + Text(text = ErrorMessage.get(exception, LocalContext.current)) } } diff --git a/appcompose/src/main/res/values-fr/strings.xml b/appcompose/src/main/res/values-fr/strings.xml index c5e6eea8..31f51614 100644 --- a/appcompose/src/main/res/values-fr/strings.xml +++ b/appcompose/src/main/res/values-fr/strings.xml @@ -161,7 +161,7 @@ Erreur HTTP 401, veuillez vérifier vos identifiants Erreur HTTP 403, accès interdit Erreur HTTP 404, l\'URL n\'existe pas - Erreur HTTP %1$s, veuillez vérifier vos champs - Erreur HTTP %1$s, erreur serveur - Erreur HTTP %1$s + Erreur HTTP %1$d, veuillez vérifier vos champs + Erreur HTTP %1$d, erreur serveur + Erreur HTTP %1$d \ No newline at end of file diff --git a/appcompose/src/main/res/values/strings.xml b/appcompose/src/main/res/values/strings.xml index 03a6eff1..cf90ecb2 100644 --- a/appcompose/src/main/res/values/strings.xml +++ b/appcompose/src/main/res/values/strings.xml @@ -167,7 +167,7 @@ HTTP error 401, please check your credentials HTTP error 403, access forbidden HTTP error 404, URL not found - HTTP error %1$s, please check your fields - HTTP error %1$s, server error - HTTP error %1$s + HTTP error %1$d, please check your fields + HTTP error %1$d, server error + HTTP error %1$d \ No newline at end of file