/// Location Provider /// /// Riverpod providers for cities and wards management. library; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:worker/core/database/hive_service.dart'; import 'package:worker/core/network/dio_client.dart'; import 'package:worker/features/account/data/datasources/location_local_datasource.dart'; import 'package:worker/features/account/data/datasources/location_remote_datasource.dart'; import 'package:worker/features/account/data/repositories/location_repository_impl.dart'; import 'package:worker/features/account/domain/entities/city.dart'; import 'package:worker/features/account/domain/entities/ward.dart'; import 'package:worker/features/account/domain/repositories/location_repository.dart'; part 'location_provider.g.dart'; // ============================================================================ // DATASOURCE PROVIDERS // ============================================================================ /// Provides instance of LocationRemoteDataSource @riverpod Future locationRemoteDataSource(Ref ref) async { final dioClient = await ref.watch(dioClientProvider.future); return LocationRemoteDataSource(dioClient.dio); } /// Provides instance of LocationLocalDataSource @riverpod LocationLocalDataSource locationLocalDataSource(Ref ref) { final hiveService = HiveService(); return LocationLocalDataSource(hiveService); } // ============================================================================ // REPOSITORY PROVIDER // ============================================================================ /// Provides instance of LocationRepository @riverpod Future locationRepository(Ref ref) async { final remoteDataSource = await ref.watch(locationRemoteDataSourceProvider.future); final localDataSource = ref.watch(locationLocalDataSourceProvider); return LocationRepositoryImpl( remoteDataSource: remoteDataSource, localDataSource: localDataSource, ); } // ============================================================================ // CITIES PROVIDER // ============================================================================ /// Manages list of cities with offline-first approach /// /// This is the MAIN provider for cities. /// Returns list of City entities (cached → API). @Riverpod(keepAlive: true) class Cities extends _$Cities { late LocationRepository _repository; @override Future> build() async { _repository = await ref.read(locationRepositoryProvider.future); return await _loadCities(); } /// Load cities (offline-first) Future> _loadCities({bool forceRefresh = false}) async { try { final cities = await _repository.getCities(forceRefresh: forceRefresh); return cities; } catch (e) { rethrow; } } /// Refresh cities from API Future refresh() async { state = const AsyncValue.loading(); state = await AsyncValue.guard(() async { return await _loadCities(forceRefresh: true); }); } } // ============================================================================ // WARDS PROVIDER (per city) // ============================================================================ /// Manages list of wards for a specific city with offline-first approach /// /// Uses .family modifier to create a provider per city code. /// Returns list of Ward entities (cached → API). @riverpod Future> wards(Ref ref, String cityCode) async { final repository = await ref.watch(locationRepositoryProvider.future); try { final wards = await repository.getWards(cityCode); return wards; } catch (e) { rethrow; } } // ============================================================================ // HELPER PROVIDERS // ============================================================================ /// Get city by code @riverpod City? cityByCode(Ref ref, String code) { final citiesAsync = ref.watch(citiesProvider); return citiesAsync.when( data: (cities) { try { return cities.firstWhere((city) => city.code == code); } catch (e) { return null; } }, loading: () => null, error: (_, __) => null, ); } /// Get cities as map (code → City) for easy lookup @riverpod Map citiesMap(Ref ref) { final citiesAsync = ref.watch(citiesProvider); return citiesAsync.when( data: (cities) => {for (final city in cities) city.code: city}, loading: () => {}, error: (_, __) => {}, ); } /// Get wards as map (code → Ward) for a city @riverpod Map wardsMap(Ref ref, String cityCode) { final wardsAsync = ref.watch(wardsProvider(cityCode)); return wardsAsync.when( data: (wards) => {for (final ward in wards) ward.code: ward}, loading: () => {}, error: (_, __) => {}, ); }