209 lines
4.9 KiB
Dart
209 lines
4.9 KiB
Dart
/// 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;
|
|
|
|
/// Tags/keywords for the article
|
|
final List<String> tags;
|
|
|
|
/// Like count
|
|
final int likeCount;
|
|
|
|
/// Comment count
|
|
final int commentCount;
|
|
|
|
/// Share count
|
|
final int shareCount;
|
|
|
|
/// 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,
|
|
this.tags = const [],
|
|
this.likeCount = 0,
|
|
this.commentCount = 0,
|
|
this.shareCount = 0,
|
|
});
|
|
|
|
/// 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,
|
|
List<String>? tags,
|
|
int? likeCount,
|
|
int? commentCount,
|
|
int? shareCount,
|
|
}) {
|
|
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,
|
|
tags: tags ?? this.tags,
|
|
likeCount: likeCount ?? this.likeCount,
|
|
commentCount: commentCount ?? this.commentCount,
|
|
shareCount: shareCount ?? this.shareCount,
|
|
);
|
|
}
|
|
|
|
/// Equality operator
|
|
@override
|
|
bool operator ==(Object other) {
|
|
if (identical(this, other)) return true;
|
|
|
|
return other is NewsArticle && other.id == id;
|
|
}
|
|
|
|
/// Hash code
|
|
@override
|
|
int get hashCode {
|
|
return id.hashCode;
|
|
}
|
|
|
|
/// 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';
|
|
}
|
|
}
|
|
}
|