This commit is contained in:
Phuoc Nguyen
2025-10-17 17:49:01 +07:00
parent 628c81ce13
commit 57bf73e4d1
23 changed files with 2655 additions and 87 deletions

View File

@@ -0,0 +1,161 @@
/// Repository Implementation: Home Repository
///
/// Concrete implementation of the HomeRepository interface.
/// Coordinates between local and remote data sources to provide home data.
///
/// Implements offline-first strategy:
/// 1. Try to return cached data immediately
/// 2. Fetch fresh data from server in background
/// 3. Update cache with fresh data
library;
import 'package:worker/core/errors/exceptions.dart';
import 'package:worker/core/errors/failures.dart';
import 'package:worker/features/home/data/datasources/home_local_datasource.dart';
import 'package:worker/features/home/domain/entities/member_card.dart';
import 'package:worker/features/home/domain/entities/promotion.dart';
import 'package:worker/features/home/domain/repositories/home_repository.dart';
/// Home Repository Implementation
///
/// Responsibilities:
/// - Coordinate between local cache and remote API
/// - Implement offline-first data strategy
/// - Handle errors and convert to domain failures
/// - Manage cache invalidation
class HomeRepositoryImpl implements HomeRepository {
/// Local data source (Hive)
final HomeLocalDataSource localDataSource;
/// Remote data source (API) - TODO: Add when API is ready
// final HomeRemoteDataSource remoteDataSource;
/// Constructor
HomeRepositoryImpl({
required this.localDataSource,
// required this.remoteDataSource, // TODO: Add when API ready
});
@override
Future<MemberCard> getMemberCard() async {
try {
// TODO: Implement offline-first strategy
// 1. Check if cache is valid
final isCacheValid = await localDataSource.isMemberCardCacheValid();
if (isCacheValid) {
// 2. Return cached data if valid
final cachedModel = await localDataSource.getMemberCard();
return cachedModel.toEntity();
}
// 3. If cache invalid, fetch from remote (when API ready)
// For now, try to return cached data even if expired
try {
final cachedModel = await localDataSource.getMemberCard();
return cachedModel.toEntity();
} catch (e) {
// TODO: Fetch from remote API when available
// final remoteModel = await remoteDataSource.getMemberCard();
// await localDataSource.cacheMemberCard(remoteModel);
// return remoteModel.toEntity();
throw const CacheException('No member card data available');
}
} on CacheException catch (e) {
throw CacheFailure(message: e.message);
} catch (e) {
throw ServerFailure(message: 'Failed to get member card: $e');
}
}
@override
Future<List<Promotion>> getPromotions() async {
try {
// TODO: Implement offline-first strategy
// 1. Check if cache is valid
final isCacheValid = await localDataSource.isPromotionsCacheValid();
if (isCacheValid) {
// 2. Return cached data if valid
final cachedModels = await localDataSource.getPromotions();
return cachedModels.map((model) => model.toEntity()).toList();
}
// 3. If cache invalid, fetch from remote (when API ready)
// For now, try to return cached data even if expired
try {
final cachedModels = await localDataSource.getPromotions();
return cachedModels.map((model) => model.toEntity()).toList();
} catch (e) {
// TODO: Fetch from remote API when available
// final remoteModels = await remoteDataSource.getPromotions();
// await localDataSource.cachePromotions(remoteModels);
// return remoteModels.map((m) => m.toEntity()).toList();
// Return empty list if no data available
return [];
}
} on CacheException catch (e) {
throw CacheFailure(message: e.message);
} catch (e) {
throw ServerFailure(message: 'Failed to get promotions: $e');
}
}
@override
Future<MemberCard> refreshMemberCard() async {
try {
// TODO: Implement force refresh from API
// This should always fetch from remote, bypassing cache
// When API is ready:
// 1. Fetch from remote
// final remoteModel = await remoteDataSource.getMemberCard();
// 2. Update cache
// await localDataSource.cacheMemberCard(remoteModel);
// 3. Return entity
// return remoteModel.toEntity();
// For now, just return cached data
final cachedModel = await localDataSource.getMemberCard();
return cachedModel.toEntity();
} on ServerException catch (e) {
throw ServerFailure(message: e.message);
} on NetworkException catch (e) {
throw NetworkFailure(message: e.message);
} catch (e) {
throw ServerFailure(message: 'Failed to refresh member card: $e');
}
}
@override
Future<List<Promotion>> refreshPromotions() async {
try {
// TODO: Implement force refresh from API
// This should always fetch from remote, bypassing cache
// When API is ready:
// 1. Fetch from remote
// final remoteModels = await remoteDataSource.getPromotions();
// 2. Update cache
// await localDataSource.cachePromotions(remoteModels);
// 3. Return entities
// return remoteModels.map((m) => m.toEntity()).toList();
// For now, just return cached data
final cachedModels = await localDataSource.getPromotions();
return cachedModels.map((model) => model.toEntity()).toList();
} on ServerException catch (e) {
throw ServerFailure(message: e.message);
} on NetworkException catch (e) {
throw NetworkFailure(message: e.message);
} catch (e) {
throw ServerFailure(message: 'Failed to refresh promotions: $e');
}
}
}