This commit is contained in:
Phuoc Nguyen
2025-11-11 16:02:09 +07:00
parent 2f296ad8d3
commit 0093b62c29
5 changed files with 117 additions and 139 deletions

View File

@@ -176,7 +176,7 @@ SPEC CHECKSUMS:
connectivity_plus: 2a701ffec2c0ae28a48cf7540e279787e77c447d connectivity_plus: 2a701ffec2c0ae28a48cf7540e279787e77c447d
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60 DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655 file_picker: b159e0c068aef54932bb15dc9fd1571818edaf49
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12 flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a
@@ -196,7 +196,7 @@ SPEC CHECKSUMS:
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
SDWebImage: 9f177d83116802728e122410fb25ad88f5c7608a SDWebImage: 9f177d83116802728e122410fb25ad88f5c7608a
share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4

View File

@@ -5,7 +5,7 @@
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string> <string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key> <key>CFBundleDisplayName</key>
<string>Worker</string> <string>DBIZ Partner</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string> <string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>

View File

@@ -10,6 +10,8 @@ import 'package:flutter/services.dart';
import 'package:flutter_html/flutter_html.dart'; import 'package:flutter_html/flutter_html.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:share_plus/share_plus.dart';
import 'package:worker/core/constants/api_constants.dart';
import 'package:worker/core/constants/ui_constants.dart'; import 'package:worker/core/constants/ui_constants.dart';
import 'package:worker/core/theme/colors.dart'; import 'package:worker/core/theme/colors.dart';
import 'package:worker/features/news/domain/entities/news_article.dart'; import 'package:worker/features/news/domain/entities/news_article.dart';
@@ -159,76 +161,73 @@ class _NewsDetailPageState extends ConsumerState<NewsDetailPage> {
// Article Body - Render HTML content // Article Body - Render HTML content
if (article.content != null && article.content!.isNotEmpty) if (article.content != null && article.content!.isNotEmpty)
Container( Html(
// Wrap Html in Container to prevent rendering issues data: article.content,
child: Html( style: {
data: article.content, "body": Style(
style: { margin: Margins.zero,
"body": Style( padding: HtmlPaddings.zero,
margin: Margins.zero, fontSize: FontSize(16),
padding: HtmlPaddings.zero, lineHeight: const LineHeight(1.7),
fontSize: FontSize(16), color: const Color(0xFF1E293B),
lineHeight: const LineHeight(1.7), ),
color: const Color(0xFF1E293B), "h2": Style(
fontSize: FontSize(20),
fontWeight: FontWeight.w600,
color: const Color(0xFF1E293B),
margin: Margins.only(top: 32, bottom: 16),
),
"h3": Style(
fontSize: FontSize(18),
fontWeight: FontWeight.w600,
color: const Color(0xFF1E293B),
margin: Margins.only(top: 24, bottom: 12),
),
"p": Style(
fontSize: FontSize(16),
color: const Color(0xFF1E293B),
lineHeight: const LineHeight(1.7),
margin: Margins.only(bottom: 16),
),
"strong": Style(
fontWeight: FontWeight.w600,
color: const Color(0xFF1E293B),
),
"img": Style(
margin: Margins.symmetric(vertical: 16),
),
"ul": Style(
margin: Margins.only(left: 16, bottom: 16),
),
"ol": Style(
margin: Margins.only(left: 16, bottom: 16),
),
"li": Style(
fontSize: FontSize(16),
color: const Color(0xFF1E293B),
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),
), ),
"h2": Style( padding: HtmlPaddings.all(16),
fontSize: FontSize(20), margin: Margins.symmetric(vertical: 24),
fontWeight: FontWeight.w600, fontStyle: FontStyle.italic,
color: const Color(0xFF1E293B), ),
margin: Margins.only(top: 32, bottom: 16), "div": Style(
), margin: Margins.zero,
"h3": Style( padding: HtmlPaddings.zero,
fontSize: FontSize(18), ),
fontWeight: FontWeight.w600, },
color: const Color(0xFF1E293B), onLinkTap: (url, attributes, element) {
margin: Margins.only(top: 24, bottom: 12), // Handle link taps if needed
), if (url != null) {
"p": Style( debugPrint('Link tapped: $url');
fontSize: FontSize(16), }
color: const Color(0xFF1E293B), },
lineHeight: const LineHeight(1.7),
margin: Margins.only(bottom: 16),
),
"strong": Style(
fontWeight: FontWeight.w600,
color: const Color(0xFF1E293B),
),
"img": Style(
margin: Margins.symmetric(vertical: 16),
),
"ul": Style(
margin: Margins.only(left: 16, bottom: 16),
),
"ol": Style(
margin: Margins.only(left: 16, bottom: 16),
),
"li": Style(
fontSize: FontSize(16),
color: const Color(0xFF1E293B),
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),
),
padding: HtmlPaddings.all(16),
margin: Margins.symmetric(vertical: 24),
fontStyle: FontStyle.italic,
),
"div": Style(
margin: Margins.zero,
padding: HtmlPaddings.zero,
),
},
onLinkTap: (url, attributes, element) {
// Handle link taps if needed
if (url != null) {
debugPrint('Link tapped: $url');
}
},
),
), ),
const SizedBox(height: 32), const SizedBox(height: 32),
@@ -256,9 +255,8 @@ class _NewsDetailPageState extends ConsumerState<NewsDetailPage> {
/// Build metadata /// Build metadata
Widget _buildMetadata(NewsArticle article) { Widget _buildMetadata(NewsArticle article) {
return Wrap( return Row(
spacing: 16, spacing: 16,
runSpacing: 8,
children: [ children: [
// Category badge // Category badge
Container( Container(
@@ -281,13 +279,13 @@ class _NewsDetailPageState extends ConsumerState<NewsDetailPage> {
_buildMetaItem(Icons.calendar_today, article.formattedDate), _buildMetaItem(Icons.calendar_today, article.formattedDate),
// Reading time // Reading time
_buildMetaItem(Icons.schedule, article.readingTimeText), // _buildMetaItem(Icons.schedule, article.readingTimeText),
// Views // Views
_buildMetaItem( // _buildMetaItem(
Icons.visibility, // Icons.visibility,
'${article.formattedViewCount} lượt xem', // '${article.formattedViewCount} lượt xem',
), // ),
], ],
); );
} }
@@ -362,48 +360,27 @@ class _NewsDetailPageState extends ConsumerState<NewsDetailPage> {
Widget _buildSocialActions(NewsArticle article) { Widget _buildSocialActions(NewsArticle article) {
return Container( return Container(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: const BoxDecoration(
border: Border.symmetric( border: Border.symmetric(
horizontal: BorderSide(color: const Color(0xFFE2E8F0)), horizontal: BorderSide(color: Color(0xFFE2E8F0)),
), ),
), ),
child: Column( child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
// Engagement stats _buildActionButton(
Wrap( icon: _isLiked ? Icons.favorite : Icons.favorite_border,
spacing: 16, onPressed: _onLikeTap,
runSpacing: 8, color: _isLiked ? Colors.red : null,
children: [
_buildStatItem(Icons.favorite, '${article.likeCount} lượt thích'),
_buildStatItem(
Icons.comment,
'${article.commentCount} bình luận',
),
_buildStatItem(Icons.share, '${article.shareCount} lượt chia sẻ'),
],
), ),
const SizedBox(width: 8),
const SizedBox(height: 16), _buildActionButton(
icon: _isBookmarked ? Icons.bookmark : Icons.bookmark_border,
// Action buttons onPressed: _onBookmarkTap,
Row( color: _isBookmarked ? AppColors.warning : null,
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildActionButton(
icon: _isLiked ? Icons.favorite : Icons.favorite_border,
onPressed: _onLikeTap,
color: _isLiked ? Colors.red : null,
),
const SizedBox(width: 8),
_buildActionButton(
icon: _isBookmarked ? Icons.bookmark : Icons.bookmark_border,
onPressed: _onBookmarkTap,
color: _isBookmarked ? AppColors.warning : null,
),
const SizedBox(width: 8),
_buildActionButton(icon: Icons.share, onPressed: _onShareTap),
],
), ),
const SizedBox(width: 8),
_buildActionButton(icon: Icons.share, onPressed: _onShareTap),
], ],
), ),
); );
@@ -577,21 +554,21 @@ class _NewsDetailPageState extends ConsumerState<NewsDetailPage> {
/// Handle share tap /// Handle share tap
void _onShareTap() { void _onShareTap() {
// Copy link to clipboard // Copy link to clipboard
Clipboard.setData( // Clipboard.setData(
ClipboardData(text: 'https://worker.app/news/${widget.articleId}'), // ClipboardData(text: 'https://worker.app/news/${widget.articleId}'),
); // );
//
ScaffoldMessenger.of(context).showSnackBar( // ScaffoldMessenger.of(context).showSnackBar(
const SnackBar( // const SnackBar(
content: Text('Đã sao chép link bài viết!'), // content: Text('Đã sao chép link bài viết!'),
duration: Duration(seconds: 2), // duration: Duration(seconds: 2),
), // ),
); // );
// TODO: Implement native share when share_plus package is added // TODO: Implement native share when share_plus package is added
// Share.share( SharePlus.instance.share(
// 'Xem bài viết: ${article.title}\nhttps://worker.app/news/${article.id}', ShareParams(text: 'Xem bài viết: ${ApiConstants.baseUrl}')
// subject: article.title, );
// );
} }
} }

