From 73f6dbba75dfd4b5b3f55e439d87ac35e0b5a27d Mon Sep 17 00:00:00 2001 From: be-nj Date: Wed, 26 Aug 2026 03:59:25 +0200 Subject: [PATCH] Surface failed refreshes instead of swallowing them Favourites and stream profiles were refreshed inside a bare runCatching, so when they failed nothing was logged and the app just showed a stale cached list. An expired login now always raises the re-login screen, even when cached channels could still be displayed, and the settings gained a "Senderliste neu laden" row for a manual retry. Co-Authored-By: Claude Fable 5 --- app/build.gradle.kts | 4 ++-- app/src/main/java/dev/castarr/tv/AppState.kt | 10 ++++++++-- .../java/dev/castarr/tv/data/DispatcharrRepository.kt | 11 +++++++++-- app/src/main/java/dev/castarr/tv/ui/SettingsScreen.kt | 4 ++++ 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 62b09d3..3af78c9 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -12,8 +12,8 @@ android { applicationId = "dev.castarr.tv" minSdk = 26 targetSdk = 35 - versionCode = 31 - versionName = "0.10.5" + versionCode = 32 + versionName = "0.10.6" } // Release signing from environment (see ~/.keys/castarr-release.env on the diff --git a/app/src/main/java/dev/castarr/tv/AppState.kt b/app/src/main/java/dev/castarr/tv/AppState.kt index 44fcc8d..b2f79c3 100644 --- a/app/src/main/java/dev/castarr/tv/AppState.kt +++ b/app/src/main/java/dev/castarr/tv/AppState.kt @@ -200,8 +200,14 @@ class AppState( result.fold( onSuccess = { appError = AppError.NONE }, onFailure = { throwable -> - // Cached channels keep the app usable; only surface a - // fullscreen state when there is nothing to show. + // An expired login must always surface: cached channels + // would otherwise sit there without EPG or favourites and + // nothing would say why. + if (throwable.message == "not logged in") { + appError = AppError.RELOGIN + return + } + // Otherwise cached channels keep the app usable. if (activeChannels().isNotEmpty()) return appError = when { throwable.message == "not logged in" -> AppError.RELOGIN diff --git a/app/src/main/java/dev/castarr/tv/data/DispatcharrRepository.kt b/app/src/main/java/dev/castarr/tv/data/DispatcharrRepository.kt index bc316eb..02a8278 100644 --- a/app/src/main/java/dev/castarr/tv/data/DispatcharrRepository.kt +++ b/app/src/main/java/dev/castarr/tv/data/DispatcharrRepository.kt @@ -49,13 +49,20 @@ class DispatcharrRepository(context: Context, private val auth: DeviceAuth) { scope.launch { val result = runCatching { status.value = "loading_channels" + Log.i(TAG, "refresh start (loggedIn=${auth.isLoggedIn})") val token = auth.accessToken() ?: error("not logged in") val groups = fetchGroups(token) val list = fetchChannels(token, groups) channels.value = list prefs.edit().putString("channels_cache", Channel.listToJson(list).toString()).apply() - launch { runCatching { refreshFavorites(token) } } - launch { runCatching { refreshProfiles(token) } } + launch { + runCatching { refreshFavorites(token) } + .onFailure { Log.w(TAG, "favorites failed: ${it.javaClass.simpleName}: ${it.message?.take(120)}") } + } + launch { + runCatching { refreshProfiles(token) } + .onFailure { Log.w(TAG, "profiles failed: ${it.javaClass.simpleName}: ${it.message?.take(120)}") } + } launch { runCatching { refreshEpg(token) } .onFailure { Log.w(TAG, "epg failed: ${it.javaClass.simpleName}: ${it.message?.take(160)}") } diff --git a/app/src/main/java/dev/castarr/tv/ui/SettingsScreen.kt b/app/src/main/java/dev/castarr/tv/ui/SettingsScreen.kt index 1c44f7f..ad09592 100644 --- a/app/src/main/java/dev/castarr/tv/ui/SettingsScreen.kt +++ b/app/src/main/java/dev/castarr/tv/ui/SettingsScreen.kt @@ -251,6 +251,10 @@ fun SettingsScreen(state: AppState) { } } } + SettingRow( + "Senderliste neu laden", + subtitle = "Holt Sender, Programm und Favoriten erneut", + ) { state.refreshActive() } SettingRow( "Erweitert", subtitle = "M3U/EPG-Adressen von Hand eintragen",