feat: surface read-only load errors

This commit is contained in:
Artem Kokos
2026-04-23 20:01:37 +07:00
parent 504f65fc5f
commit 5d2d0ac4a7
7 changed files with 491 additions and 76 deletions

View File

@@ -0,0 +1,55 @@
import 'package:dio/dio.dart';
String describeLoadError(Object error) {
if (error is DioException) {
final statusCode = error.response?.statusCode;
if (statusCode == 401 || statusCode == 403) {
return 'Нет доступа: API key отклонён ($statusCode).';
}
if (_isNetworkFailure(error)) {
return 'Backend недоступен: ${error.message ?? error.type.name}.';
}
final responseMessage = _responseMessage(error);
if (responseMessage != null) {
return 'Backend вернул ошибку $statusCode: $responseMessage';
}
if (statusCode != null) {
return 'Backend вернул ошибку $statusCode.';
}
return 'Ошибка запроса: ${error.message ?? error.type.name}.';
}
if (error is FormatException) {
return 'Неожиданный ответ backend: ${error.message}';
}
return error.toString();
}
bool _isNetworkFailure(DioException error) {
return switch (error.type) {
DioExceptionType.connectionTimeout ||
DioExceptionType.sendTimeout ||
DioExceptionType.receiveTimeout ||
DioExceptionType.connectionError ||
DioExceptionType.unknown => true,
_ => false,
};
}
String? _responseMessage(DioException error) {
final data = error.response?.data;
if (data is Map) {
final value = data['detail'] ?? data['message'] ?? data['error'];
return value?.toString();
}
if (data is String && data.trim().isNotEmpty) {
return data.trim();
}
return null;
}

34
lib/app/load_state.dart Normal file
View File

@@ -0,0 +1,34 @@
enum LoadStatus { idle, loading, data, empty, error }
class LoadState<T> {
final LoadStatus status;
final T data;
final String? errorMessage;
const LoadState.idle(this.data)
: status = LoadStatus.idle,
errorMessage = null;
const LoadState.loading(this.data)
: status = LoadStatus.loading,
errorMessage = null;
const LoadState.data(this.data)
: status = LoadStatus.data,
errorMessage = null;
const LoadState.empty(this.data)
: status = LoadStatus.empty,
errorMessage = null;
const LoadState.error(this.data, this.errorMessage)
: status = LoadStatus.error;
bool get isIdle => status == LoadStatus.idle;
bool get isLoading => status == LoadStatus.loading;
bool get hasError => status == LoadStatus.error;
bool get isEmpty => status == LoadStatus.empty;
}