Files
worker/lib/features/favorites/README.md
Phuoc Nguyen 19d9a3dc2d update loaing
2025-12-02 18:09:20 +07:00

285 lines
6.9 KiB
Markdown

# Favorites Feature
A complete favorites state management system for the Worker app using **Riverpod 3.0** with code generation and **Hive** for local persistence.
---
## 📁 File Structure
```
lib/features/favorites/
├── data/
│ ├── datasources/
│ │ └── favorites_local_datasource.dart # Hive operations
│ └── models/
│ ├── favorite_model.dart # Hive model (TypeId: 28)
│ └── favorite_model.g.dart # Generated adapter
├── domain/
│ └── entities/
│ └── favorite.dart # Business entity
├── presentation/
│ └── providers/
│ ├── favorites_provider.dart # Riverpod providers
│ └── favorites_provider.g.dart # Generated providers
├── INTEGRATION_SUMMARY.md # Complete documentation
├── USAGE_EXAMPLES.md # Code examples
└── README.md # This file
```
---
## 🚀 Quick Start
### 1. Import Provider
```dart
import 'package:worker/features/favorites/presentation/providers/favorites_provider.dart';
```
### 2. Check if Product is Favorited
```dart
final isFavorited = ref.watch(isFavoriteProvider(productId));
```
### 3. Toggle Favorite
```dart
ref.read(favoritesProvider.notifier).toggleFavorite(productId);
```
### 4. Get Favorites Count
```dart
final count = ref.watch(favoriteCountProvider);
```
---
## 📦 Available Providers
| Provider | Type | Description |
|----------|------|-------------|
| `favoritesProvider` | `AsyncNotifier<Set<String>>` | Main favorites state |
| `isFavoriteProvider(productId)` | `Provider<bool>` | Check if product is favorited |
| `favoriteCountProvider` | `Provider<int>` | Total count of favorites |
| `favoriteProductIdsProvider` | `Provider<List<String>>` | All favorite product IDs |
| `favoritesLocalDataSourceProvider` | `Provider<DataSource>` | Database access |
| `currentUserIdProvider` | `Provider<String>` | Current user ID (TODO: auth) |
---
## 🎯 Common Use Cases
### Favorite Button in Product Card
```dart
class ProductCard extends ConsumerWidget {
final String productId;
@override
Widget build(BuildContext context, WidgetRef ref) {
final isFavorited = ref.watch(isFavoriteProvider(productId));
return IconButton(
icon: Icon(
isFavorited ? Icons.favorite : Icons.favorite_border,
color: isFavorited ? Colors.red : Colors.grey,
),
onPressed: () {
ref.read(favoritesProvider.notifier).toggleFavorite(productId);
},
);
}
}
```
### Favorites Count Badge
```dart
class FavoriteBadge extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(favoriteCountProvider);
return Badge(
label: Text('$count'),
child: Icon(Icons.favorite),
);
}
}
```
### Display All Favorites
```dart
class FavoritesPage extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final favoritesAsync = ref.watch(favoritesProvider);
return favoritesAsync.when(
data: (favorites) => ListView.builder(
itemCount: favorites.length,
itemBuilder: (context, index) {
final productId = favorites.elementAt(index);
return ProductTile(productId: productId);
},
),
loading: () => const CustomLoadingIndicator(),
error: (error, stack) => ErrorWidget(error),
);
}
}
```
---
## 🔧 Provider Methods
### Main Provider (`favoritesProvider.notifier`)
| Method | Description |
|--------|-------------|
| `addFavorite(productId)` | Add product to favorites |
| `removeFavorite(productId)` | Remove product from favorites |
| `toggleFavorite(productId)` | Toggle favorite status |
| `refresh()` | Reload favorites from database |
| `clearAll()` | Remove all favorites |
---
## 💾 Data Persistence
- **Storage**: Hive local database
- **Box**: `favorite_box` (HiveBoxNames.favoriteBox)
- **Model**: `FavoriteModel` (TypeId: 28)
- **Fields**:
- `favoriteId`: Unique ID (userId_productId_timestamp)
- `productId`: Product reference
- `userId`: User reference
- `createdAt`: Timestamp
---
## ⚠️ Important Notes
### TODO: Auth Integration
Currently using hardcoded userId (`'user_001'`). Replace when auth is ready:
```dart
// In favorites_provider.dart
@riverpod
String currentUserId(Ref ref) {
// TODO: Replace with actual auth provider
final user = ref.watch(authProvider).user;
return user?.id ?? 'guest';
}
```
### Hive Box Initialization
Ensure the favorites box is opened in `main.dart`:
```dart
await Hive.openBox<FavoriteModel>(HiveBoxNames.favoriteBox);
```
---
## 📚 Documentation
- **[INTEGRATION_SUMMARY.md](./INTEGRATION_SUMMARY.md)** - Complete technical documentation
- **[USAGE_EXAMPLES.md](./USAGE_EXAMPLES.md)** - Comprehensive code examples
---
## ✅ Features
- ✅ Add/remove favorites
- ✅ Toggle favorite status
- ✅ Persistent storage (Hive)
- ✅ Multi-user support
- ✅ Optimistic updates
- ✅ Error handling
- ✅ Loading states
- ✅ Debug logging
- ✅ Auto-dispose providers
---
## 🧪 Testing
```dart
test('should add favorite', () async {
final container = ProviderContainer();
addTearDown(container.dispose);
await container.read(favoritesProvider.notifier)
.addFavorite('product_1');
final favorites = await container.read(favoritesProvider.future);
expect(favorites, contains('product_1'));
});
```
---
## 🎨 UI Integration Examples
See **[USAGE_EXAMPLES.md](./USAGE_EXAMPLES.md)** for:
- Favorite buttons with loading states
- Favorites page with pull-to-refresh
- Empty state handling
- Error state handling
- App lifecycle management
- Performance optimization
---
## 🐛 Debugging
All operations log to console:
```
[FavoritesProvider] Added favorite: product_123
[FavoritesLocalDataSource] Loaded 5 favorites for user: user_001
[FavoritesProvider] Error adding favorite: ...
```
---
## 📈 Performance
- **O(1)** favorite lookup using `Set<String>`
- **Auto-dispose** providers when not in use
- **Selective rebuilds** with `isFavoriteProvider`
- **Database compaction** available
---
## 🔮 Future Enhancements
- [ ] Remote API sync
- [ ] Offline queue
- [ ] Cross-device sync
- [ ] Batch operations
- [ ] Favorites collections
- [ ] Share favorites
- [ ] Analytics tracking
---
## 🆘 Troubleshooting
| Issue | Solution |
|-------|----------|
| Favorites not persisting | Check Hive box initialization in `main.dart` |
| State not updating | Use `ref.read()` for mutations, `ref.watch()` for listening |
| Performance issues | Use `isFavoriteProvider` instead of watching full `favoritesProvider` |
---
## 📞 Support
For detailed examples and advanced use cases, see:
- **[USAGE_EXAMPLES.md](./USAGE_EXAMPLES.md)**
- **[INTEGRATION_SUMMARY.md](./INTEGRATION_SUMMARY.md)**
---
**Status**: ✅ Ready for integration
**Version**: 1.0.0
**Last Updated**: 2024-10-24