View File

@@ -413,10 +413,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: file_picker name: file_picker
sha256: "825aec673606875c33cd8d3c4083f1a3c3999015a84178b317b7ef396b7384f3" sha256: ab13ae8ef5580a411c458d6207b6774a6c237d77ac37011b13994879f68a8810
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "8.0.7" version: "8.3.7"
file_selector_linux: file_selector_linux:
dependency: transitive dependency: transitive
description: description:
@@ -1160,18 +1160,18 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: share_plus name: share_plus
sha256: ef3489a969683c4f3d0239010cc8b7a2a46543a8d139e111c06c558875083544 sha256: "14c8860d4de93d3a7e53af51bff479598c4e999605290756bbbe45cf65b37840"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "9.0.0" version: "12.0.1"
share_plus_platform_interface: share_plus_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: share_plus_platform_interface name: share_plus_platform_interface
sha256: "0f9e4418835d1b2c3ae78fdb918251959106cefdbc4dd43526e182f80e82f6d4" sha256: "88023e53a13429bd65d8e85e11a9b484f49d4c190abbd96c7932b74d6927cc9a"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.0" version: "6.1.0"
shared_preferences: shared_preferences:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -1565,10 +1565,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: web name: web
sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.5.1" version: "1.1.1"
web_socket: web_socket:
dependency: transitive dependency: transitive
description: description:

View File

@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts # In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix. # of the product and file versions while build-number is used as the build suffix.
version: 1.0.0+1 version: 1.0.0+3
environment: environment:
sdk: ^3.9.2 sdk: ^3.9.2
@@ -63,9 +63,10 @@ dependencies:
qr_flutter: ^4.1.0 qr_flutter: ^4.1.0
mobile_scanner: ^5.2.3 mobile_scanner: ^5.2.3
# Utilities # Utilities
intl: ^0.20.0 intl: ^0.20.0
share_plus: ^9.0.0 share_plus: ^12.0.1
image_picker: ^1.1.2 image_picker: ^1.1.2
file_picker: ^8.0.0 file_picker: ^8.0.0
url_launcher: ^6.3.0 url_launcher: ^6.3.0