154 lines
4.8 KiB
Dart
154 lines
4.8 KiB
Dart
/// 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> 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> 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<List<City>> build() async {
|
|
_repository = await ref.read(locationRepositoryProvider.future);
|
|
return await _loadCities();
|
|
}
|
|
|
|
/// Load cities (offline-first)
|
|
Future<List<City>> _loadCities({bool forceRefresh = false}) async {
|
|
try {
|
|
final cities = await _repository.getCities(forceRefresh: forceRefresh);
|
|
return cities;
|
|
} catch (e) {
|
|
rethrow;
|
|
}
|
|
}
|
|
|
|
/// Refresh cities from API
|
|
Future<void> 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<List<Ward>> 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<String, City> 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<String, Ward> 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: (_, __) => {},
|
|
);
|
|
}
|