update news
This commit is contained in:
@@ -735,16 +735,3 @@ class _NewsDetailPageState extends ConsumerState<NewsDetailPage> {
|
||||
// );
|
||||
}
|
||||
}
|
||||
|
||||
/// Provider for getting article by ID
|
||||
final newsArticleByIdProvider = FutureProvider.family<NewsArticle?, String>((
|
||||
ref,
|
||||
id,
|
||||
) async {
|
||||
final articles = await ref.watch(newsArticlesProvider.future);
|
||||
try {
|
||||
return articles.firstWhere((article) => article.id == id);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -86,44 +86,9 @@ class _NewsListPageState extends ConsumerState<NewsListPage> {
|
||||
}
|
||||
|
||||
return SliverToBoxAdapter(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Section title "Nổi bật"
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: AppSpacing.md,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.star,
|
||||
size: 18,
|
||||
color: AppColors.primaryBlue,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
const Text(
|
||||
'Nổi bật',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF1E293B),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
|
||||
// Featured card
|
||||
FeaturedNewsCard(
|
||||
article: article,
|
||||
onTap: () => _onArticleTap(context, article),
|
||||
),
|
||||
|
||||
const SizedBox(height: 32),
|
||||
],
|
||||
child: FeaturedNewsCard(
|
||||
article: article,
|
||||
onTap: () => _onArticleTap(context, article),
|
||||
),
|
||||
);
|
||||
},
|
||||
@@ -137,10 +102,13 @@ class _NewsListPageState extends ConsumerState<NewsListPage> {
|
||||
const SliverToBoxAdapter(child: SizedBox.shrink()),
|
||||
),
|
||||
|
||||
const SliverToBoxAdapter(
|
||||
child: SizedBox(height: AppSpacing.xl),
|
||||
),
|
||||
// Latest News Section
|
||||
SliverToBoxAdapter(
|
||||
const SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||||
padding: EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
@@ -148,8 +116,8 @@ class _NewsListPageState extends ConsumerState<NewsListPage> {
|
||||
size: 18,
|
||||
color: AppColors.primaryBlue,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
const Text(
|
||||
SizedBox(width: 8),
|
||||
Text(
|
||||
'Mới nhất',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
|
||||
@@ -47,27 +47,52 @@ Future<NewsRepository> newsRepository(Ref ref) async {
|
||||
);
|
||||
}
|
||||
|
||||
/// News Articles Provider
|
||||
/// All News Articles Provider (Internal)
|
||||
///
|
||||
/// Fetches all news articles sorted by published date.
|
||||
/// Returns AsyncValue<List<NewsArticle>> for proper loading/error handling.
|
||||
/// Fetches ALL blog posts from Frappe API sorted by published date (latest first).
|
||||
/// This is the complete list used by both featured and latest articles providers.
|
||||
/// Do not use this provider directly in UI - use featuredArticle or newsArticles instead.
|
||||
@riverpod
|
||||
Future<List<NewsArticle>> newsArticles(Ref ref) async {
|
||||
final repository = await ref.watch(newsRepositoryProvider.future);
|
||||
return repository.getAllArticles();
|
||||
Future<List<NewsArticle>> _allNewsArticles(Ref ref) async {
|
||||
final remoteDataSource = await ref.watch(newsRemoteDataSourceProvider.future);
|
||||
|
||||
// Fetch blog posts from Frappe API
|
||||
final blogPosts = await remoteDataSource.getBlogPosts();
|
||||
|
||||
// Convert to NewsArticle entities
|
||||
final articles = blogPosts.map((post) => post.toEntity()).toList();
|
||||
|
||||
// Already sorted by published_on desc from API
|
||||
return articles;
|
||||
}
|
||||
|
||||
/// Featured Article Provider
|
||||
///
|
||||
/// Fetches the featured article for the top section.
|
||||
/// Returns AsyncValue<NewsArticle?> (null if no featured article).
|
||||
/// Returns the first article from the complete list.
|
||||
/// This is the latest published article that will be displayed prominently at the top.
|
||||
@riverpod
|
||||
Future<NewsArticle?> featuredArticle(Ref ref) async {
|
||||
final repository = await ref.watch(newsRepositoryProvider.future);
|
||||
return repository.getFeaturedArticle();
|
||||
final allArticles = await ref.watch(_allNewsArticlesProvider.future);
|
||||
|
||||
// Return first article if available (latest post)
|
||||
return allArticles.isNotEmpty ? allArticles.first : null;
|
||||
}
|
||||
|
||||
/// Selected News Category Provider
|
||||
/// News Articles Provider
|
||||
///
|
||||
/// Returns latest news articles EXCLUDING the first item (which is shown as featured).
|
||||
/// This ensures each article only appears once on the page.
|
||||
/// Returns AsyncValue<List<NewsArticle>> for proper loading/error handling.
|
||||
@riverpod
|
||||
Future<List<NewsArticle>> newsArticles(Ref ref) async {
|
||||
final allArticles = await ref.watch(_allNewsArticlesProvider.future);
|
||||
|
||||
// Return all articles except first (which is featured)
|
||||
// If only 0-1 articles, return empty list
|
||||
return allArticles.length > 1 ? allArticles.sublist(1) : [];
|
||||
}
|
||||
|
||||
/// Selected News Category Provider (Legacy - using enum)
|
||||
///
|
||||
/// Manages the currently selected category filter.
|
||||
/// null means "All" is selected (show all categories).
|
||||
@@ -90,32 +115,67 @@ class SelectedNewsCategory extends _$SelectedNewsCategory {
|
||||
}
|
||||
}
|
||||
|
||||
/// Filtered News Articles Provider
|
||||
/// Selected Category Name Provider
|
||||
///
|
||||
/// Returns news articles filtered by selected category.
|
||||
/// If no category is selected, returns all articles.
|
||||
/// Manages the currently selected blog category name (from Frappe API).
|
||||
/// null means "All" is selected (show all categories).
|
||||
///
|
||||
/// Examples: "tin-tức", "dự-án", "chuyên-môn", "khuyến-mãi"
|
||||
@riverpod
|
||||
Future<List<NewsArticle>> filteredNewsArticles(Ref ref) async {
|
||||
final selectedCategory = ref.watch(selectedNewsCategoryProvider);
|
||||
final repository = await ref.watch(newsRepositoryProvider.future);
|
||||
|
||||
// If no category selected, return all articles
|
||||
if (selectedCategory == null) {
|
||||
return repository.getAllArticles();
|
||||
class SelectedCategoryName extends _$SelectedCategoryName {
|
||||
@override
|
||||
String? build() {
|
||||
// Default: show all categories
|
||||
return null;
|
||||
}
|
||||
|
||||
// Filter by selected category
|
||||
return repository.getArticlesByCategory(selectedCategory);
|
||||
/// Set selected category by name
|
||||
void setCategoryName(String? categoryName) {
|
||||
state = categoryName;
|
||||
}
|
||||
|
||||
/// Clear selection (show all)
|
||||
void clearSelection() {
|
||||
state = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Filtered News Articles Provider
|
||||
///
|
||||
/// Returns news articles filtered by selected blog category name.
|
||||
/// Excludes the first article (which is shown as featured).
|
||||
/// If no category is selected, returns all articles except first.
|
||||
///
|
||||
/// The blog_category name from API is stored in article.tags[0] for filtering.
|
||||
@riverpod
|
||||
Future<List<NewsArticle>> filteredNewsArticles(Ref ref) async {
|
||||
final selectedCategoryName = ref.watch(selectedCategoryNameProvider);
|
||||
final allArticles = await ref.watch(_allNewsArticlesProvider.future);
|
||||
|
||||
// Get articles excluding first (which is featured)
|
||||
final articlesWithoutFeatured = allArticles.length > 1 ? allArticles.sublist(1) : <NewsArticle>[];
|
||||
|
||||
// If no category selected, return all articles except first
|
||||
if (selectedCategoryName == null) {
|
||||
return articlesWithoutFeatured;
|
||||
}
|
||||
|
||||
// Filter articles by blog_category name (stored in tags[0])
|
||||
return articlesWithoutFeatured.where((article) {
|
||||
// Check if article has tags and first tag matches selected category
|
||||
return article.tags.isNotEmpty && article.tags[0] == selectedCategoryName;
|
||||
}).toList();
|
||||
}
|
||||
|
||||
/// News Article by ID Provider
|
||||
///
|
||||
/// Fetches a specific article by ID.
|
||||
/// Fetches a specific article by ID from the Frappe API.
|
||||
/// Uses frappe.client.get endpoint to fetch the full blog post detail.
|
||||
/// Used for article detail page.
|
||||
@riverpod
|
||||
Future<NewsArticle?> newsArticleById(Ref ref, String articleId) async {
|
||||
final repository = await ref.watch(newsRepositoryProvider.future);
|
||||
return repository.getArticleById(articleId);
|
||||
return repository.getArticleByIdFromApi(articleId);
|
||||
}
|
||||
|
||||
/// Blog Categories Provider
|
||||
|
||||
@@ -170,9 +170,121 @@ final class NewsRepositoryProvider
|
||||
|
||||
String _$newsRepositoryHash() => r'8e66d847014926ad542e402874e52d35b00cdbcc';
|
||||
|
||||
/// All News Articles Provider (Internal)
|
||||
///
|
||||
/// Fetches ALL blog posts from Frappe API sorted by published date (latest first).
|
||||
/// This is the complete list used by both featured and latest articles providers.
|
||||
/// Do not use this provider directly in UI - use featuredArticle or newsArticles instead.
|
||||
|
||||
@ProviderFor(_allNewsArticles)
|
||||
const _allNewsArticlesProvider = _AllNewsArticlesProvider._();
|
||||
|
||||
/// All News Articles Provider (Internal)
|
||||
///
|
||||
/// Fetches ALL blog posts from Frappe API sorted by published date (latest first).
|
||||
/// This is the complete list used by both featured and latest articles providers.
|
||||
/// Do not use this provider directly in UI - use featuredArticle or newsArticles instead.
|
||||
|
||||
final class _AllNewsArticlesProvider
|
||||
extends
|
||||
$FunctionalProvider<
|
||||
AsyncValue<List<NewsArticle>>,
|
||||
List<NewsArticle>,
|
||||
FutureOr<List<NewsArticle>>
|
||||
>
|
||||
with
|
||||
$FutureModifier<List<NewsArticle>>,
|
||||
$FutureProvider<List<NewsArticle>> {
|
||||
/// All News Articles Provider (Internal)
|
||||
///
|
||||
/// Fetches ALL blog posts from Frappe API sorted by published date (latest first).
|
||||
/// This is the complete list used by both featured and latest articles providers.
|
||||
/// Do not use this provider directly in UI - use featuredArticle or newsArticles instead.
|
||||
const _AllNewsArticlesProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'_allNewsArticlesProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$_allNewsArticlesHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
$FutureProviderElement<List<NewsArticle>> $createElement(
|
||||
$ProviderPointer pointer,
|
||||
) => $FutureProviderElement(pointer);
|
||||
|
||||
@override
|
||||
FutureOr<List<NewsArticle>> create(Ref ref) {
|
||||
return _allNewsArticles(ref);
|
||||
}
|
||||
}
|
||||
|
||||
String _$_allNewsArticlesHash() => r'9ee5c1449f1a72710e801a6b4a9e5c72df842e61';
|
||||
|
||||
/// Featured Article Provider
|
||||
///
|
||||
/// Returns the first article from the complete list.
|
||||
/// This is the latest published article that will be displayed prominently at the top.
|
||||
|
||||
@ProviderFor(featuredArticle)
|
||||
const featuredArticleProvider = FeaturedArticleProvider._();
|
||||
|
||||
/// Featured Article Provider
|
||||
///
|
||||
/// Returns the first article from the complete list.
|
||||
/// This is the latest published article that will be displayed prominently at the top.
|
||||
|
||||
final class FeaturedArticleProvider
|
||||
extends
|
||||
$FunctionalProvider<
|
||||
AsyncValue<NewsArticle?>,
|
||||
NewsArticle?,
|
||||
FutureOr<NewsArticle?>
|
||||
>
|
||||
with $FutureModifier<NewsArticle?>, $FutureProvider<NewsArticle?> {
|
||||
/// Featured Article Provider
|
||||
///
|
||||
/// Returns the first article from the complete list.
|
||||
/// This is the latest published article that will be displayed prominently at the top.
|
||||
const FeaturedArticleProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'featuredArticleProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$featuredArticleHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
$FutureProviderElement<NewsArticle?> $createElement(
|
||||
$ProviderPointer pointer,
|
||||
) => $FutureProviderElement(pointer);
|
||||
|
||||
@override
|
||||
FutureOr<NewsArticle?> create(Ref ref) {
|
||||
return featuredArticle(ref);
|
||||
}
|
||||
}
|
||||
|
||||
String _$featuredArticleHash() => r'046567d4385aca2abe10767a98744c2c1cfafd78';
|
||||
|
||||
/// News Articles Provider
|
||||
///
|
||||
/// Fetches all news articles sorted by published date.
|
||||
/// Returns latest news articles EXCLUDING the first item (which is shown as featured).
|
||||
/// This ensures each article only appears once on the page.
|
||||
/// Returns AsyncValue<List<NewsArticle>> for proper loading/error handling.
|
||||
|
||||
@ProviderFor(newsArticles)
|
||||
@@ -180,7 +292,8 @@ const newsArticlesProvider = NewsArticlesProvider._();
|
||||
|
||||
/// News Articles Provider
|
||||
///
|
||||
/// Fetches all news articles sorted by published date.
|
||||
/// Returns latest news articles EXCLUDING the first item (which is shown as featured).
|
||||
/// This ensures each article only appears once on the page.
|
||||
/// Returns AsyncValue<List<NewsArticle>> for proper loading/error handling.
|
||||
|
||||
final class NewsArticlesProvider
|
||||
@@ -195,7 +308,8 @@ final class NewsArticlesProvider
|
||||
$FutureProvider<List<NewsArticle>> {
|
||||
/// News Articles Provider
|
||||
///
|
||||
/// Fetches all news articles sorted by published date.
|
||||
/// Returns latest news articles EXCLUDING the first item (which is shown as featured).
|
||||
/// This ensures each article only appears once on the page.
|
||||
/// Returns AsyncValue<List<NewsArticle>> for proper loading/error handling.
|
||||
const NewsArticlesProvider._()
|
||||
: super(
|
||||
@@ -223,62 +337,9 @@ final class NewsArticlesProvider
|
||||
}
|
||||
}
|
||||
|
||||
String _$newsArticlesHash() => r'789d916f1ce7d76f26429cfce97c65a71915edf3';
|
||||
String _$newsArticlesHash() => r'954f28885540368a095a3423f4f64c0f1ff0f47d';
|
||||
|
||||
/// Featured Article Provider
|
||||
///
|
||||
/// Fetches the featured article for the top section.
|
||||
/// Returns AsyncValue<NewsArticle?> (null if no featured article).
|
||||
|
||||
@ProviderFor(featuredArticle)
|
||||
const featuredArticleProvider = FeaturedArticleProvider._();
|
||||
|
||||
/// Featured Article Provider
|
||||
///
|
||||
/// Fetches the featured article for the top section.
|
||||
/// Returns AsyncValue<NewsArticle?> (null if no featured article).
|
||||
|
||||
final class FeaturedArticleProvider
|
||||
extends
|
||||
$FunctionalProvider<
|
||||
AsyncValue<NewsArticle?>,
|
||||
NewsArticle?,
|
||||
FutureOr<NewsArticle?>
|
||||
>
|
||||
with $FutureModifier<NewsArticle?>, $FutureProvider<NewsArticle?> {
|
||||
/// Featured Article Provider
|
||||
///
|
||||
/// Fetches the featured article for the top section.
|
||||
/// Returns AsyncValue<NewsArticle?> (null if no featured article).
|
||||
const FeaturedArticleProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'featuredArticleProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$featuredArticleHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
$FutureProviderElement<NewsArticle?> $createElement(
|
||||
$ProviderPointer pointer,
|
||||
) => $FutureProviderElement(pointer);
|
||||
|
||||
@override
|
||||
FutureOr<NewsArticle?> create(Ref ref) {
|
||||
return featuredArticle(ref);
|
||||
}
|
||||
}
|
||||
|
||||
String _$featuredArticleHash() => r'5fd7057d3f828d6f717b08d59561aa9637eb0097';
|
||||
|
||||
/// Selected News Category Provider
|
||||
/// Selected News Category Provider (Legacy - using enum)
|
||||
///
|
||||
/// Manages the currently selected category filter.
|
||||
/// null means "All" is selected (show all categories).
|
||||
@@ -286,13 +347,13 @@ String _$featuredArticleHash() => r'5fd7057d3f828d6f717b08d59561aa9637eb0097';
|
||||
@ProviderFor(SelectedNewsCategory)
|
||||
const selectedNewsCategoryProvider = SelectedNewsCategoryProvider._();
|
||||
|
||||
/// Selected News Category Provider
|
||||
/// Selected News Category Provider (Legacy - using enum)
|
||||
///
|
||||
/// Manages the currently selected category filter.
|
||||
/// null means "All" is selected (show all categories).
|
||||
final class SelectedNewsCategoryProvider
|
||||
extends $NotifierProvider<SelectedNewsCategory, NewsCategory?> {
|
||||
/// Selected News Category Provider
|
||||
/// Selected News Category Provider (Legacy - using enum)
|
||||
///
|
||||
/// Manages the currently selected category filter.
|
||||
/// null means "All" is selected (show all categories).
|
||||
@@ -326,7 +387,7 @@ final class SelectedNewsCategoryProvider
|
||||
String _$selectedNewsCategoryHash() =>
|
||||
r'f1dca9a5d7de94cac90494d94ce05b727e6e4d5f';
|
||||
|
||||
/// Selected News Category Provider
|
||||
/// Selected News Category Provider (Legacy - using enum)
|
||||
///
|
||||
/// Manages the currently selected category filter.
|
||||
/// null means "All" is selected (show all categories).
|
||||
@@ -350,18 +411,104 @@ abstract class _$SelectedNewsCategory extends $Notifier<NewsCategory?> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Selected Category Name Provider
|
||||
///
|
||||
/// Manages the currently selected blog category name (from Frappe API).
|
||||
/// null means "All" is selected (show all categories).
|
||||
///
|
||||
/// Examples: "tin-tức", "dự-án", "chuyên-môn", "khuyến-mãi"
|
||||
|
||||
@ProviderFor(SelectedCategoryName)
|
||||
const selectedCategoryNameProvider = SelectedCategoryNameProvider._();
|
||||
|
||||
/// Selected Category Name Provider
|
||||
///
|
||||
/// Manages the currently selected blog category name (from Frappe API).
|
||||
/// null means "All" is selected (show all categories).
|
||||
///
|
||||
/// Examples: "tin-tức", "dự-án", "chuyên-môn", "khuyến-mãi"
|
||||
final class SelectedCategoryNameProvider
|
||||
extends $NotifierProvider<SelectedCategoryName, String?> {
|
||||
/// Selected Category Name Provider
|
||||
///
|
||||
/// Manages the currently selected blog category name (from Frappe API).
|
||||
/// null means "All" is selected (show all categories).
|
||||
///
|
||||
/// Examples: "tin-tức", "dự-án", "chuyên-môn", "khuyến-mãi"
|
||||
const SelectedCategoryNameProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'selectedCategoryNameProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$selectedCategoryNameHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
SelectedCategoryName create() => SelectedCategoryName();
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(String? value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<String?>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$selectedCategoryNameHash() =>
|
||||
r'8dfbf490b986275e6ed9d7b423ae16f074c7fa36';
|
||||
|
||||
/// Selected Category Name Provider
|
||||
///
|
||||
/// Manages the currently selected blog category name (from Frappe API).
|
||||
/// null means "All" is selected (show all categories).
|
||||
///
|
||||
/// Examples: "tin-tức", "dự-án", "chuyên-môn", "khuyến-mãi"
|
||||
|
||||
abstract class _$SelectedCategoryName extends $Notifier<String?> {
|
||||
String? build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final created = build();
|
||||
final ref = this.ref as $Ref<String?, String?>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<String?, String?>,
|
||||
String?,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleValue(ref, created);
|
||||
}
|
||||
}
|
||||
|
||||
/// Filtered News Articles Provider
|
||||
///
|
||||
/// Returns news articles filtered by selected category.
|
||||
/// If no category is selected, returns all articles.
|
||||
/// Returns news articles filtered by selected blog category name.
|
||||
/// Excludes the first article (which is shown as featured).
|
||||
/// If no category is selected, returns all articles except first.
|
||||
///
|
||||
/// The blog_category name from API is stored in article.tags[0] for filtering.
|
||||
|
||||
@ProviderFor(filteredNewsArticles)
|
||||
const filteredNewsArticlesProvider = FilteredNewsArticlesProvider._();
|
||||
|
||||
/// Filtered News Articles Provider
|
||||
///
|
||||
/// Returns news articles filtered by selected category.
|
||||
/// If no category is selected, returns all articles.
|
||||
/// Returns news articles filtered by selected blog category name.
|
||||
/// Excludes the first article (which is shown as featured).
|
||||
/// If no category is selected, returns all articles except first.
|
||||
///
|
||||
/// The blog_category name from API is stored in article.tags[0] for filtering.
|
||||
|
||||
final class FilteredNewsArticlesProvider
|
||||
extends
|
||||
@@ -375,8 +522,11 @@ final class FilteredNewsArticlesProvider
|
||||
$FutureProvider<List<NewsArticle>> {
|
||||
/// Filtered News Articles Provider
|
||||
///
|
||||
/// Returns news articles filtered by selected category.
|
||||
/// If no category is selected, returns all articles.
|
||||
/// Returns news articles filtered by selected blog category name.
|
||||
/// Excludes the first article (which is shown as featured).
|
||||
/// If no category is selected, returns all articles except first.
|
||||
///
|
||||
/// The blog_category name from API is stored in article.tags[0] for filtering.
|
||||
const FilteredNewsArticlesProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
@@ -404,11 +554,12 @@ final class FilteredNewsArticlesProvider
|
||||
}
|
||||
|
||||
String _$filteredNewsArticlesHash() =>
|
||||
r'f5d6faa2d510eae188f12fa41d052eeb43e08cc9';
|
||||
r'52b823eabce0acfbef33cc85b5f31f3e9588df4f';
|
||||
|
||||
/// News Article by ID Provider
|
||||
///
|
||||
/// Fetches a specific article by ID.
|
||||
/// Fetches a specific article by ID from the Frappe API.
|
||||
/// Uses frappe.client.get endpoint to fetch the full blog post detail.
|
||||
/// Used for article detail page.
|
||||
|
||||
@ProviderFor(newsArticleById)
|
||||
@@ -416,7 +567,8 @@ const newsArticleByIdProvider = NewsArticleByIdFamily._();
|
||||
|
||||
/// News Article by ID Provider
|
||||
///
|
||||
/// Fetches a specific article by ID.
|
||||
/// Fetches a specific article by ID from the Frappe API.
|
||||
/// Uses frappe.client.get endpoint to fetch the full blog post detail.
|
||||
/// Used for article detail page.
|
||||
|
||||
final class NewsArticleByIdProvider
|
||||
@@ -429,7 +581,8 @@ final class NewsArticleByIdProvider
|
||||
with $FutureModifier<NewsArticle?>, $FutureProvider<NewsArticle?> {
|
||||
/// News Article by ID Provider
|
||||
///
|
||||
/// Fetches a specific article by ID.
|
||||
/// Fetches a specific article by ID from the Frappe API.
|
||||
/// Uses frappe.client.get endpoint to fetch the full blog post detail.
|
||||
/// Used for article detail page.
|
||||
const NewsArticleByIdProvider._({
|
||||
required NewsArticleByIdFamily super.from,
|
||||
@@ -475,11 +628,12 @@ final class NewsArticleByIdProvider
|
||||
}
|
||||
}
|
||||
|
||||
String _$newsArticleByIdHash() => r'f2b5ee4a3f7b67d0ee9e9c91169d740a9f250b50';
|
||||
String _$newsArticleByIdHash() => r'83e4790f0ebb80da5f0385f489ed2221fe769e3c';
|
||||
|
||||
/// News Article by ID Provider
|
||||
///
|
||||
/// Fetches a specific article by ID.
|
||||
/// Fetches a specific article by ID from the Frappe API.
|
||||
/// Uses frappe.client.get endpoint to fetch the full blog post detail.
|
||||
/// Used for article detail page.
|
||||
|
||||
final class NewsArticleByIdFamily extends $Family
|
||||
@@ -495,7 +649,8 @@ final class NewsArticleByIdFamily extends $Family
|
||||
|
||||
/// News Article by ID Provider
|
||||
///
|
||||
/// Fetches a specific article by ID.
|
||||
/// Fetches a specific article by ID from the Frappe API.
|
||||
/// Uses frappe.client.get endpoint to fetch the full blog post detail.
|
||||
/// Used for article detail page.
|
||||
|
||||
NewsArticleByIdProvider call(String articleId) =>
|
||||
|
||||
@@ -20,15 +20,15 @@ import 'package:worker/features/news/domain/entities/news_article.dart';
|
||||
/// - Category badge (primary blue)
|
||||
/// - Shadow and rounded corners
|
||||
class FeaturedNewsCard extends StatelessWidget {
|
||||
|
||||
/// Constructor
|
||||
const FeaturedNewsCard({super.key, required this.article, this.onTap});
|
||||
/// News article to display
|
||||
final NewsArticle article;
|
||||
|
||||
/// Callback when card is tapped
|
||||
final VoidCallback? onTap;
|
||||
|
||||
/// Constructor
|
||||
const FeaturedNewsCard({super.key, required this.article, this.onTap});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
@@ -126,17 +126,17 @@ class FeaturedNewsCard extends StatelessWidget {
|
||||
text: article.formattedDate,
|
||||
),
|
||||
|
||||
// Views
|
||||
_buildMetaItem(
|
||||
icon: Icons.visibility,
|
||||
text: '${article.formattedViewCount} lượt xem',
|
||||
),
|
||||
|
||||
// Reading time
|
||||
_buildMetaItem(
|
||||
icon: Icons.schedule,
|
||||
text: article.readingTimeText,
|
||||
),
|
||||
// // Views
|
||||
// _buildMetaItem(
|
||||
// icon: Icons.visibility,
|
||||
// text: '${article.formattedViewCount} lượt xem',
|
||||
// ),
|
||||
//
|
||||
// // Reading time
|
||||
// _buildMetaItem(
|
||||
// icon: Icons.schedule,
|
||||
// text: article.readingTimeText,
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -133,19 +133,19 @@ class NewsCard extends StatelessWidget {
|
||||
const SizedBox(width: 16),
|
||||
|
||||
// Views
|
||||
Icon(
|
||||
Icons.visibility,
|
||||
size: 12,
|
||||
color: const Color(0xFF64748B),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
'${article.formattedViewCount} lượt xem',
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
color: Color(0xFF64748B),
|
||||
),
|
||||
),
|
||||
// Icon(
|
||||
// Icons.visibility,
|
||||
// size: 12,
|
||||
// color: const Color(0xFF64748B),
|
||||
// ),
|
||||
// const SizedBox(width: 4),
|
||||
// Text(
|
||||
// '${article.formattedViewCount} lượt xem',
|
||||
// style: const TextStyle(
|
||||
// fontSize: 12,
|
||||
// color: Color(0xFF64748B),
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user