Files
worker/lib/features/account/presentation/providers/location_provider.dart
Phuoc Nguyen 0dda402246 update address
2025-11-18 17:04:00 +07:00

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: (_, __) => {},
);
}