Fix crash when changing seasons & improve error page (#94)

Related to #64 

If an error occurred while querying for a season's episodes, the app
would crash instead of display the error message.

This PR fixes the app crash, but not the underlying query issue. It also
changes the error page to show a button for sending logs to the server
immediately.
This commit is contained in:
damontecres 2025-10-28 17:04:15 -04:00 committed by GitHub
parent 50b0b0a508
commit 3ab9eafc7c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 88 additions and 42 deletions

View file

@ -1,14 +1,39 @@
package com.github.damontecres.wholphin.ui.components
import android.content.Context
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.ViewModel
import androidx.tv.material3.Button
import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text
import com.github.damontecres.wholphin.ui.detail.DebugViewModel.Companion.sendAppLogs
import com.github.damontecres.wholphin.util.LoadingState
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import org.jellyfin.sdk.api.client.ApiClient
import org.jellyfin.sdk.model.ClientInfo
import org.jellyfin.sdk.model.DeviceInfo
import javax.inject.Inject
@HiltViewModel
class ErrorViewModel
@Inject
constructor(
@param:ApplicationContext private val context: Context,
private val api: ApiClient,
private val clientInfo: ClientInfo,
private val deviceInfo: DeviceInfo,
) : ViewModel() {
fun sendLogs() {
sendAppLogs(context, api, clientInfo, deviceInfo)
}
}
/**
* Displays an error message and/or exception
@ -18,40 +43,48 @@ fun ErrorMessage(
message: String?,
exception: Throwable?,
modifier: Modifier = Modifier,
viewModel: ErrorViewModel = hiltViewModel(),
) {
LazyColumn(
contentPadding = PaddingValues(16.dp),
Column(
verticalArrangement = Arrangement.spacedBy(16.dp),
modifier = modifier,
modifier = modifier.padding(16.dp),
) {
Text(
text = "An error occurred! Press the button to send logs to your server.",
color = MaterialTheme.colorScheme.error,
style = MaterialTheme.typography.titleMedium,
)
Button(
onClick = {
viewModel.sendLogs()
},
) {
Text(
text = "Send Logs",
)
}
message?.let {
item {
Text(
text = it,
color = MaterialTheme.colorScheme.error,
style = MaterialTheme.typography.titleLarge,
)
}
Text(
text = it,
color = MaterialTheme.colorScheme.error,
style = MaterialTheme.typography.titleLarge,
)
}
exception?.localizedMessage?.let {
item {
Text(
text = it,
color = MaterialTheme.colorScheme.error,
style = MaterialTheme.typography.titleMedium,
)
}
Text(
text = it,
color = MaterialTheme.colorScheme.error,
style = MaterialTheme.typography.titleMedium,
)
}
var cause = exception?.cause
while (cause != null) {
cause.localizedMessage?.let {
item {
Text(
text = "Caused by: $it",
color = MaterialTheme.colorScheme.error,
style = MaterialTheme.typography.titleSmall,
)
}
Text(
text = "Caused by: $it",
color = MaterialTheme.colorScheme.error,
style = MaterialTheme.typography.titleSmall,
)
}
cause = cause.cause
}

View file

@ -1,5 +1,6 @@
package com.github.damontecres.wholphin.ui.detail
import android.content.Context
import android.util.Log
import androidx.compose.foundation.background
import androidx.compose.foundation.focusable
@ -34,11 +35,16 @@ import com.github.damontecres.wholphin.data.ServerRepository
import com.github.damontecres.wholphin.data.model.ItemPlayback
import com.github.damontecres.wholphin.preferences.UserPreferences
import com.github.damontecres.wholphin.ui.launchIO
import com.github.damontecres.wholphin.ui.showToast
import com.github.damontecres.wholphin.util.ExceptionHandler
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.jellyfin.sdk.api.client.ApiClient
import org.jellyfin.sdk.api.client.extensions.clientLogApi
import org.jellyfin.sdk.model.ClientInfo
import org.jellyfin.sdk.model.DeviceInfo
import java.io.BufferedReader
import java.io.InputStreamReader
import javax.inject.Inject
@ -110,6 +116,26 @@ class DebugViewModel
}
return logLines
}
fun ViewModel.sendAppLogs(
context: Context,
api: ApiClient,
clientInfo: ClientInfo?,
deviceInfo: DeviceInfo?,
) {
viewModelScope.launchIO(ExceptionHandler(true)) {
val logcat = getLogCatLines().joinToString("\n") { it.text }
val body =
"""
Send App Logs
clientInfo=$clientInfo
deviceInfo=$deviceInfo
""".trimIndent() + logcat
val response by api.clientLogApi.logFile(body)
showToast(context, "Sent! Filename=${response.fileName}")
}
}
}
}

View file

@ -215,7 +215,7 @@ class SeriesViewModel
)
val pager = ApiRequestPager(api, request, GetEpisodesRequestHandler, viewModelScope)
pager.init()
Timber.Forest.v("Loaded ${pager.size} episodes for season $season")
Timber.v("Loaded ${pager.size} episodes for season $season")
return EpisodeList.Success(convertPager(pager))
}

View file

@ -12,18 +12,16 @@ import com.github.damontecres.wholphin.data.isPinned
import com.github.damontecres.wholphin.data.model.NavDrawerPinnedItem
import com.github.damontecres.wholphin.data.model.NavPinType
import com.github.damontecres.wholphin.preferences.AppPreferences
import com.github.damontecres.wholphin.ui.detail.DebugViewModel
import com.github.damontecres.wholphin.ui.detail.DebugViewModel.Companion.sendAppLogs
import com.github.damontecres.wholphin.ui.launchIO
import com.github.damontecres.wholphin.ui.nav.NavDrawerItem
import com.github.damontecres.wholphin.ui.nav.NavigationManager
import com.github.damontecres.wholphin.ui.setValueOnMain
import com.github.damontecres.wholphin.ui.showToast
import com.github.damontecres.wholphin.util.ExceptionHandler
import com.github.damontecres.wholphin.util.RememberTabManager
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import org.jellyfin.sdk.api.client.ApiClient
import org.jellyfin.sdk.api.client.extensions.clientLogApi
import org.jellyfin.sdk.model.ClientInfo
import org.jellyfin.sdk.model.DeviceInfo
import javax.inject.Inject
@ -91,17 +89,6 @@ class PreferencesViewModel
}
fun sendAppLogs() {
viewModelScope.launchIO(ExceptionHandler(true)) {
val logcat = DebugViewModel.getLogCatLines().joinToString("\n") { it.text }
val body =
"""
Send App Logs
$clientInfo
$deviceInfo
""".trimIndent() + logcat
val response by api.clientLogApi.logFile(body)
showToast(context, "Sent! Filename=${response.fileName}")
}
sendAppLogs(context, api, clientInfo, deviceInfo)
}
}