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.new, ); class AppBootstrapNotifier extends Notifier { @override AppBootstrapState build() => const AppBootstrapState.bootstrapping(); Future 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); await syncGeofenceTask(ref.read(homesProvider)); 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, }; } }