Compare commits
2
Commits
6e5c9fce95
...
85da03360d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
85da03360d | ||
|
|
3a01b42aeb |
No files matched your search
@@ -10,6 +10,7 @@ import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
@@ -48,6 +49,7 @@ import com.example.myapplication.screens.SearchTopBar
|
||||
import com.example.myapplication.screens.SettingsScreen
|
||||
import com.example.myapplication.screens.SettingsTopBar
|
||||
import com.example.myapplication.screens.VideoPlayerScreen
|
||||
import com.example.myapplication.ui.design.LumaErrorState
|
||||
import com.example.myapplication.ui.design.LumaLoading
|
||||
import com.example.myapplication.ui.design.LumaColors
|
||||
import com.example.myapplication.ui.design.rememberLumaLayerBackdrop
|
||||
@@ -370,9 +372,12 @@ private fun MediaDetailDestination(
|
||||
) {
|
||||
var item by remember(server?.id, itemId) { mutableStateOf<EmbyItem?>(null) }
|
||||
var loading by remember(server?.id, itemId) { mutableStateOf(true) }
|
||||
var loadError by remember(server?.id, itemId) { mutableStateOf(false) }
|
||||
var retryToken by remember(server?.id, itemId) { mutableIntStateOf(0) }
|
||||
|
||||
LaunchedEffect(server?.id, itemId) {
|
||||
LaunchedEffect(server?.id, itemId, retryToken) {
|
||||
loading = true
|
||||
loadError = false
|
||||
item = try {
|
||||
if (server == null || server.apiKey.isBlank() || itemId.isBlank()) {
|
||||
null
|
||||
@@ -382,6 +387,7 @@ private fun MediaDetailDestination(
|
||||
} catch (error: CancellationException) {
|
||||
throw error
|
||||
} catch (_: Exception) {
|
||||
loadError = true
|
||||
null
|
||||
}
|
||||
loading = false
|
||||
@@ -390,6 +396,12 @@ private fun MediaDetailDestination(
|
||||
when {
|
||||
item != null -> MediaDetailScreen(item = item!!, server = server, servers = servers, onClose = onClose, onPlay = onPlay)
|
||||
loading -> DestinationLoading()
|
||||
loadError -> LumaErrorState(
|
||||
title = stringResource(Res.string.detail_load_failed),
|
||||
description = stringResource(Res.string.error_content_sub),
|
||||
retryLabel = stringResource(Res.string.retry),
|
||||
onRetry = { retryToken++ },
|
||||
)
|
||||
else -> DestinationMessage(stringResource(Res.string.detail_load_failed), background = Color.Transparent)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -444,6 +444,7 @@ private fun CalendarTab(onEpisodeClick: (String, String) -> Unit = { _, _ -> })
|
||||
var loading by remember { mutableStateOf(true) }
|
||||
var selectedDate by remember { mutableStateOf<java.time.LocalDate?>(null) }
|
||||
var weekOffset by remember { mutableIntStateOf(0) }
|
||||
var calendarError by remember { mutableStateOf(false) }
|
||||
|
||||
// P1: Key on watchlist size so calendar re-fetches when watchlist changes
|
||||
var watchlistVersion by remember { mutableIntStateOf(0) }
|
||||
@@ -455,9 +456,13 @@ private fun CalendarTab(onEpisodeClick: (String, String) -> Unit = { _, _ -> })
|
||||
loading = false
|
||||
return@LaunchedEffect
|
||||
}
|
||||
calendarError = false
|
||||
calendarEpisodes = try {
|
||||
TmdbRepository.getCalendarEpisodes(watchlist)
|
||||
} catch (_: Exception) { emptyList() }
|
||||
} catch (_: Exception) {
|
||||
calendarError = true
|
||||
emptyList()
|
||||
}
|
||||
loading = false
|
||||
}
|
||||
|
||||
@@ -495,8 +500,14 @@ private fun CalendarTab(onEpisodeClick: (String, String) -> Unit = { _, _ -> })
|
||||
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
LumaLoading(label = stringResource(Res.string.loading_search))
|
||||
}
|
||||
} else if (calendarError) {
|
||||
LumaEmptyStateContainer(
|
||||
icon = LumaIcon.Calendar,
|
||||
title = stringResource(Res.string.error_content_title),
|
||||
description = stringResource(Res.string.empty_calendar_sub),
|
||||
verticalOffset = (-60).dp,
|
||||
)
|
||||
} else {
|
||||
// P2: Remember filtered + grouped results to avoid recomputation on unrelated state changes
|
||||
val filteredEpisodes = remember(calendarEpisodes, selectedDate, today) {
|
||||
filterCalendarEpisodes(calendarEpisodes, selectedDate, today)
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
@@ -68,8 +69,9 @@ fun SearchAllScreen(
|
||||
var results by remember(serverId, initialQuery) { mutableStateOf<List<EmbyItem>>(emptyList()) }
|
||||
var loading by remember(serverId, initialQuery) { mutableStateOf(true) }
|
||||
var error by remember(serverId, initialQuery) { mutableStateOf(false) }
|
||||
var retryToken by remember(serverId, initialQuery) { mutableIntStateOf(0) }
|
||||
|
||||
androidx.compose.runtime.LaunchedEffect(serverId, initialQuery) {
|
||||
androidx.compose.runtime.LaunchedEffect(serverId, initialQuery, retryToken) {
|
||||
val term = initialQuery.trim()
|
||||
if (server == null || server.apiKey.isBlank() || term.isBlank()) {
|
||||
results = emptyList()
|
||||
@@ -184,7 +186,7 @@ fun SearchAllScreen(
|
||||
title = stringResource(Res.string.error_content_title),
|
||||
description = stringResource(Res.string.error_content_sub),
|
||||
retryLabel = stringResource(Res.string.retry),
|
||||
onRetry = { loading = true },
|
||||
onRetry = { retryToken++ },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -587,11 +587,28 @@ fun VideoPlayerScreen(
|
||||
|
||||
if (playerError != null) {
|
||||
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
Text(
|
||||
text = playerError ?: stringResource(Res.string.player_failed),
|
||||
color = Color.White,
|
||||
modifier = Modifier.padding(24.dp),
|
||||
)
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Text(
|
||||
text = playerError ?: stringResource(Res.string.player_failed),
|
||||
color = Color.White,
|
||||
modifier = Modifier.padding(24.dp),
|
||||
)
|
||||
androidx.compose.material3.TextButton(
|
||||
onClick = {
|
||||
playerError = null
|
||||
playbackUrl?.let { url ->
|
||||
player.setMediaItem(androidx.media3.common.MediaItem.fromUri(url))
|
||||
player.prepare()
|
||||
player.play()
|
||||
}
|
||||
},
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(Res.string.retry),
|
||||
color = LumaColors.accent,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user