Files
ignis_app/lib/app/app_bootstrap.dart
2026-05-12 11:27:01 +07:00

108 lines
3.0 KiB
Dart

import 'package:dio/dio.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../providers/providers.dart';
enum AppBootstrapStatus {
bootstrapping,
noHomes,
ready,
authFailed,
networkFailed,
invalidConfig,
failed,
}
class AppBootstrapState {
final AppBootstrapStatus status;
final String? message;
const AppBootstrapState._(this.status, {this.message});
const AppBootstrapState.bootstrapping()
: this._(AppBootstrapStatus.bootstrapping);
const AppBootstrapState.noHomes() : this._(AppBootstrapStatus.noHomes);
const AppBootstrapState.ready() : this._(AppBootstrapStatus.ready);
const AppBootstrapState.authFailed(String message)
: this._(AppBootstrapStatus.authFailed, message: message);
const AppBootstrapState.networkFailed(String message)
: this._(AppBootstrapStatus.networkFailed, message: message);
const AppBootstrapState.invalidConfig(String message)
: this._(AppBootstrapStatus.invalidConfig, message: message);
const AppBootstrapState.failed(String message)
: this._(AppBootstrapStatus.failed, message: message);
bool get isBootstrapping => status == AppBootstrapStatus.bootstrapping;
}
final appBootstrapProvider =
NotifierProvider<AppBootstrapNotifier, AppBootstrapState>(
AppBootstrapNotifier.new,
);
class AppBootstrapNotifier extends Notifier<AppBootstrapState> {
@override
AppBootstrapState build() => const AppBootstrapState.bootstrapping();
Future<void> bootstrap() async {
state = const AppBootstrapState.bootstrapping();
try {
await ref.read(homesProvider.notifier).load();
await ref.read(currentHomeProvider.notifier).load();
final home = ref.read(currentHomeProvider);
if (home == null) {
state = const AppBootstrapState.noHomes();
return;
}
await ref.read(authInfoProvider.notifier).load(failOnError: true);
state = const AppBootstrapState.ready();
} catch (e) {
state = _failureState(e);
}
}
AppBootstrapState _failureState(Object error) {
if (error is StateError || error is FormatException) {
return AppBootstrapState.invalidConfig(error.toString());
}
if (error is DioException) {
final statusCode = error.response?.statusCode;
if (statusCode == 401 || statusCode == 403) {
return AppBootstrapState.authFailed(
'API key не прошёл авторизацию ($statusCode)',
);
}
if (_isNetworkFailure(error)) {
return AppBootstrapState.networkFailed(
'Backend недоступен: ${error.message ?? error.type.name}',
);
}
}
return AppBootstrapState.failed(error.toString());
}
bool _isNetworkFailure(DioException error) {
return switch (error.type) {
DioExceptionType.connectionTimeout ||
DioExceptionType.sendTimeout ||
DioExceptionType.receiveTimeout ||
DioExceptionType.connectionError ||
DioExceptionType.unknown => true,
_ => false,
};
}
}