news page
This commit is contained in:
209
lib/features/news/domain/entities/news_article.dart
Normal file
209
lib/features/news/domain/entities/news_article.dart
Normal file
@@ -0,0 +1,209 @@
|
||||
/// Domain Entity: News Article
|
||||
///
|
||||
/// Pure business entity representing a news article or blog post.
|
||||
/// This entity is framework-independent and contains only business logic.
|
||||
library;
|
||||
|
||||
/// News Article Entity
|
||||
///
|
||||
/// Represents a news article/blog post in the app.
|
||||
/// Used for displaying news, tips, project showcases, and professional content.
|
||||
class NewsArticle {
|
||||
/// Unique article ID
|
||||
final String id;
|
||||
|
||||
/// Article title
|
||||
final String title;
|
||||
|
||||
/// Article excerpt/summary
|
||||
final String excerpt;
|
||||
|
||||
/// Full article content (optional, may load separately)
|
||||
final String? content;
|
||||
|
||||
/// Featured image URL
|
||||
final String imageUrl;
|
||||
|
||||
/// Article category
|
||||
final NewsCategory category;
|
||||
|
||||
/// Publication date
|
||||
final DateTime publishedDate;
|
||||
|
||||
/// View count
|
||||
final int viewCount;
|
||||
|
||||
/// Estimated reading time in minutes
|
||||
final int readingTimeMinutes;
|
||||
|
||||
/// Whether this is a featured article
|
||||
final bool isFeatured;
|
||||
|
||||
/// Author name (optional)
|
||||
final String? authorName;
|
||||
|
||||
/// Author avatar URL (optional)
|
||||
final String? authorAvatar;
|
||||
|
||||
/// Constructor
|
||||
const NewsArticle({
|
||||
required this.id,
|
||||
required this.title,
|
||||
required this.excerpt,
|
||||
this.content,
|
||||
required this.imageUrl,
|
||||
required this.category,
|
||||
required this.publishedDate,
|
||||
required this.viewCount,
|
||||
required this.readingTimeMinutes,
|
||||
this.isFeatured = false,
|
||||
this.authorName,
|
||||
this.authorAvatar,
|
||||
});
|
||||
|
||||
/// Get formatted publication date (dd/MM/yyyy)
|
||||
String get formattedDate {
|
||||
return '${publishedDate.day.toString().padLeft(2, '0')}/'
|
||||
'${publishedDate.month.toString().padLeft(2, '0')}/'
|
||||
'${publishedDate.year}';
|
||||
}
|
||||
|
||||
/// Get formatted view count (e.g., "2.3K")
|
||||
String get formattedViewCount {
|
||||
if (viewCount >= 1000) {
|
||||
return '${(viewCount / 1000).toStringAsFixed(1)}K';
|
||||
}
|
||||
return viewCount.toString();
|
||||
}
|
||||
|
||||
/// Get reading time display text
|
||||
String get readingTimeText => '$readingTimeMinutes phút đọc';
|
||||
|
||||
/// Copy with method for immutability
|
||||
NewsArticle copyWith({
|
||||
String? id,
|
||||
String? title,
|
||||
String? excerpt,
|
||||
String? content,
|
||||
String? imageUrl,
|
||||
NewsCategory? category,
|
||||
DateTime? publishedDate,
|
||||
int? viewCount,
|
||||
int? readingTimeMinutes,
|
||||
bool? isFeatured,
|
||||
String? authorName,
|
||||
String? authorAvatar,
|
||||
}) {
|
||||
return NewsArticle(
|
||||
id: id ?? this.id,
|
||||
title: title ?? this.title,
|
||||
excerpt: excerpt ?? this.excerpt,
|
||||
content: content ?? this.content,
|
||||
imageUrl: imageUrl ?? this.imageUrl,
|
||||
category: category ?? this.category,
|
||||
publishedDate: publishedDate ?? this.publishedDate,
|
||||
viewCount: viewCount ?? this.viewCount,
|
||||
readingTimeMinutes: readingTimeMinutes ?? this.readingTimeMinutes,
|
||||
isFeatured: isFeatured ?? this.isFeatured,
|
||||
authorName: authorName ?? this.authorName,
|
||||
authorAvatar: authorAvatar ?? this.authorAvatar,
|
||||
);
|
||||
}
|
||||
|
||||
/// Equality operator
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is NewsArticle &&
|
||||
other.id == id &&
|
||||
other.title == title &&
|
||||
other.excerpt == excerpt &&
|
||||
other.content == content &&
|
||||
other.imageUrl == imageUrl &&
|
||||
other.category == category &&
|
||||
other.publishedDate == publishedDate &&
|
||||
other.viewCount == viewCount &&
|
||||
other.readingTimeMinutes == readingTimeMinutes &&
|
||||
other.isFeatured == isFeatured &&
|
||||
other.authorName == authorName &&
|
||||
other.authorAvatar == authorAvatar;
|
||||
}
|
||||
|
||||
/// Hash code
|
||||
@override
|
||||
int get hashCode {
|
||||
return Object.hash(
|
||||
id,
|
||||
title,
|
||||
excerpt,
|
||||
content,
|
||||
imageUrl,
|
||||
category,
|
||||
publishedDate,
|
||||
viewCount,
|
||||
readingTimeMinutes,
|
||||
isFeatured,
|
||||
authorName,
|
||||
authorAvatar,
|
||||
);
|
||||
}
|
||||
|
||||
/// String representation
|
||||
@override
|
||||
String toString() {
|
||||
return 'NewsArticle(id: $id, title: $title, category: $category, '
|
||||
'publishedDate: $publishedDate, isFeatured: $isFeatured)';
|
||||
}
|
||||
}
|
||||
|
||||
/// News Category enum
|
||||
enum NewsCategory {
|
||||
/// General news
|
||||
news,
|
||||
|
||||
/// Professional/technical content
|
||||
professional,
|
||||
|
||||
/// Project showcases
|
||||
projects,
|
||||
|
||||
/// Events
|
||||
events,
|
||||
|
||||
/// Promotions
|
||||
promotions,
|
||||
}
|
||||
|
||||
/// Extension for News Category display
|
||||
extension NewsCategoryX on NewsCategory {
|
||||
String get displayName {
|
||||
switch (this) {
|
||||
case NewsCategory.news:
|
||||
return 'Tin tức';
|
||||
case NewsCategory.professional:
|
||||
return 'Chuyên môn';
|
||||
case NewsCategory.projects:
|
||||
return 'Dự án';
|
||||
case NewsCategory.events:
|
||||
return 'Sự kiện';
|
||||
case NewsCategory.promotions:
|
||||
return 'Khuyến mãi';
|
||||
}
|
||||
}
|
||||
|
||||
String get filterName {
|
||||
switch (this) {
|
||||
case NewsCategory.news:
|
||||
return 'news';
|
||||
case NewsCategory.professional:
|
||||
return 'professional';
|
||||
case NewsCategory.projects:
|
||||
return 'projects';
|
||||
case NewsCategory.events:
|
||||
return 'events';
|
||||
case NewsCategory.promotions:
|
||||
return 'promotions';
|
||||
}
|
||||
}
|
||||
}
|
||||
27
lib/features/news/domain/repositories/news_repository.dart
Normal file
27
lib/features/news/domain/repositories/news_repository.dart
Normal file
@@ -0,0 +1,27 @@
|
||||
/// Domain Repository Interface: News Repository
|
||||
///
|
||||
/// Defines the contract for news article data operations.
|
||||
/// This is an abstract interface following the Repository Pattern.
|
||||
library;
|
||||
|
||||
import 'package:worker/features/news/domain/entities/news_article.dart';
|
||||
|
||||
/// News Repository Interface
|
||||
///
|
||||
/// Provides methods to fetch and manage news articles.
|
||||
abstract class NewsRepository {
|
||||
/// Get all news articles
|
||||
Future<List<NewsArticle>> getAllArticles();
|
||||
|
||||
/// Get featured article
|
||||
Future<NewsArticle?> getFeaturedArticle();
|
||||
|
||||
/// Get articles by category
|
||||
Future<List<NewsArticle>> getArticlesByCategory(NewsCategory category);
|
||||
|
||||
/// Get a specific article by ID
|
||||
Future<NewsArticle?> getArticleById(String articleId);
|
||||
|
||||
/// Refresh articles from server
|
||||
Future<List<NewsArticle>> refreshArticles();
|
||||
}
|
||||
Reference in New Issue
Block a user