fix(player): 评论总数含回复并改用相对时间

This commit is contained in:
Zhou Weidong committed 2026-10-21 12:00:27 +08:00
1 parent 22cb74ded4
commit 2659c29501
14 files changed
+212 -9

No files matched your search

@@ -0,0 +1,24 @@
// Author: fruit.com.cn
import '../../l10n/app_localizations.dart';
/// Formats a timestamp as a relative label, falling back to a compact local date.
String formatRelativeDateTime(
DateTime value,
AppLocalizations l10n, {
DateTime? now,
}) {
final localValue = value.toLocal();
final localNow = (now ?? DateTime.now()).toLocal();
final elapsed = localNow.difference(localValue);
if (elapsed.isNegative || elapsed.inMinutes < 1) return l10n.justNow;
if (elapsed.inMinutes < 60) return l10n.minutesAgo(elapsed.inMinutes);
if (elapsed.inHours < 24) return l10n.hoursAgo(elapsed.inHours);
if (localValue.year == localNow.year) {
return '${_twoDigits(localValue.month)}-${_twoDigits(localValue.day)}';
}
return '${localValue.year}-${_twoDigits(localValue.month)}-'
'${_twoDigits(localValue.day)}';
}
String _twoDigits(int value) => value.toString().padLeft(2, '0');
+7
View File
@@ -100,6 +100,13 @@
"count": {"type": "int"}
}
},
"justNow": "Just now",
"hoursAgo": "{count} hours ago",
"@hoursAgo": {
"placeholders": {
"count": {"type": "int"}
}
},
"artworkOnly": "Artwork",
"artworkOnlyNotice": "This is a visual catalog item. No video is available yet.",
"videoPending": "Video pending",
+7
View File
@@ -79,6 +79,13 @@
"count": {"type": "int"}
}
},
"justNow": "Baru saja",
"hoursAgo": "{count} jam lalu",
"@hoursAgo": {
"placeholders": {
"count": {"type": "int"}
}
},
"artworkOnly": "Gambar",
"artworkOnlyNotice": "Ini hanya gambar katalog. Videonya belum tersedia.",
"videoPending": "Video belum siap",
+12
View File
@@ -508,6 +508,18 @@ abstract class AppLocalizations {
/// **'{count} min ago'**
String minutesAgo(int count);
/// No description provided for @justNow.
///
/// In en, this message translates to:
/// **'Just now'**
String get justNow;
/// No description provided for @hoursAgo.
///
/// In en, this message translates to:
/// **'{count} hours ago'**
String hoursAgo(int count);
/// No description provided for @artworkOnly.
///
/// In en, this message translates to:
+8
View File
@@ -227,6 +227,14 @@ class AppLocalizationsEn extends AppLocalizations {
return '$count min ago';
}
@override
String get justNow => 'Just now';
@override
String hoursAgo(int count) {
return '$count hours ago';
}
@override
String get artworkOnly => 'Artwork';
+8
View File
@@ -227,6 +227,14 @@ class AppLocalizationsId extends AppLocalizations {
return '$count mnt lalu';
}
@override
String get justNow => 'Baru saja';
@override
String hoursAgo(int count) {
return '$count jam lalu';
}
@override
String get artworkOnly => 'Gambar';
+8
View File
@@ -224,6 +224,14 @@ class AppLocalizationsZh extends AppLocalizations {
return '$count 分钟前';
}
@override
String get justNow => '刚刚';
@override
String hoursAgo(int count) {
return '$count 小时前';
}
@override
String get artworkOnly => '海报展示';
+7
View File
@@ -79,6 +79,13 @@
"count": {"type": "int"}
}
},
"justNow": "刚刚",
"hoursAgo": "{count} 小时前",
"@hoursAgo": {
"placeholders": {
"count": {"type": "int"}
}
},
"artworkOnly": "海报展示",
"artworkOnlyNotice": "这张海报仅用于目录展示,暂时没有可播放视频。",
"videoPending": "视频待提供",
+2 -2
View File
@@ -3,7 +3,7 @@
import 'package:flutter/material.dart';
import '../../../core/data/models/comment.dart';
import '../../../core/formatting/localized_date_time.dart';
import '../../../core/formatting/relative_date_time.dart';
import '../../../core/theme/app_colors.dart';
import '../../../core/theme/app_dimens.dart';
import '../../../core/widgets/remote_image.dart';
@@ -164,7 +164,7 @@ class CommentTile extends StatelessWidget {
children: [
if (createdAt != null)
Text(
formatLocalizedDateTime(context, createdAt),
formatRelativeDateTime(createdAt, l10n),
style: const TextStyle(color: AppColors.subtle, fontSize: 12),
),
if (createdAt != null && !isReply) ...[
@@ -43,18 +43,23 @@ class DramaCommentsSheet extends StatefulWidget {
class _DramaCommentsSheetState extends State<DramaCommentsSheet> {
final _input = TextEditingController();
final _inputFocus = FocusNode();
late final CommentRepository _comments;
late final CommentController _controller;
DramaComment? _replyTo;
int? _serverCount;
int _countRequest = 0;
@override
void initState() {
super.initState();
_comments = context.read<CommentRepository>();
_controller = CommentController(
comments: context.read<CommentRepository>(),
comments: _comments,
interactions: context.read<InteractionRepository>(),
dramaId: widget.drama.id,
);
unawaited(_controller.load());
unawaited(_refreshCount());
}
@override
@@ -76,6 +81,7 @@ class _DramaCommentsSheetState extends State<DramaCommentsSheet> {
_input.clear();
setState(() => _replyTo = null);
FocusScope.of(context).unfocus();
await _refreshCount();
return;
}
_showMessage(
@@ -122,11 +128,26 @@ class _DramaCommentsSheetState extends State<DramaCommentsSheet> {
);
if (confirmed != true || !mounted) return;
final result = await _controller.delete(comment);
if (!result.isSuccess && mounted) {
if (!result.isSuccess) {
if (!mounted) return;
_showMessage(
result.errorMessage ??
AppLocalizations.of(context)!.commentDeleteFailed,
);
return;
}
await _refreshCount();
}
Future<void> _refreshCount() async {
final request = ++_countRequest;
try {
final count = await _comments.count(widget.drama.id);
if (!mounted || request != _countRequest) return;
setState(() => _serverCount = count);
} catch (_) {
if (!mounted || request != _countRequest) return;
setState(() => _serverCount = null);
}
}
@@ -190,7 +211,7 @@ class _DramaCommentsSheetState extends State<DramaCommentsSheet> {
Widget _content(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
final count = _controller.exactCount;
final count = _serverCount ?? _controller.exactCount;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [