From a14a659876ed1d2df09b669ecec1857d96f0e81e Mon Sep 17 00:00:00 2001 From: Eugene Date: Wed, 19 Aug 2026 19:37:13 +0300 Subject: [PATCH] update error content --- .../shared/presentation/list/VideoScreen.kt | 4 +- .../presentation/overview/OverviewScreen.kt | 4 +- .../overview/episode/EpisodeOverviewScreen.kt | 4 +- .../overview/season/SeasonOverviewScreen.kt | 4 +- .../presentation/player/PlayerScreen.kt | 8 +-- .../presentation/search/SearchScreen.kt | 8 +-- .../chat/presentation/ChatScreen.kt | 14 +---- .../updater/presentation/UpdateDialog.kt | 59 +++++++++++++++---- .../videoplayer/ui/VideoPlayer.kt | 2 +- .../composeResources/values/strings.xml | 1 + .../shadowsparky/ui/components/ErrorInfo.kt | 13 ++-- 11 files changed, 71 insertions(+), 50 deletions(-) diff --git a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/list/VideoScreen.kt b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/list/VideoScreen.kt index 36b31e6..76c8ef9 100644 --- a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/list/VideoScreen.kt +++ b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/list/VideoScreen.kt @@ -3,7 +3,6 @@ package ru.shadowsparky.vbox.shared.presentation.list import androiddev.apps.vbox.client.shared.generated.resources.Res import androiddev.apps.vbox.client.shared.generated.resources.not_found import androiddev.apps.vbox.client.shared.generated.resources.recommendations -import androiddev.apps.vbox.client.shared.generated.resources.retry import androidx.compose.animation.AnimatedContent import androidx.compose.foundation.Image import androidx.compose.foundation.background @@ -102,8 +101,7 @@ fun VideoScreenContent( if (error != null) { ErrorContent( error, - modifier = Modifier.fillMaxSize(), - buttonText = stringResource(Res.string.retry) + modifier = Modifier.fillMaxSize() ) { onRefresh() } } else if (items != null) { AnimatedContent( diff --git a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/overview/OverviewScreen.kt b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/overview/OverviewScreen.kt index b757bf1..40244f9 100644 --- a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/overview/OverviewScreen.kt +++ b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/overview/OverviewScreen.kt @@ -8,7 +8,6 @@ import androiddev.apps.vbox.client.shared.generated.resources.details_serial import androiddev.apps.vbox.client.shared.generated.resources.more import androiddev.apps.vbox.client.shared.generated.resources.offline_remove import androiddev.apps.vbox.client.shared.generated.resources.offline_save -import androiddev.apps.vbox.client.shared.generated.resources.retry import androiddev.apps.vbox.client.shared.generated.resources.share import androiddev.apps.vbox.client.shared.generated.resources.star import androiddev.apps.vbox.client.shared.generated.resources.star_unchecked @@ -98,8 +97,7 @@ fun OverviewScreen(component: OverviewComponent) { errorState != null -> ErrorContent( errorState!!, - modifier = Modifier.fillMaxSize(), - buttonText = stringResource(Res.string.retry) + modifier = Modifier.fillMaxSize() ) { component.loadDetailsIfNeeded() } details != null -> DetailsInfo( diff --git a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/overview/episode/EpisodeOverviewScreen.kt b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/overview/episode/EpisodeOverviewScreen.kt index 455ccb5..4f106f7 100644 --- a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/overview/episode/EpisodeOverviewScreen.kt +++ b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/overview/episode/EpisodeOverviewScreen.kt @@ -7,7 +7,6 @@ import androiddev.apps.vbox.client.shared.generated.resources.episodes_overview import androiddev.apps.vbox.client.shared.generated.resources.eye import androiddev.apps.vbox.client.shared.generated.resources.folder_check import androiddev.apps.vbox.client.shared.generated.resources.pending -import androiddev.apps.vbox.client.shared.generated.resources.retry import androiddev.apps.vbox.client.shared.generated.resources.save_as import androiddev.apps.vbox.client.shared.generated.resources.select_all import androidx.compose.foundation.ExperimentalFoundationApi @@ -174,8 +173,7 @@ fun EpisodesOverviewList( if (errorCode != null) { ErrorContent( errorCode, - modifier = Modifier.fillMaxSize(), - buttonText = stringResource(Res.string.retry) + modifier = Modifier.fillMaxSize() ) { loadLinks() } } else if (episodes != null) { val lazyListState = rememberLazyListState() diff --git a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/overview/season/SeasonOverviewScreen.kt b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/overview/season/SeasonOverviewScreen.kt index f52888f..2afc179 100644 --- a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/overview/season/SeasonOverviewScreen.kt +++ b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/overview/season/SeasonOverviewScreen.kt @@ -1,7 +1,6 @@ package ru.shadowsparky.vbox.shared.presentation.overview.season import androiddev.apps.vbox.client.shared.generated.resources.Res -import androiddev.apps.vbox.client.shared.generated.resources.retry import androiddev.apps.vbox.client.shared.generated.resources.seasons_overview import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.PaddingValues @@ -38,8 +37,7 @@ fun SeasonOverviewScreen(component: SeasonOverviewComponent) { if (err.value != null) { ErrorContent( err.value!!, - modifier = Modifier.fillMaxSize(), - buttonText = stringResource(Res.string.retry) + modifier = Modifier.fillMaxSize() ) { component.load() } } else if (seasons.value != null) { SeasonsInfo( diff --git a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/player/PlayerScreen.kt b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/player/PlayerScreen.kt index d03b3a8..3dec711 100644 --- a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/player/PlayerScreen.kt +++ b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/player/PlayerScreen.kt @@ -2,13 +2,13 @@ package ru.shadowsparky.vbox.shared.presentation.player import androiddev.apps.vbox.client.shared.generated.resources.Res import androiddev.apps.vbox.client.shared.generated.resources.next_episode -import androiddev.apps.vbox.client.shared.generated.resources.retry import androiddev.apps.vbox.client.shared.generated.resources.start_from import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.animation.slideInHorizontally import androidx.compose.animation.slideOutHorizontally +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding @@ -24,6 +24,7 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha +import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import org.jetbrains.compose.resources.stringResource @@ -49,14 +50,13 @@ fun PlayerScreen(component: PlayerComponent) { is PlayerEvent.Error -> { ErrorContent( state.message, - modifier = Modifier.fillMaxSize().padding(it), - buttonText = stringResource(Res.string.retry), + modifier = Modifier.fillMaxSize().padding(it) ) { component.load() } } is PlayerEvent.Ended -> component.requestNextEpisode() is PlayerEvent.Playing -> { - Box(modifier = Modifier.fillMaxSize()) { + Box(modifier = Modifier.fillMaxSize().background(Color.Black)) { VideoPlayer( component.playerController, Modifier.fillMaxSize(), diff --git a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/search/SearchScreen.kt b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/search/SearchScreen.kt index 7068f0a..4d7ce91 100644 --- a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/search/SearchScreen.kt +++ b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/search/SearchScreen.kt @@ -282,15 +282,15 @@ fun AppBarWithSearch( navigationIcon = {}, inputField = { Surface( - modifier = Modifier - .fillMaxWidth() - .clickable(enabled = !isDefault) { onEdit() }, + modifier = Modifier.fillMaxWidth(), shape = RoundedCornerShape(12.dp), color = Color.Transparent ) { Row(modifier = Modifier, verticalAlignment = Alignment.CenterVertically) { BackButton { onBack() } - Text(text = title, maxLines = 1) + Box(modifier = Modifier.weight(1f).clickable(enabled = !isDefault) { onEdit() }) { + Text(text = title, maxLines = 1) + } } } } diff --git a/feature/chat/chat-client/src/commonMain/kotlin/ru/shadowsparky/chat/presentation/ChatScreen.kt b/feature/chat/chat-client/src/commonMain/kotlin/ru/shadowsparky/chat/presentation/ChatScreen.kt index a48d967..704df6f 100644 --- a/feature/chat/chat-client/src/commonMain/kotlin/ru/shadowsparky/chat/presentation/ChatScreen.kt +++ b/feature/chat/chat-client/src/commonMain/kotlin/ru/shadowsparky/chat/presentation/ChatScreen.kt @@ -2,9 +2,7 @@ package ru.shadowsparky.chat.presentation import androiddev.feature.chat.chat_client.generated.resources.Res import androiddev.feature.chat.chat_client.generated.resources.chat_input_placeholder -import androiddev.feature.chat.chat_client.generated.resources.chat_load_error import androiddev.feature.chat.chat_client.generated.resources.chat_ok -import androiddev.feature.chat.chat_client.generated.resources.chat_retry_button import androiddev.feature.chat.chat_client.generated.resources.send import androidx.compose.foundation.focusable import androidx.compose.foundation.gestures.animateScrollBy @@ -79,6 +77,7 @@ import ru.shadowsparky.chat.domain.ChatRoles import ru.shadowsparky.chat.domain.Message import ru.shadowsparky.chat.domain.TextToken import ru.shadowsparky.ui.components.AlertContent +import ru.shadowsparky.ui.components.ErrorContent import ru.shadowsparky.ui.components.IconButton import ru.shadowsparky.ui.components.LazyColumn import ru.shadowsparky.ui.components.Loading @@ -107,16 +106,7 @@ fun ChatContent(component: ChatComponent, paddingValues: PaddingValues, state: C } is ChatState.Error -> { - Column(horizontalAlignment = Alignment.CenterHorizontally) { - Text( - text = stringResource(Res.string.chat_load_error), - color = MaterialTheme.colorScheme.error - ) - Spacer(modifier = Modifier.height(8.dp)) - Button(onClick = { component.loadInitialMessages() }) { - Text(stringResource(Res.string.chat_retry_button)) - } - } + ErrorContent(message = state.message) { component.clearSendError() } } is ChatState.Data -> { ChatContent( diff --git a/feature/updater/updater-client/src/commonMain/kotlin/ru/shadowsparky/updater/presentation/UpdateDialog.kt b/feature/updater/updater-client/src/commonMain/kotlin/ru/shadowsparky/updater/presentation/UpdateDialog.kt index 2b5709a..e6ccdd4 100644 --- a/feature/updater/updater-client/src/commonMain/kotlin/ru/shadowsparky/updater/presentation/UpdateDialog.kt +++ b/feature/updater/updater-client/src/commonMain/kotlin/ru/shadowsparky/updater/presentation/UpdateDialog.kt @@ -29,10 +29,14 @@ import androidx.compose.material3.LinearWavyProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.runtime.withFrameNanos import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.arkivanov.decompose.extensions.compose.subscribeAsState @@ -58,16 +62,19 @@ fun UpdateDialog( onDismissRequest = { component.reset() }, canDismiss = canDismiss, content = { + val focusRequester = remember { FocusRequester() } when (val currentState = state) { is UpdateState.Checking -> UpdateCheckingContent() is UpdateState.UpdateAvailable -> UpdateAvailableContent( versionName = currentState.versionName, changeLog = currentState.changeLog, - onDownloadClick = { component.downloadUpdate(currentState.url) } + onDownloadClick = { component.downloadUpdate(currentState.url) }, + focusRequester = focusRequester ) is UpdateState.InstallPermissionRequired -> UpdatePermissionContent( - onGrantClick = { component.requestPermission() } + onGrantClick = { component.requestPermission() }, + focusRequester = focusRequester ) is UpdateState.Downloading -> UpdateDownloadingContent( @@ -77,12 +84,14 @@ fun UpdateDialog( ) is UpdateState.ReadyToInstall -> UpdateReadyContent( - onInstallClick = { component.installApk(currentState.filePath) } + onInstallClick = { component.installApk(currentState.filePath) }, + focusRequester = focusRequester ) is UpdateState.Error -> UpdateErrorContent( message = currentState.message, - onRetryClick = { component.checkUpdates() } + onRetryClick = { component.checkUpdates() }, + focusRequester = focusRequester ) is UpdateState.UpToDate -> { @@ -98,6 +107,10 @@ fun UpdateDialog( else -> Unit } + LaunchedEffect(state) { + withFrameNanos {} + focusRequester.requestFocus() + } } ) } @@ -113,6 +126,7 @@ private fun UpdateAvailableContent( versionName: String, changeLog: String?, onDownloadClick: () -> Unit, + focusRequester: FocusRequester, modifier: Modifier = Modifier ) { Column( @@ -141,7 +155,10 @@ private fun UpdateAvailableContent( ) } item { - Button(onClick = onDownloadClick, modifier = Modifier.fillMaxWidth()) { + Button( + onClick = onDownloadClick, + modifier = Modifier.focusRequester(focusRequester).fillMaxWidth() + ) { Text( stringResource(Res.string.updater_btn_download), style = MaterialTheme.typography.labelLarge @@ -155,6 +172,7 @@ private fun UpdateAvailableContent( @Composable private fun UpdatePermissionContent( onGrantClick: () -> Unit, + focusRequester: FocusRequester, modifier: Modifier = Modifier ) { Column( @@ -170,7 +188,10 @@ private fun UpdatePermissionContent( text = stringResource(Res.string.updater_desc_permission), style = MaterialTheme.typography.bodyMedium ) - Button(onClick = onGrantClick, modifier = Modifier.fillMaxWidth()) { + Button( + onClick = onGrantClick, + modifier = Modifier.focusRequester(focusRequester).fillMaxWidth() + ) { Text( stringResource(Res.string.updater_btn_grant), style = MaterialTheme.typography.labelLarge @@ -215,6 +236,7 @@ private fun UpdateDownloadingContent( @Composable private fun UpdateReadyContent( onInstallClick: () -> Unit, + focusRequester: FocusRequester, modifier: Modifier = Modifier ) { Column( @@ -226,7 +248,10 @@ private fun UpdateReadyContent( text = stringResource(Res.string.updater_title_ready), style = MaterialTheme.typography.titleLarge ) - Button(onClick = onInstallClick, modifier = Modifier.fillMaxWidth()) { + Button( + onClick = onInstallClick, + modifier = Modifier.focusRequester(focusRequester).fillMaxWidth() + ) { Text( stringResource(Res.string.updater_btn_install), style = MaterialTheme.typography.labelLarge @@ -260,6 +285,7 @@ private fun UpdateFinishedContent( private fun UpdateErrorContent( message: String, onRetryClick: () -> Unit, + focusRequester: FocusRequester, modifier: Modifier = Modifier ) { Column( @@ -273,7 +299,10 @@ private fun UpdateErrorContent( color = MaterialTheme.colorScheme.error ) Text(text = message, style = MaterialTheme.typography.bodyMedium) - Button(onClick = onRetryClick, modifier = Modifier.fillMaxWidth()) { + Button( + onClick = onRetryClick, + modifier = Modifier.focusRequester(focusRequester).fillMaxWidth() + ) { Text( stringResource(Res.string.updater_btn_retry), style = MaterialTheme.typography.labelLarge @@ -289,7 +318,8 @@ private fun UpdateAvailablePreview() { UpdateAvailableContent( versionName = "v1.4.0", changeLog = "- Добавили темную тему\n- Исправили падение на главном экране\n- Повысили стабильность", - onDownloadClick = {} + onDownloadClick = {}, + focusRequester = FocusRequester() ) } } @@ -310,7 +340,10 @@ private fun UpdateDownloadingPreview() { @Composable private fun UpdatePermissionPreview() { MaterialTheme { - UpdatePermissionContent(onGrantClick = {}) + UpdatePermissionContent( + onGrantClick = {}, + focusRequester = FocusRequester() + ) } } @@ -318,6 +351,10 @@ private fun UpdatePermissionPreview() { @Composable private fun UpdateErrorPreview() { MaterialTheme { - UpdateErrorContent(message = "Http status 503: Service Unavailable", onRetryClick = {}) + UpdateErrorContent( + message = "Http status 503: Service Unavailable", + onRetryClick = {}, + focusRequester = FocusRequester() + ) } } diff --git a/feature/video-player/src/commonMain/kotlin/ru/shadowsparky/videoplayer/ui/VideoPlayer.kt b/feature/video-player/src/commonMain/kotlin/ru/shadowsparky/videoplayer/ui/VideoPlayer.kt index 81c97da..21c6503 100644 --- a/feature/video-player/src/commonMain/kotlin/ru/shadowsparky/videoplayer/ui/VideoPlayer.kt +++ b/feature/video-player/src/commonMain/kotlin/ru/shadowsparky/videoplayer/ui/VideoPlayer.kt @@ -114,7 +114,7 @@ fun VideoPlayer( ) } - Box(modifier = rootModifier.fillMaxSize().background(Color.Black)) { + Box(modifier = rootModifier.fillMaxSize()) { VideoCanvas( controller = controller, modifier = rootModifier.fillMaxSize(), diff --git a/libs/ui/src/commonMain/composeResources/values/strings.xml b/libs/ui/src/commonMain/composeResources/values/strings.xml index c05ccd3..ab16f34 100644 --- a/libs/ui/src/commonMain/composeResources/values/strings.xml +++ b/libs/ui/src/commonMain/composeResources/values/strings.xml @@ -1,4 +1,5 @@ Произошла ошибка Понятно + Повторить diff --git a/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/components/ErrorInfo.kt b/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/components/ErrorInfo.kt index a6236ad..53aee18 100644 --- a/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/components/ErrorInfo.kt +++ b/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/components/ErrorInfo.kt @@ -3,6 +3,7 @@ package ru.shadowsparky.ui.components import androiddev.libs.ui.generated.resources.Res import androiddev.libs.ui.generated.resources.error_title import androiddev.libs.ui.generated.resources.ok +import androiddev.libs.ui.generated.resources.retry import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -25,9 +26,10 @@ fun ErrorContent( message: String, modifier: Modifier = Modifier, buttonModifier: Modifier = Modifier, - buttonText: String = stringResource(Res.string.ok), + retry: Boolean = true, onClick: () -> Unit ) { + val buttonTextRes = if (retry) Res.string.retry else Res.string.ok Column( verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, @@ -35,10 +37,10 @@ fun ErrorContent( ) { Text( text = stringResource(Res.string.error_title), - style = MaterialTheme.typography.titleMedium, + style = MaterialTheme.typography.titleLarge, textAlign = TextAlign.Center ) - Spacer(Modifier.height(2.dp)) + Spacer(Modifier.height(4.dp)) Text( text = message, style = MaterialTheme.typography.bodyMedium, @@ -47,7 +49,7 @@ fun ErrorContent( Spacer(Modifier.height(8.dp)) Button(modifier = buttonModifier, onClick = { onClick() }) { Text( - text = buttonText, + text = stringResource(buttonTextRes), style = MaterialTheme.typography.bodyMedium ) } @@ -57,7 +59,6 @@ fun ErrorContent( @Composable fun ErrorAlert( message: String, - buttonText: String = stringResource(Res.string.ok), onClick: () -> Unit, onSkip: () -> Unit = onClick ) { @@ -66,7 +67,7 @@ fun ErrorAlert( message, Modifier, Modifier.fillMaxWidth(), - buttonText, + false, onClick ) }