ux: единый экран ошибки F7ErrorState + retry (Этап 2 п.12)

Новый F7ErrorState: центрированная иконка + сообщение + «Повторить»
вместо россыпи красного текста. F7ModuleScreen при ошибке без данных
показывает его (+ новый onErrorRetry — retry без хедер-кнопки ↻).
Подключено: Контакты/Конференции/Карточки/Поддержка (через F7ModuleScreen),
Файлы (onErrorRetry=refreshCurrent), Почта (пустой список + ошибка →
F7ErrorState, убран верхний баннер-текст). Retry везде дергает load/refresh.
This commit is contained in:
b-dev-mobile
2026-07-09 14:08:36 +00:00
parent 8f7eaff611
commit a674721353
5 changed files with 75 additions and 11 deletions
@@ -16,8 +16,11 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
@@ -43,11 +46,14 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
/**
@@ -69,6 +75,46 @@ fun F7ScreenBackground(modifier: Modifier = Modifier, content: @Composable () ->
}
}
/**
* Единый экран ошибки (по центру): иконка + сообщение + кнопка «Повторить».
* Используется везде, где загрузка упала и данных нет — вместо россыпи красного текста.
*/
@Composable
fun F7ErrorState(
message: String,
modifier: Modifier = Modifier,
onRetry: (() -> Unit)? = null,
) {
Column(
modifier = modifier
.fillMaxSize()
.padding(24.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
Box(
modifier = Modifier
.size(56.dp)
.clip(CircleShape)
.background(F7Colors.Error.copy(alpha = 0.12f)),
contentAlignment = Alignment.Center,
) {
Text("!", color = F7Colors.Error, fontSize = 30.sp, fontWeight = FontWeight.Bold)
}
Spacer(Modifier.height(12.dp))
Text(
text = message,
style = MaterialTheme.typography.bodyMedium,
color = F7Colors.TextSecondary,
textAlign = TextAlign.Center,
)
if (onRetry != null) {
Spacer(Modifier.height(16.dp))
F7SecondaryButton(text = "Повторить", onClick = onRetry)
}
}
}
@Composable
fun F7ModuleScreen(
title: String? = null,
@@ -76,6 +122,9 @@ fun F7ModuleScreen(
loading: Boolean = false,
error: String? = null,
onRefresh: (() -> Unit)? = null,
// Действие «Повторить» в экране ошибки; по умолчанию — как onRefresh.
// Позволяет дать retry, не показывая хедер-кнопку ↻ (когда onRefresh не задан).
onErrorRetry: (() -> Unit)? = onRefresh,
headerActions: @Composable RowScope.() -> Unit = {},
content: @Composable ColumnScope.() -> Unit,
) {
@@ -126,14 +175,22 @@ fun F7ModuleScreen(
CircularProgressIndicator(color = F7Colors.Primary)
}
if (!error.isNullOrBlank()) {
Text(text = error, color = F7Colors.Error, style = MaterialTheme.typography.bodyMedium)
// Ошибка без данных → центрированный экран с «Повторить»
F7ErrorState(
message = error,
onRetry = onErrorRetry,
modifier = Modifier
.fillMaxWidth()
.weight(1f),
)
} else {
Column(
modifier = Modifier
.fillMaxWidth()
.weight(1f),
content = content,
)
}
Column(
modifier = Modifier
.fillMaxWidth()
.weight(1f),
content = content,
)
}
}