fix
This commit is contained in:
@@ -13,7 +13,7 @@ class ProductModel extends HiveObject {
|
||||
final String name;
|
||||
|
||||
@HiveField(2)
|
||||
final String description;
|
||||
final String? description;
|
||||
|
||||
@HiveField(3)
|
||||
final double price;
|
||||
@@ -39,7 +39,7 @@ class ProductModel extends HiveObject {
|
||||
ProductModel({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.description,
|
||||
this.description,
|
||||
required this.price,
|
||||
this.imageUrl,
|
||||
required this.categoryId,
|
||||
@@ -86,7 +86,7 @@ class ProductModel extends HiveObject {
|
||||
return ProductModel(
|
||||
id: json['id'] as String,
|
||||
name: json['name'] as String,
|
||||
description: json['description'] as String? ?? '',
|
||||
description: json['description'] as String?,
|
||||
price: (json['price'] as num).toDouble(),
|
||||
imageUrl: json['imageUrl'] as String?,
|
||||
categoryId: json['categoryId'] as String,
|
||||
|
||||
@@ -19,8 +19,27 @@ class ProductRepositoryImpl implements ProductRepository {
|
||||
@override
|
||||
Future<Either<Failure, List<Product>>> getAllProducts() async {
|
||||
try {
|
||||
final products = await localDataSource.getAllProducts();
|
||||
// Try remote first (online-first)
|
||||
final products = await remoteDataSource.getAllProducts();
|
||||
// Cache the results
|
||||
await localDataSource.cacheProducts(products);
|
||||
return Right(products.map((model) => model.toEntity()).toList());
|
||||
} on ServerException catch (e) {
|
||||
// Remote failed, try local cache
|
||||
try {
|
||||
final cachedProducts = await localDataSource.getAllProducts();
|
||||
return Right(cachedProducts.map((model) => model.toEntity()).toList());
|
||||
} on CacheException catch (cacheError) {
|
||||
return Left(ServerFailure('${e.message}. Cache also unavailable.'));
|
||||
}
|
||||
} on NetworkException catch (e) {
|
||||
// Network failed, try local cache
|
||||
try {
|
||||
final cachedProducts = await localDataSource.getAllProducts();
|
||||
return Right(cachedProducts.map((model) => model.toEntity()).toList());
|
||||
} on CacheException catch (cacheError) {
|
||||
return Left(NetworkFailure('${e.message}. Cache also unavailable.'));
|
||||
}
|
||||
} on CacheException catch (e) {
|
||||
return Left(CacheFailure(e.message));
|
||||
}
|
||||
@@ -29,9 +48,29 @@ class ProductRepositoryImpl implements ProductRepository {
|
||||
@override
|
||||
Future<Either<Failure, List<Product>>> getProductsByCategory(String categoryId) async {
|
||||
try {
|
||||
final allProducts = await localDataSource.getAllProducts();
|
||||
final filtered = allProducts.where((p) => p.categoryId == categoryId).toList();
|
||||
return Right(filtered.map((model) => model.toEntity()).toList());
|
||||
// Try remote first (online-first)
|
||||
final allProducts = await remoteDataSource.getAllProducts(categoryId: categoryId);
|
||||
// Cache the results
|
||||
await localDataSource.cacheProducts(allProducts);
|
||||
return Right(allProducts.map((model) => model.toEntity()).toList());
|
||||
} on ServerException catch (e) {
|
||||
// Remote failed, try local cache
|
||||
try {
|
||||
final cachedProducts = await localDataSource.getAllProducts();
|
||||
final filtered = cachedProducts.where((p) => p.categoryId == categoryId).toList();
|
||||
return Right(filtered.map((model) => model.toEntity()).toList());
|
||||
} on CacheException catch (cacheError) {
|
||||
return Left(ServerFailure('${e.message}. Cache also unavailable.'));
|
||||
}
|
||||
} on NetworkException catch (e) {
|
||||
// Network failed, try local cache
|
||||
try {
|
||||
final cachedProducts = await localDataSource.getAllProducts();
|
||||
final filtered = cachedProducts.where((p) => p.categoryId == categoryId).toList();
|
||||
return Right(filtered.map((model) => model.toEntity()).toList());
|
||||
} on CacheException catch (cacheError) {
|
||||
return Left(NetworkFailure('${e.message}. Cache also unavailable.'));
|
||||
}
|
||||
} on CacheException catch (e) {
|
||||
return Left(CacheFailure(e.message));
|
||||
}
|
||||
@@ -40,13 +79,37 @@ class ProductRepositoryImpl implements ProductRepository {
|
||||
@override
|
||||
Future<Either<Failure, List<Product>>> searchProducts(String query) async {
|
||||
try {
|
||||
final allProducts = await localDataSource.getAllProducts();
|
||||
final filtered = allProducts.where((p) {
|
||||
final nameMatch = p.name.toLowerCase().contains(query.toLowerCase());
|
||||
final descMatch = p.description?.toLowerCase().contains(query.toLowerCase()) ?? false;
|
||||
return nameMatch || descMatch;
|
||||
}).toList();
|
||||
return Right(filtered.map((model) => model.toEntity()).toList());
|
||||
// Try remote first (online-first)
|
||||
final searchResults = await remoteDataSource.getAllProducts(search: query);
|
||||
// Cache the results
|
||||
await localDataSource.cacheProducts(searchResults);
|
||||
return Right(searchResults.map((model) => model.toEntity()).toList());
|
||||
} on ServerException catch (e) {
|
||||
// Remote failed, search in local cache
|
||||
try {
|
||||
final cachedProducts = await localDataSource.getAllProducts();
|
||||
final filtered = cachedProducts.where((p) {
|
||||
final nameMatch = p.name.toLowerCase().contains(query.toLowerCase());
|
||||
final descMatch = p.description?.toLowerCase().contains(query.toLowerCase()) ?? false;
|
||||
return nameMatch || descMatch;
|
||||
}).toList();
|
||||
return Right(filtered.map((model) => model.toEntity()).toList());
|
||||
} on CacheException catch (cacheError) {
|
||||
return Left(ServerFailure('${e.message}. Cache also unavailable.'));
|
||||
}
|
||||
} on NetworkException catch (e) {
|
||||
// Network failed, search in local cache
|
||||
try {
|
||||
final cachedProducts = await localDataSource.getAllProducts();
|
||||
final filtered = cachedProducts.where((p) {
|
||||
final nameMatch = p.name.toLowerCase().contains(query.toLowerCase());
|
||||
final descMatch = p.description?.toLowerCase().contains(query.toLowerCase()) ?? false;
|
||||
return nameMatch || descMatch;
|
||||
}).toList();
|
||||
return Right(filtered.map((model) => model.toEntity()).toList());
|
||||
} on CacheException catch (cacheError) {
|
||||
return Left(NetworkFailure('${e.message}. Cache also unavailable.'));
|
||||
}
|
||||
} on CacheException catch (e) {
|
||||
return Left(CacheFailure(e.message));
|
||||
}
|
||||
@@ -55,11 +118,33 @@ class ProductRepositoryImpl implements ProductRepository {
|
||||
@override
|
||||
Future<Either<Failure, Product>> getProductById(String id) async {
|
||||
try {
|
||||
final product = await localDataSource.getProductById(id);
|
||||
if (product == null) {
|
||||
return Left(NotFoundFailure('Product not found'));
|
||||
}
|
||||
// Try remote first (online-first)
|
||||
final product = await remoteDataSource.getProductById(id);
|
||||
// Cache the result
|
||||
await localDataSource.updateProduct(product);
|
||||
return Right(product.toEntity());
|
||||
} on ServerException catch (e) {
|
||||
// Remote failed, try local cache
|
||||
try {
|
||||
final cachedProduct = await localDataSource.getProductById(id);
|
||||
if (cachedProduct == null) {
|
||||
return Left(NotFoundFailure('Product not found in cache'));
|
||||
}
|
||||
return Right(cachedProduct.toEntity());
|
||||
} on CacheException catch (cacheError) {
|
||||
return Left(ServerFailure('${e.message}. Cache also unavailable.'));
|
||||
}
|
||||
} on NetworkException catch (e) {
|
||||
// Network failed, try local cache
|
||||
try {
|
||||
final cachedProduct = await localDataSource.getProductById(id);
|
||||
if (cachedProduct == null) {
|
||||
return Left(NotFoundFailure('Product not found in cache'));
|
||||
}
|
||||
return Right(cachedProduct.toEntity());
|
||||
} on CacheException catch (cacheError) {
|
||||
return Left(NetworkFailure('${e.message}. Cache also unavailable.'));
|
||||
}
|
||||
} on CacheException catch (e) {
|
||||
return Left(CacheFailure(e.message));
|
||||
}
|
||||
@@ -68,11 +153,7 @@ class ProductRepositoryImpl implements ProductRepository {
|
||||
@override
|
||||
Future<Either<Failure, List<Product>>> syncProducts() async {
|
||||
try {
|
||||
final response = await remoteDataSource.getAllProducts();
|
||||
final productsData = response['data'] as List<dynamic>;
|
||||
final products = productsData
|
||||
.map((json) => ProductModel.fromJson(json as Map<String, dynamic>))
|
||||
.toList();
|
||||
final products = await remoteDataSource.getAllProducts();
|
||||
await localDataSource.cacheProducts(products);
|
||||
final entities = products.map((model) => model.toEntity()).toList();
|
||||
return Right(entities);
|
||||
|
||||
@@ -246,7 +246,7 @@ class ProductDetailPage extends ConsumerWidget {
|
||||
|
||||
/// Build description section
|
||||
Widget _buildDescriptionSection(BuildContext context) {
|
||||
if (product.description.isEmpty) {
|
||||
if (product.description == null || product.description!.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
@@ -261,7 +261,7 @@ class ProductDetailPage extends ConsumerWidget {
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
product.description,
|
||||
product.description!,
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
],
|
||||
|
||||
@@ -15,7 +15,7 @@ class FilteredProducts extends _$FilteredProducts {
|
||||
// Watch products state
|
||||
final productsAsync = ref.watch(productsProvider);
|
||||
final products = productsAsync.when(
|
||||
data: (data) => data.products,
|
||||
data: (data) => data,
|
||||
loading: () => <Product>[],
|
||||
error: (_, __) => <Product>[],
|
||||
);
|
||||
|
||||
@@ -50,7 +50,7 @@ final class FilteredProductsProvider
|
||||
}
|
||||
}
|
||||
|
||||
String _$filteredProductsHash() => r'd8ca6d80a71bf354e3afe6c38335996a8bfc74b7';
|
||||
String _$filteredProductsHash() => r'97fb09ade4bc65f92f3d4844b059bb2b0660d3df';
|
||||
|
||||
/// Filtered products provider
|
||||
/// Combines products, search query, and category filter to provide filtered results
|
||||
|
||||
@@ -5,12 +5,12 @@ import '../../../../core/providers/providers.dart';
|
||||
|
||||
part 'products_provider.g.dart';
|
||||
|
||||
/// Provider for products list with API-first approach
|
||||
/// Provider for products list with online-first approach
|
||||
@riverpod
|
||||
class Products extends _$Products {
|
||||
@override
|
||||
Future<List<Product>> build() async {
|
||||
// API-first: Try to load from API first
|
||||
// Online-first: Try to load from API first
|
||||
final repository = ref.watch(productRepositoryProvider);
|
||||
final networkInfo = ref.watch(networkInfoProvider);
|
||||
|
||||
|
||||
@@ -85,9 +85,9 @@ class ProductListItem extends StatelessWidget {
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
if (product.description.isNotEmpty)
|
||||
if (product.description != null && product.description!.isNotEmpty)
|
||||
Text(
|
||||
product.description,
|
||||
product.description!,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user