This commit is contained in:
Phuoc Nguyen
2025-11-10 14:21:27 +07:00
parent 2a71c65577
commit 36bdf6613b
33 changed files with 2206 additions and 252 deletions

View File

@@ -5,10 +5,14 @@
library;
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:worker/core/network/dio_client.dart';
import 'package:worker/core/services/frappe_auth_provider.dart';
import 'package:worker/features/news/data/datasources/news_local_datasource.dart';
import 'package:worker/features/news/data/repositories/news_repository_impl.dart';
import 'package:worker/features/news/data/datasources/news_remote_datasource.dart';
import 'package:worker/features/news/domain/entities/blog_category.dart';
import 'package:worker/features/news/domain/entities/news_article.dart';
import 'package:worker/features/news/domain/repositories/news_repository.dart';
import 'package:worker/features/news/data/repositories/news_repository_impl.dart';
part 'news_provider.g.dart';
@@ -20,13 +24,27 @@ NewsLocalDataSource newsLocalDataSource(Ref ref) {
return NewsLocalDataSource();
}
/// News Remote DataSource Provider
///
/// Provides instance of NewsRemoteDataSource with Frappe auth service.
@riverpod
Future<NewsRemoteDataSource> newsRemoteDataSource(Ref ref) async {
final dioClient = await ref.watch(dioClientProvider.future);
final frappeAuthService = ref.watch(frappeAuthServiceProvider);
return NewsRemoteDataSource(dioClient, frappeAuthService);
}
/// News Repository Provider
///
/// Provides instance of NewsRepository implementation.
@riverpod
NewsRepository newsRepository(Ref ref) {
Future<NewsRepository> newsRepository(Ref ref) async {
final localDataSource = ref.watch(newsLocalDataSourceProvider);
return NewsRepositoryImpl(localDataSource: localDataSource);
final remoteDataSource = await ref.watch(newsRemoteDataSourceProvider.future);
return NewsRepositoryImpl(
localDataSource: localDataSource,
remoteDataSource: remoteDataSource,
);
}
/// News Articles Provider
@@ -35,7 +53,7 @@ NewsRepository newsRepository(Ref ref) {
/// Returns AsyncValue<List<NewsArticle>> for proper loading/error handling.
@riverpod
Future<List<NewsArticle>> newsArticles(Ref ref) async {
final repository = ref.watch(newsRepositoryProvider);
final repository = await ref.watch(newsRepositoryProvider.future);
return repository.getAllArticles();
}
@@ -45,7 +63,7 @@ Future<List<NewsArticle>> newsArticles(Ref ref) async {
/// Returns AsyncValue<NewsArticle?> (null if no featured article).
@riverpod
Future<NewsArticle?> featuredArticle(Ref ref) async {
final repository = ref.watch(newsRepositoryProvider);
final repository = await ref.watch(newsRepositoryProvider.future);
return repository.getFeaturedArticle();
}
@@ -79,7 +97,7 @@ class SelectedNewsCategory extends _$SelectedNewsCategory {
@riverpod
Future<List<NewsArticle>> filteredNewsArticles(Ref ref) async {
final selectedCategory = ref.watch(selectedNewsCategoryProvider);
final repository = ref.watch(newsRepositoryProvider);
final repository = await ref.watch(newsRepositoryProvider.future);
// If no category selected, return all articles
if (selectedCategory == null) {
@@ -96,6 +114,22 @@ Future<List<NewsArticle>> filteredNewsArticles(Ref ref) async {
/// Used for article detail page.
@riverpod
Future<NewsArticle?> newsArticleById(Ref ref, String articleId) async {
final repository = ref.watch(newsRepositoryProvider);
final repository = await ref.watch(newsRepositoryProvider.future);
return repository.getArticleById(articleId);
}
/// Blog Categories Provider
///
/// Fetches all published blog categories from Frappe API.
/// Returns AsyncValue<List<BlogCategory>> (domain entities) for proper loading/error handling.
///
/// Example categories:
/// - Tin tức (News)
/// - Chuyên môn (Professional)
/// - Dự án (Projects)
/// - Khuyến mãi (Promotions)
@riverpod
Future<List<BlogCategory>> blogCategories(Ref ref) async {
final repository = await ref.watch(newsRepositoryProvider.future);
return repository.getBlogCategories();
}

View File

@@ -67,6 +67,59 @@ final class NewsLocalDataSourceProvider
String _$newsLocalDataSourceHash() =>
r'e7e7d71d20274fe8b498c7b15f8aeb9eb515af27';
/// News Remote DataSource Provider
///
/// Provides instance of NewsRemoteDataSource with Frappe auth service.
@ProviderFor(newsRemoteDataSource)
const newsRemoteDataSourceProvider = NewsRemoteDataSourceProvider._();
/// News Remote DataSource Provider
///
/// Provides instance of NewsRemoteDataSource with Frappe auth service.
final class NewsRemoteDataSourceProvider
extends
$FunctionalProvider<
AsyncValue<NewsRemoteDataSource>,
NewsRemoteDataSource,
FutureOr<NewsRemoteDataSource>
>
with
$FutureModifier<NewsRemoteDataSource>,
$FutureProvider<NewsRemoteDataSource> {
/// News Remote DataSource Provider
///
/// Provides instance of NewsRemoteDataSource with Frappe auth service.
const NewsRemoteDataSourceProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'newsRemoteDataSourceProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$newsRemoteDataSourceHash();
@$internal
@override
$FutureProviderElement<NewsRemoteDataSource> $createElement(
$ProviderPointer pointer,
) => $FutureProviderElement(pointer);
@override
FutureOr<NewsRemoteDataSource> create(Ref ref) {
return newsRemoteDataSource(ref);
}
}
String _$newsRemoteDataSourceHash() =>
r'27db8dc4fadf806349fe4f0ad5fed1999620c1a3';
/// News Repository Provider
///
/// Provides instance of NewsRepository implementation.
@@ -79,8 +132,13 @@ const newsRepositoryProvider = NewsRepositoryProvider._();
/// Provides instance of NewsRepository implementation.
final class NewsRepositoryProvider
extends $FunctionalProvider<NewsRepository, NewsRepository, NewsRepository>
with $Provider<NewsRepository> {
extends
$FunctionalProvider<
AsyncValue<NewsRepository>,
NewsRepository,
FutureOr<NewsRepository>
>
with $FutureModifier<NewsRepository>, $FutureProvider<NewsRepository> {
/// News Repository Provider
///
/// Provides instance of NewsRepository implementation.
@@ -100,24 +158,17 @@ final class NewsRepositoryProvider
@$internal
@override
$ProviderElement<NewsRepository> $createElement($ProviderPointer pointer) =>
$ProviderElement(pointer);
$FutureProviderElement<NewsRepository> $createElement(
$ProviderPointer pointer,
) => $FutureProviderElement(pointer);
@override
NewsRepository create(Ref ref) {
FutureOr<NewsRepository> create(Ref ref) {
return newsRepository(ref);
}
/// {@macro riverpod.override_with_value}
Override overrideWithValue(NewsRepository value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<NewsRepository>(value),
);
}
}
String _$newsRepositoryHash() => r'1536188fae6934f147f022a8f5d7bd62ff9453b5';
String _$newsRepositoryHash() => r'8e66d847014926ad542e402874e52d35b00cdbcc';
/// News Articles Provider
///
@@ -172,7 +223,7 @@ final class NewsArticlesProvider
}
}
String _$newsArticlesHash() => r'24d70e49f7137c614c024dc93c97451c6e161ce6';
String _$newsArticlesHash() => r'789d916f1ce7d76f26429cfce97c65a71915edf3';
/// Featured Article Provider
///
@@ -225,7 +276,7 @@ final class FeaturedArticleProvider
}
}
String _$featuredArticleHash() => r'f7146600bc3bbaf5987ab6b09262135b1558f1c0';
String _$featuredArticleHash() => r'5fd7057d3f828d6f717b08d59561aa9637eb0097';
/// Selected News Category Provider
///
@@ -353,7 +404,7 @@ final class FilteredNewsArticlesProvider
}
String _$filteredNewsArticlesHash() =>
r'f40a737b74b44f2d4fa86977175314ed0da471fa';
r'f5d6faa2d510eae188f12fa41d052eeb43e08cc9';
/// News Article by ID Provider
///
@@ -424,7 +475,7 @@ final class NewsArticleByIdProvider
}
}
String _$newsArticleByIdHash() => r'4d28caa81d486fcd6cfefd16477355927bbcadc8';
String _$newsArticleByIdHash() => r'f2b5ee4a3f7b67d0ee9e9c91169d740a9f250b50';
/// News Article by ID Provider
///
@@ -453,3 +504,76 @@ final class NewsArticleByIdFamily extends $Family
@override
String toString() => r'newsArticleByIdProvider';
}
/// Blog Categories Provider
///
/// Fetches all published blog categories from Frappe API.
/// Returns AsyncValue<List<BlogCategory>> (domain entities) for proper loading/error handling.
///
/// Example categories:
/// - Tin tức (News)
/// - Chuyên môn (Professional)
/// - Dự án (Projects)
/// - Khuyến mãi (Promotions)
@ProviderFor(blogCategories)
const blogCategoriesProvider = BlogCategoriesProvider._();
/// Blog Categories Provider
///
/// Fetches all published blog categories from Frappe API.
/// Returns AsyncValue<List<BlogCategory>> (domain entities) for proper loading/error handling.
///
/// Example categories:
/// - Tin tức (News)
/// - Chuyên môn (Professional)
/// - Dự án (Projects)
/// - Khuyến mãi (Promotions)
final class BlogCategoriesProvider
extends
$FunctionalProvider<
AsyncValue<List<BlogCategory>>,
List<BlogCategory>,
FutureOr<List<BlogCategory>>
>
with
$FutureModifier<List<BlogCategory>>,
$FutureProvider<List<BlogCategory>> {
/// Blog Categories Provider
///
/// Fetches all published blog categories from Frappe API.
/// Returns AsyncValue<List<BlogCategory>> (domain entities) for proper loading/error handling.
///
/// Example categories:
/// - Tin tức (News)
/// - Chuyên môn (Professional)
/// - Dự án (Projects)
/// - Khuyến mãi (Promotions)
const BlogCategoriesProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'blogCategoriesProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$blogCategoriesHash();
@$internal
@override
$FutureProviderElement<List<BlogCategory>> $createElement(
$ProviderPointer pointer,
) => $FutureProviderElement(pointer);
@override
FutureOr<List<BlogCategory>> create(Ref ref) {
return blogCategories(ref);
}
}
String _$blogCategoriesHash() => r'd87493142946be20ab309ea94d6173a8005b516e';