update theme
This commit is contained in:
@@ -6,7 +6,6 @@ library;
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_html/flutter_html.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
@@ -17,7 +16,6 @@ import 'package:worker/core/constants/ui_constants.dart';
|
||||
import 'package:worker/core/theme/colors.dart';
|
||||
import 'package:worker/features/news/domain/entities/news_article.dart';
|
||||
import 'package:worker/features/news/presentation/providers/news_provider.dart';
|
||||
import 'package:worker/features/news/presentation/widgets/highlight_box.dart';
|
||||
import 'package:worker/features/news/presentation/widgets/related_article_card.dart';
|
||||
|
||||
/// News Detail Page
|
||||
@@ -49,49 +47,53 @@ class _NewsDetailPageState extends ConsumerState<NewsDetailPage> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final articleAsync = ref.watch(newsArticleByIdProvider(widget.articleId));
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
backgroundColor: colorScheme.surface,
|
||||
appBar: _buildAppBar(context),
|
||||
body: articleAsync.when(
|
||||
data: (article) {
|
||||
if (article == null) {
|
||||
return _buildNotFoundState();
|
||||
return _buildNotFoundState(context);
|
||||
}
|
||||
return _buildContent(context, article);
|
||||
},
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
error: (error, stack) => _buildErrorState(error.toString()),
|
||||
error: (error, stack) => _buildErrorState(context, error.toString()),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Build AppBar
|
||||
PreferredSizeWidget _buildAppBar(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return AppBar(
|
||||
backgroundColor: AppColors.white,
|
||||
backgroundColor: colorScheme.surface,
|
||||
elevation: AppBarSpecs.elevation,
|
||||
title: Text(
|
||||
'Chi tiết bài viết',
|
||||
style: const TextStyle(color: Colors.black),
|
||||
style: TextStyle(color: colorScheme.onSurface),
|
||||
),
|
||||
centerTitle: false,
|
||||
leading: IconButton(
|
||||
icon: const FaIcon(FontAwesomeIcons.arrowLeft, color: Colors.black, size: 20),
|
||||
icon: FaIcon(FontAwesomeIcons.arrowLeft, color: colorScheme.onSurface, size: 20),
|
||||
onPressed: () => context.pop(),
|
||||
),
|
||||
actions: [
|
||||
// Share button
|
||||
IconButton(
|
||||
icon: const FaIcon(FontAwesomeIcons.shareNodes, color: Colors.black, size: 20),
|
||||
icon: FaIcon(FontAwesomeIcons.shareNodes, color: colorScheme.onSurface, size: 20),
|
||||
onPressed: _onShareTap,
|
||||
),
|
||||
// Bookmark button
|
||||
IconButton(
|
||||
icon: FaIcon(
|
||||
_isBookmarked ? FontAwesomeIcons.solidBookmark : FontAwesomeIcons.bookmark,
|
||||
color: _isBookmarked ? AppColors.warning : Colors.black,
|
||||
// Keep AppColors.warning for bookmarked state - semantic status color
|
||||
color: _isBookmarked ? AppColors.warning : colorScheme.onSurface,
|
||||
size: 20,
|
||||
),
|
||||
onPressed: _onBookmarkTap,
|
||||
@@ -103,6 +105,7 @@ class _NewsDetailPageState extends ConsumerState<NewsDetailPage> {
|
||||
|
||||
/// Build content
|
||||
Widget _buildContent(BuildContext context, NewsArticle article) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final relatedArticles = ref
|
||||
.watch(filteredNewsArticlesProvider)
|
||||
.value
|
||||
@@ -122,16 +125,16 @@ class _NewsDetailPageState extends ConsumerState<NewsDetailPage> {
|
||||
fit: BoxFit.cover,
|
||||
placeholder: (context, url) => Container(
|
||||
height: 250,
|
||||
color: AppColors.grey100,
|
||||
color: colorScheme.surfaceContainerHighest,
|
||||
child: const Center(child: CircularProgressIndicator()),
|
||||
),
|
||||
errorWidget: (context, url, error) => Container(
|
||||
height: 250,
|
||||
color: AppColors.grey100,
|
||||
child: const FaIcon(
|
||||
color: colorScheme.surfaceContainerHighest,
|
||||
child: FaIcon(
|
||||
FontAwesomeIcons.image,
|
||||
size: 48,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -143,17 +146,17 @@ class _NewsDetailPageState extends ConsumerState<NewsDetailPage> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Metadata
|
||||
_buildMetadata(article),
|
||||
_buildMetadata(context, article),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Title
|
||||
Text(
|
||||
article.title,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Color(0xFF1E293B),
|
||||
color: colorScheme.onSurface,
|
||||
height: 1.3,
|
||||
),
|
||||
),
|
||||
@@ -171,29 +174,29 @@ class _NewsDetailPageState extends ConsumerState<NewsDetailPage> {
|
||||
padding: HtmlPaddings.zero,
|
||||
fontSize: FontSize(16),
|
||||
lineHeight: const LineHeight(1.7),
|
||||
color: const Color(0xFF1E293B),
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
"h2": Style(
|
||||
fontSize: FontSize(20),
|
||||
fontWeight: FontWeight.w600,
|
||||
color: const Color(0xFF1E293B),
|
||||
color: colorScheme.onSurface,
|
||||
margin: Margins.only(top: 32, bottom: 16),
|
||||
),
|
||||
"h3": Style(
|
||||
fontSize: FontSize(18),
|
||||
fontWeight: FontWeight.w600,
|
||||
color: const Color(0xFF1E293B),
|
||||
color: colorScheme.onSurface,
|
||||
margin: Margins.only(top: 24, bottom: 12),
|
||||
),
|
||||
"p": Style(
|
||||
fontSize: FontSize(16),
|
||||
color: const Color(0xFF1E293B),
|
||||
color: colorScheme.onSurface,
|
||||
lineHeight: const LineHeight(1.7),
|
||||
margin: Margins.only(bottom: 16),
|
||||
),
|
||||
"strong": Style(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: const Color(0xFF1E293B),
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
"img": Style(
|
||||
margin: Margins.symmetric(vertical: 16),
|
||||
@@ -206,14 +209,14 @@ class _NewsDetailPageState extends ConsumerState<NewsDetailPage> {
|
||||
),
|
||||
"li": Style(
|
||||
fontSize: FontSize(16),
|
||||
color: const Color(0xFF1E293B),
|
||||
color: colorScheme.onSurface,
|
||||
lineHeight: const LineHeight(1.5),
|
||||
margin: Margins.only(bottom: 8),
|
||||
),
|
||||
"blockquote": Style(
|
||||
backgroundColor: const Color(0xFFF0F9FF),
|
||||
border: const Border(
|
||||
left: BorderSide(color: AppColors.primaryBlue, width: 4),
|
||||
backgroundColor: colorScheme.primaryContainer,
|
||||
border: Border(
|
||||
left: BorderSide(color: colorScheme.primary, width: 4),
|
||||
),
|
||||
padding: HtmlPaddings.all(16),
|
||||
margin: Margins.symmetric(vertical: 24),
|
||||
@@ -235,18 +238,18 @@ class _NewsDetailPageState extends ConsumerState<NewsDetailPage> {
|
||||
const SizedBox(height: 32),
|
||||
|
||||
// Tags Section
|
||||
if (article.tags.isNotEmpty) _buildTagsSection(article.tags),
|
||||
if (article.tags.isNotEmpty) _buildTagsSection(context, article.tags),
|
||||
|
||||
const SizedBox(height: 32),
|
||||
|
||||
// Social Actions
|
||||
_buildSocialActions(article),
|
||||
_buildSocialActions(context, article),
|
||||
|
||||
const SizedBox(height: 32),
|
||||
|
||||
// Related Articles
|
||||
if (relatedArticles != null && relatedArticles.isNotEmpty)
|
||||
_buildRelatedArticles(relatedArticles),
|
||||
_buildRelatedArticles(context, relatedArticles),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -256,7 +259,9 @@ class _NewsDetailPageState extends ConsumerState<NewsDetailPage> {
|
||||
}
|
||||
|
||||
/// Build metadata
|
||||
Widget _buildMetadata(NewsArticle article) {
|
||||
Widget _buildMetadata(BuildContext context, NewsArticle article) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Row(
|
||||
spacing: 16,
|
||||
children: [
|
||||
@@ -264,27 +269,28 @@ class _NewsDetailPageState extends ConsumerState<NewsDetailPage> {
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.primaryBlue,
|
||||
color: colorScheme.primary,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Text(
|
||||
article.category.displayName,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white,
|
||||
color: colorScheme.onPrimary,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Date
|
||||
_buildMetaItem(FontAwesomeIcons.calendar, article.formattedDate),
|
||||
_buildMetaItem(context, FontAwesomeIcons.calendar, article.formattedDate),
|
||||
|
||||
// Reading time
|
||||
// _buildMetaItem(Icons.schedule, article.readingTimeText),
|
||||
// _buildMetaItem(context, Icons.schedule, article.readingTimeText),
|
||||
|
||||
// Views
|
||||
// _buildMetaItem(
|
||||
// context,
|
||||
// Icons.visibility,
|
||||
// '${article.formattedViewCount} lượt xem',
|
||||
// ),
|
||||
@@ -293,37 +299,41 @@ class _NewsDetailPageState extends ConsumerState<NewsDetailPage> {
|
||||
}
|
||||
|
||||
/// Build metadata item
|
||||
Widget _buildMetaItem(IconData icon, String text) {
|
||||
Widget _buildMetaItem(BuildContext context, IconData icon, String text) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
FaIcon(icon, size: 12, color: const Color(0xFF64748B)),
|
||||
FaIcon(icon, size: 12, color: colorScheme.onSurfaceVariant),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
text,
|
||||
style: const TextStyle(fontSize: 12, color: Color(0xFF64748B)),
|
||||
style: TextStyle(fontSize: 12, color: colorScheme.onSurfaceVariant),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// Build tags section
|
||||
Widget _buildTagsSection(List<String> tags) {
|
||||
Widget _buildTagsSection(BuildContext context, List<String> tags) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF8FAFC),
|
||||
color: colorScheme.surfaceContainerLowest,
|
||||
borderRadius: BorderRadius.circular(AppRadius.lg),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
Text(
|
||||
'Thẻ liên quan',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF1E293B),
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
@@ -338,15 +348,15 @@ class _NewsDetailPageState extends ConsumerState<NewsDetailPage> {
|
||||
vertical: 4,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
border: Border.all(color: const Color(0xFFE2E8F0)),
|
||||
color: colorScheme.surface,
|
||||
border: Border.all(color: colorScheme.outlineVariant),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Text(
|
||||
tag,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Color(0xFF64748B),
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -359,84 +369,96 @@ class _NewsDetailPageState extends ConsumerState<NewsDetailPage> {
|
||||
}
|
||||
|
||||
/// Build social actions section
|
||||
Widget _buildSocialActions(NewsArticle article) {
|
||||
Widget _buildSocialActions(BuildContext context, NewsArticle article) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: const BoxDecoration(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.symmetric(
|
||||
horizontal: BorderSide(color: Color(0xFFE2E8F0)),
|
||||
horizontal: BorderSide(color: colorScheme.outlineVariant),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
_buildActionButton(
|
||||
context,
|
||||
icon: _isLiked ? FontAwesomeIcons.solidHeart : FontAwesomeIcons.heart,
|
||||
onPressed: _onLikeTap,
|
||||
color: _isLiked ? Colors.red : null,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
_buildActionButton(
|
||||
context,
|
||||
icon: _isBookmarked ? FontAwesomeIcons.solidBookmark : FontAwesomeIcons.bookmark,
|
||||
onPressed: _onBookmarkTap,
|
||||
// Keep AppColors.warning for bookmarked state - semantic status color
|
||||
color: _isBookmarked ? AppColors.warning : null,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
_buildActionButton(icon: FontAwesomeIcons.shareNodes, onPressed: _onShareTap),
|
||||
_buildActionButton(context, icon: FontAwesomeIcons.shareNodes, onPressed: _onShareTap),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Build stat item
|
||||
Widget _buildStatItem(IconData icon, String text) {
|
||||
Widget _buildStatItem(BuildContext context, IconData icon, String text) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(icon, size: 14, color: const Color(0xFF64748B)),
|
||||
Icon(icon, size: 14, color: colorScheme.onSurfaceVariant),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
text,
|
||||
style: const TextStyle(fontSize: 14, color: Color(0xFF64748B)),
|
||||
style: TextStyle(fontSize: 14, color: colorScheme.onSurfaceVariant),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// Build action button
|
||||
Widget _buildActionButton({
|
||||
Widget _buildActionButton(
|
||||
BuildContext context, {
|
||||
required IconData icon,
|
||||
required VoidCallback onPressed,
|
||||
Color? color,
|
||||
}) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return OutlinedButton(
|
||||
onPressed: onPressed,
|
||||
style: OutlinedButton.styleFrom(
|
||||
padding: const EdgeInsets.all(12),
|
||||
side: BorderSide(color: color ?? const Color(0xFFE2E8F0), width: 2),
|
||||
side: BorderSide(color: color ?? colorScheme.outlineVariant, width: 2),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||
),
|
||||
child: FaIcon(icon, size: 18, color: color ?? const Color(0xFF64748B)),
|
||||
child: FaIcon(icon, size: 18, color: color ?? colorScheme.onSurfaceVariant),
|
||||
);
|
||||
}
|
||||
|
||||
/// Build related articles section
|
||||
Widget _buildRelatedArticles(List<NewsArticle> articles) {
|
||||
Widget _buildRelatedArticles(BuildContext context, List<NewsArticle> articles) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF8FAFC),
|
||||
color: colorScheme.surfaceContainerLowest,
|
||||
borderRadius: BorderRadius.circular(AppRadius.lg),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
Text(
|
||||
'Bài viết liên quan',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF1E293B),
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
@@ -455,25 +477,27 @@ class _NewsDetailPageState extends ConsumerState<NewsDetailPage> {
|
||||
}
|
||||
|
||||
/// Build not found state
|
||||
Widget _buildNotFoundState() {
|
||||
Widget _buildNotFoundState(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
FaIcon(FontAwesomeIcons.fileLines, size: 64, color: AppColors.grey500),
|
||||
FaIcon(FontAwesomeIcons.fileLines, size: 64, color: colorScheme.onSurfaceVariant),
|
||||
const SizedBox(height: 16),
|
||||
const Text(
|
||||
Text(
|
||||
'Không tìm thấy bài viết',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF1E293B),
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const Text(
|
||||
Text(
|
||||
'Bài viết này không tồn tại hoặc đã bị xóa',
|
||||
style: TextStyle(fontSize: 14, color: Color(0xFF64748B)),
|
||||
style: TextStyle(fontSize: 14, color: colorScheme.onSurfaceVariant),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
@@ -487,19 +511,22 @@ class _NewsDetailPageState extends ConsumerState<NewsDetailPage> {
|
||||
}
|
||||
|
||||
/// Build error state
|
||||
Widget _buildErrorState(String error) {
|
||||
Widget _buildErrorState(BuildContext context, String error) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
// Keep AppColors.danger for error state - semantic status color
|
||||
FaIcon(FontAwesomeIcons.circleExclamation, size: 64, color: AppColors.danger),
|
||||
const SizedBox(height: 16),
|
||||
const Text(
|
||||
Text(
|
||||
'Không thể tải bài viết',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF1E293B),
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
@@ -507,7 +534,7 @@ class _NewsDetailPageState extends ConsumerState<NewsDetailPage> {
|
||||
padding: const EdgeInsets.symmetric(horizontal: 32),
|
||||
child: Text(
|
||||
error,
|
||||
style: const TextStyle(fontSize: 14, color: Color(0xFF64748B)),
|
||||
style: TextStyle(fontSize: 14, color: colorScheme.onSurfaceVariant),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -38,6 +38,8 @@ class _NewsListPageState extends ConsumerState<NewsListPage> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
// Watch providers
|
||||
final featuredArticleAsync = ref.watch(featuredArticleProvider);
|
||||
final newsArticlesAsync = ref.watch(newsArticlesProvider);
|
||||
@@ -52,7 +54,7 @@ class _NewsListPageState extends ConsumerState<NewsListPage> {
|
||||
});
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
backgroundColor: colorScheme.surface,
|
||||
appBar: _buildAppBar(context),
|
||||
body: RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
@@ -107,23 +109,23 @@ class _NewsListPageState extends ConsumerState<NewsListPage> {
|
||||
child: SizedBox(height: AppSpacing.xl),
|
||||
),
|
||||
// Latest News Section
|
||||
const SliverToBoxAdapter(
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||||
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||||
child: Row(
|
||||
children: [
|
||||
FaIcon(
|
||||
FontAwesomeIcons.newspaper,
|
||||
size: 16,
|
||||
color: AppColors.primaryBlue,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'Mới nhất',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF1E293B),
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -173,13 +175,15 @@ class _NewsListPageState extends ConsumerState<NewsListPage> {
|
||||
|
||||
/// Build standard AppBar
|
||||
PreferredSizeWidget _buildAppBar(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return AppBar(
|
||||
backgroundColor: AppColors.white,
|
||||
backgroundColor: colorScheme.surface,
|
||||
elevation: AppBarSpecs.elevation,
|
||||
title: const Text(
|
||||
title: Text(
|
||||
'Tin tức & chuyên môn',
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
color: colorScheme.onSurface,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
@@ -191,24 +195,26 @@ class _NewsListPageState extends ConsumerState<NewsListPage> {
|
||||
|
||||
/// Build empty state
|
||||
Widget _buildEmptyState() {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
FaIcon(FontAwesomeIcons.newspaper, size: 64, color: AppColors.grey500),
|
||||
FaIcon(FontAwesomeIcons.newspaper, size: 64, color: colorScheme.onSurfaceVariant),
|
||||
const SizedBox(height: 16),
|
||||
const Text(
|
||||
Text(
|
||||
'Chưa có tin tức',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF1E293B),
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const Text(
|
||||
Text(
|
||||
'Hãy quay lại sau để xem các bài viết mới',
|
||||
style: TextStyle(fontSize: 14, color: Color(0xFF64748B)),
|
||||
style: TextStyle(fontSize: 14, color: colorScheme.onSurfaceVariant),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
@@ -218,18 +224,21 @@ class _NewsListPageState extends ConsumerState<NewsListPage> {
|
||||
|
||||
/// Build error state
|
||||
Widget _buildErrorState(String error) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
// Keep AppColors.danger for error state - semantic status color
|
||||
FaIcon(FontAwesomeIcons.circleExclamation, size: 64, color: AppColors.danger),
|
||||
const SizedBox(height: 16),
|
||||
const Text(
|
||||
Text(
|
||||
'Không thể tải tin tức',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF1E293B),
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
@@ -237,7 +246,7 @@ class _NewsListPageState extends ConsumerState<NewsListPage> {
|
||||
padding: const EdgeInsets.symmetric(horizontal: 32),
|
||||
child: Text(
|
||||
error,
|
||||
style: const TextStyle(fontSize: 14, color: Color(0xFF64748B)),
|
||||
style: TextStyle(fontSize: 14, color: colorScheme.onSurfaceVariant),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -9,7 +9,6 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:worker/core/constants/ui_constants.dart';
|
||||
import 'package:worker/core/theme/colors.dart';
|
||||
import 'package:worker/features/news/domain/entities/blog_category.dart';
|
||||
import 'package:worker/features/news/presentation/providers/news_provider.dart';
|
||||
|
||||
@@ -42,14 +41,14 @@ class CategoryFilterChips extends ConsumerWidget {
|
||||
final categoriesAsync = ref.watch(blogCategoriesProvider);
|
||||
|
||||
return categoriesAsync.when(
|
||||
data: (categories) => _buildCategoryChips(categories),
|
||||
loading: () => _buildLoadingState(),
|
||||
error: (error, stack) => _buildErrorState(error, ref),
|
||||
data: (categories) => _buildCategoryChips(context, categories),
|
||||
loading: () => _buildLoadingState(context),
|
||||
error: (error, stack) => _buildErrorState(context, error, ref),
|
||||
);
|
||||
}
|
||||
|
||||
/// Build category chips with data
|
||||
Widget _buildCategoryChips(List<BlogCategory> categories) {
|
||||
Widget _buildCategoryChips(BuildContext context, List<BlogCategory> categories) {
|
||||
return SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||||
@@ -57,6 +56,7 @@ class CategoryFilterChips extends ConsumerWidget {
|
||||
children: [
|
||||
// "Tất cả" chip
|
||||
_buildCategoryChip(
|
||||
context,
|
||||
label: 'Tất cả',
|
||||
isSelected: selectedCategoryName == null,
|
||||
onTap: () => onCategorySelected(null),
|
||||
@@ -69,6 +69,7 @@ class CategoryFilterChips extends ConsumerWidget {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(right: AppSpacing.sm),
|
||||
child: _buildCategoryChip(
|
||||
context,
|
||||
label: category.title,
|
||||
isSelected: selectedCategoryName == category.name,
|
||||
onTap: () => onCategorySelected(category.name),
|
||||
@@ -81,7 +82,9 @@ class CategoryFilterChips extends ConsumerWidget {
|
||||
}
|
||||
|
||||
/// Build loading state with shimmer placeholders
|
||||
Widget _buildLoadingState() {
|
||||
Widget _buildLoadingState(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||||
@@ -93,7 +96,7 @@ class CategoryFilterChips extends ConsumerWidget {
|
||||
width: 80,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.grey100,
|
||||
color: colorScheme.surfaceContainerHighest,
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
),
|
||||
),
|
||||
@@ -104,7 +107,9 @@ class CategoryFilterChips extends ConsumerWidget {
|
||||
}
|
||||
|
||||
/// Build error state with retry
|
||||
Widget _buildErrorState(Object error, WidgetRef ref) {
|
||||
Widget _buildErrorState(BuildContext context, Object error, WidgetRef ref) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||||
@@ -116,25 +121,25 @@ class CategoryFilterChips extends ConsumerWidget {
|
||||
vertical: AppSpacing.sm,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.grey100,
|
||||
color: colorScheme.surfaceContainerHighest,
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
FaIcon(FontAwesomeIcons.circleExclamation, size: 16, color: AppColors.grey500),
|
||||
FaIcon(FontAwesomeIcons.circleExclamation, size: 16, color: colorScheme.onSurfaceVariant),
|
||||
const SizedBox(width: AppSpacing.xs),
|
||||
Text(
|
||||
'Lỗi tải danh mục',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: AppSpacing.xs),
|
||||
GestureDetector(
|
||||
onTap: () => ref.refresh(blogCategoriesProvider),
|
||||
child: FaIcon(FontAwesomeIcons.arrowsRotate, size: 14, color: AppColors.primaryBlue),
|
||||
child: FaIcon(FontAwesomeIcons.arrowsRotate, size: 14, color: colorScheme.primary),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -145,11 +150,14 @@ class CategoryFilterChips extends ConsumerWidget {
|
||||
}
|
||||
|
||||
/// Build individual category chip
|
||||
Widget _buildCategoryChip({
|
||||
Widget _buildCategoryChip(
|
||||
BuildContext context, {
|
||||
required String label,
|
||||
required bool isSelected,
|
||||
required VoidCallback onTap,
|
||||
}) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
@@ -158,7 +166,7 @@ class CategoryFilterChips extends ConsumerWidget {
|
||||
vertical: AppSpacing.sm,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected ? AppColors.primaryBlue : AppColors.grey100,
|
||||
color: isSelected ? colorScheme.primary : colorScheme.surfaceContainerHighest,
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
),
|
||||
child: Text(
|
||||
@@ -166,7 +174,7 @@ class CategoryFilterChips extends ConsumerWidget {
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: isSelected ? Colors.white : AppColors.grey500,
|
||||
color: isSelected ? colorScheme.onPrimary : colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -8,7 +8,6 @@ import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:worker/core/constants/ui_constants.dart';
|
||||
import 'package:worker/core/theme/colors.dart';
|
||||
import 'package:worker/features/news/domain/entities/news_article.dart';
|
||||
|
||||
/// Featured News Card
|
||||
@@ -32,14 +31,16 @@ class FeaturedNewsCard extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(AppRadius.xl),
|
||||
border: Border.all(color: const Color(0xFFE2E8F0)),
|
||||
border: Border.all(color: colorScheme.outlineVariant),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.08),
|
||||
@@ -63,16 +64,16 @@ class FeaturedNewsCard extends StatelessWidget {
|
||||
fit: BoxFit.cover,
|
||||
placeholder: (context, url) => Container(
|
||||
height: 200,
|
||||
color: AppColors.grey100,
|
||||
color: colorScheme.surfaceContainerHighest,
|
||||
child: const Center(child: CircularProgressIndicator()),
|
||||
),
|
||||
errorWidget: (context, url, error) => Container(
|
||||
height: 200,
|
||||
color: AppColors.grey100,
|
||||
child: const FaIcon(
|
||||
color: colorScheme.surfaceContainerHighest,
|
||||
child: FaIcon(
|
||||
FontAwesomeIcons.image,
|
||||
size: 48,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -87,10 +88,10 @@ class FeaturedNewsCard extends StatelessWidget {
|
||||
// Title
|
||||
Text(
|
||||
article.title,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF1E293B),
|
||||
color: colorScheme.onSurface,
|
||||
height: 1.4,
|
||||
),
|
||||
),
|
||||
@@ -100,9 +101,9 @@ class FeaturedNewsCard extends StatelessWidget {
|
||||
// Excerpt
|
||||
Text(
|
||||
article.excerpt,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Color(0xFF64748B),
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
height: 1.5,
|
||||
),
|
||||
maxLines: 3,
|
||||
@@ -123,18 +124,21 @@ class FeaturedNewsCard extends StatelessWidget {
|
||||
children: [
|
||||
// Date
|
||||
_buildMetaItem(
|
||||
context,
|
||||
icon: FontAwesomeIcons.calendar,
|
||||
text: article.formattedDate,
|
||||
),
|
||||
|
||||
// // Views
|
||||
// _buildMetaItem(
|
||||
// context,
|
||||
// icon: Icons.visibility,
|
||||
// text: '${article.formattedViewCount} lượt xem',
|
||||
// ),
|
||||
//
|
||||
// // Reading time
|
||||
// _buildMetaItem(
|
||||
// context,
|
||||
// icon: Icons.schedule,
|
||||
// text: article.readingTimeText,
|
||||
// ),
|
||||
@@ -149,15 +153,15 @@ class FeaturedNewsCard extends StatelessWidget {
|
||||
vertical: 4,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.primaryBlue,
|
||||
color: colorScheme.primary,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Text(
|
||||
article.category.displayName,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.white,
|
||||
color: colorScheme.onPrimary,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -173,15 +177,17 @@ class FeaturedNewsCard extends StatelessWidget {
|
||||
}
|
||||
|
||||
/// Build metadata item
|
||||
Widget _buildMetaItem({required IconData icon, required String text}) {
|
||||
Widget _buildMetaItem(BuildContext context, {required IconData icon, required String text}) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
FaIcon(icon, size: 12, color: const Color(0xFF64748B)),
|
||||
FaIcon(icon, size: 12, color: colorScheme.onSurfaceVariant),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
text,
|
||||
style: const TextStyle(fontSize: 12, color: Color(0xFF64748B)),
|
||||
style: TextStyle(fontSize: 12, color: colorScheme.onSurfaceVariant),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
@@ -8,7 +8,6 @@ import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:worker/core/constants/ui_constants.dart';
|
||||
import 'package:worker/core/theme/colors.dart';
|
||||
import 'package:worker/features/news/domain/entities/news_article.dart';
|
||||
|
||||
/// News Card
|
||||
@@ -31,15 +30,17 @@ class NewsCard extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(bottom: AppSpacing.md),
|
||||
padding: const EdgeInsets.all(AppSpacing.md),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(AppRadius.card),
|
||||
border: Border.all(color: const Color(0xFFE2E8F0)),
|
||||
border: Border.all(color: colorScheme.outlineVariant),
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -55,7 +56,7 @@ class NewsCard extends StatelessWidget {
|
||||
placeholder: (context, url) => Container(
|
||||
width: 80,
|
||||
height: 80,
|
||||
color: AppColors.grey100,
|
||||
color: colorScheme.surfaceContainerHighest,
|
||||
child: const Center(
|
||||
child: SizedBox(
|
||||
width: 20,
|
||||
@@ -67,11 +68,11 @@ class NewsCard extends StatelessWidget {
|
||||
errorWidget: (context, url, error) => Container(
|
||||
width: 80,
|
||||
height: 80,
|
||||
color: AppColors.grey100,
|
||||
child: const FaIcon(
|
||||
color: colorScheme.surfaceContainerHighest,
|
||||
child: FaIcon(
|
||||
FontAwesomeIcons.image,
|
||||
size: 24,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -87,10 +88,10 @@ class NewsCard extends StatelessWidget {
|
||||
// Title (max 2 lines)
|
||||
Text(
|
||||
article.title,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF1E293B),
|
||||
color: colorScheme.onSurface,
|
||||
height: 1.3,
|
||||
),
|
||||
maxLines: 2,
|
||||
@@ -102,9 +103,9 @@ class NewsCard extends StatelessWidget {
|
||||
// Excerpt (max 2 lines)
|
||||
Text(
|
||||
article.excerpt,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Color(0xFF64748B),
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
height: 1.4,
|
||||
),
|
||||
maxLines: 2,
|
||||
@@ -117,17 +118,17 @@ class NewsCard extends StatelessWidget {
|
||||
Row(
|
||||
children: [
|
||||
// Date
|
||||
const FaIcon(
|
||||
FaIcon(
|
||||
FontAwesomeIcons.calendar,
|
||||
size: 12,
|
||||
color: Color(0xFF64748B),
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
article.formattedDate,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Color(0xFF64748B),
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -137,14 +138,14 @@ class NewsCard extends StatelessWidget {
|
||||
// Icon(
|
||||
// Icons.visibility,
|
||||
// size: 12,
|
||||
// color: const Color(0xFF64748B),
|
||||
// color: colorScheme.onSurfaceVariant,
|
||||
// ),
|
||||
// const SizedBox(width: 4),
|
||||
// Text(
|
||||
// '${article.formattedViewCount} lượt xem',
|
||||
// style: const TextStyle(
|
||||
// style: TextStyle(
|
||||
// fontSize: 12,
|
||||
// color: Color(0xFF64748B),
|
||||
// color: colorScheme.onSurfaceVariant,
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
|
||||
@@ -8,7 +8,6 @@ import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:worker/core/constants/ui_constants.dart';
|
||||
import 'package:worker/core/theme/colors.dart';
|
||||
import 'package:worker/features/news/domain/entities/news_article.dart';
|
||||
|
||||
/// Related Article Card
|
||||
@@ -31,15 +30,17 @@ class RelatedArticleCard extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(bottom: AppSpacing.md),
|
||||
padding: const EdgeInsets.all(AppSpacing.md),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(AppRadius.lg),
|
||||
border: Border.all(color: const Color(0xFFE2E8F0)),
|
||||
border: Border.all(color: colorScheme.outlineVariant),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
@@ -54,7 +55,7 @@ class RelatedArticleCard extends StatelessWidget {
|
||||
placeholder: (context, url) => Container(
|
||||
width: 60,
|
||||
height: 60,
|
||||
color: AppColors.grey100,
|
||||
color: colorScheme.surfaceContainerHighest,
|
||||
child: const Center(
|
||||
child: SizedBox(
|
||||
width: 16,
|
||||
@@ -66,11 +67,11 @@ class RelatedArticleCard extends StatelessWidget {
|
||||
errorWidget: (context, url, error) => Container(
|
||||
width: 60,
|
||||
height: 60,
|
||||
color: AppColors.grey100,
|
||||
child: const FaIcon(
|
||||
color: colorScheme.surfaceContainerHighest,
|
||||
child: FaIcon(
|
||||
FontAwesomeIcons.image,
|
||||
size: 20,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -86,10 +87,10 @@ class RelatedArticleCard extends StatelessWidget {
|
||||
// Title (max 2 lines)
|
||||
Text(
|
||||
article.title,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF1E293B),
|
||||
color: colorScheme.onSurface,
|
||||
height: 1.3,
|
||||
),
|
||||
maxLines: 2,
|
||||
@@ -101,9 +102,9 @@ class RelatedArticleCard extends StatelessWidget {
|
||||
// Metadata
|
||||
Text(
|
||||
'${article.formattedDate} • ${article.formattedViewCount} lượt xem',
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Color(0xFF64748B),
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user