feat(player): 评论发布成功提示

This commit is contained in:
Zhou Weidong committed 2026-10-21 13:47:36 +08:00
1 parent 9c0c1b8982
commit e86daf559a
10 files changed
+138 -47

No files matched your search

+1
View File
@@ -211,6 +211,7 @@
"writeComment": "Write a comment",
"commentLoginHint": "Sign in to comment",
"postComment": "Post",
"commentPostSuccess": "Comment posted",
"loadMore": "Load more",
"commentsLoadFailed": "Comments could not be loaded.",
"commentPostFailed": "Could not post your comment.",
+1
View File
@@ -185,6 +185,7 @@
"writeComment": "Tulis komentar",
"commentLoginHint": "Masuk untuk berkomentar",
"postComment": "Kirim",
"commentPostSuccess": "Komentar terkirim",
"loadMore": "Muat lebih banyak",
"commentsLoadFailed": "Komentar tidak dapat dimuat.",
"commentPostFailed": "Komentar tidak dapat dikirim.",
+6
View File
@@ -946,6 +946,12 @@ abstract class AppLocalizations {
/// **'Post'**
String get postComment;
/// No description provided for @commentPostSuccess.
///
/// In en, this message translates to:
/// **'Comment posted'**
String get commentPostSuccess;
/// No description provided for @loadMore.
///
/// In en, this message translates to:
+3
View File
@@ -462,6 +462,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get postComment => 'Post';
@override
String get commentPostSuccess => 'Comment posted';
@override
String get loadMore => 'Load more';
+3
View File
@@ -463,6 +463,9 @@ class AppLocalizationsId extends AppLocalizations {
@override
String get postComment => 'Kirim';
@override
String get commentPostSuccess => 'Komentar terkirim';
@override
String get loadMore => 'Muat lebih banyak';
+3
View File
@@ -457,6 +457,9 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get postComment => '发布';
@override
String get commentPostSuccess => '发表成功';
@override
String get loadMore => '加载更多';
+1
View File
@@ -185,6 +185,7 @@
"writeComment": "写评论",
"commentLoginHint": "登录后参与评论",
"postComment": "发布",
"commentPostSuccess": "发表成功",
"loadMore": "加载更多",
"commentsLoadFailed": "评论加载失败,请重试。",
"commentPostFailed": "评论发布失败,请重试。",
@@ -129,6 +129,7 @@ class _CommentComposerSheetState extends State<CommentComposerSheet>
);
if (!mounted) return;
if (result.isSuccess) {
_input.clear();
_closing = true;
Navigator.pop(context, true);
return;
@@ -41,6 +41,7 @@ class DramaCommentsSheet extends StatefulWidget {
class _DramaCommentsSheetState extends State<DramaCommentsSheet> {
late final CommentRepository _comments;
late final CommentController _controller;
final _messengerKey = GlobalKey<ScaffoldMessengerState>();
int? _serverCount;
int _countRequest = 0;
@@ -91,65 +92,110 @@ class _DramaCommentsSheetState extends State<DramaCommentsSheet> {
builder: (_) =>
CommentComposerSheet(controller: _controller, replyTo: replyTo),
);
if (published == true && mounted) await _refreshCount();
if (published == true && mounted) {
_showPostSuccess();
await _refreshCount();
}
}
void _handleDismissLayerTap() {
Navigator.pop(context);
}
@override
Widget build(BuildContext context) {
void _showPostSuccess() {
final messenger = _messengerKey.currentState;
if (messenger == null) return;
final l10n = AppLocalizations.of(context)!;
messenger
..hideCurrentSnackBar()
..showSnackBar(
SnackBar(
key: const Key('comment-post-success-snackbar'),
content: Text(
l10n.commentPostSuccess,
style: const TextStyle(
color: Colors.white,
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
behavior: SnackBarBehavior.floating,
duration: const Duration(milliseconds: 1500),
backgroundColor: AppColors.ink,
elevation: 6,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
margin: EdgeInsets.fromLTRB(16, 0, 16, _sheetHeight(context) + 12),
),
);
}
double _sheetHeight(BuildContext context) {
final screenHeight = MediaQuery.sizeOf(context).height;
final topSafeArea = MediaQuery.paddingOf(context).top;
final halfHeight = (screenHeight * 0.5).roundToDouble();
final maxHeight = math.max(0.0, screenHeight - topSafeArea);
final sheetHeight = math.min(halfHeight, maxHeight);
return Stack(
fit: StackFit.expand,
children: [
Positioned.fill(
child: GestureDetector(
key: const Key('player-comments-dismiss-layer'),
behavior: HitTestBehavior.opaque,
onTap: _handleDismissLayerTap,
),
),
Align(
alignment: Alignment.bottomCenter,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {},
child: KeyedSubtree(
key: const Key('player-comments-sheet'),
child: SizedBox(
key: const Key('comment-sheet-constraints'),
height: sheetHeight,
child: Container(
decoration: const BoxDecoration(
color: AppColors.card,
borderRadius: BorderRadius.vertical(
top: Radius.circular(_sheetRadius),
),
),
child: SafeArea(
top: false,
child: ListenableBuilder(
listenable: _controller,
builder: (context, _) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: _content(context),
);
},
return math.min(halfHeight, maxHeight);
}
@override
Widget build(BuildContext context) {
final sheetHeight = _sheetHeight(context);
return ScaffoldMessenger(
key: _messengerKey,
child: Scaffold(
backgroundColor: Colors.transparent,
resizeToAvoidBottomInset: false,
body: Stack(
fit: StackFit.expand,
children: [
Positioned.fill(
child: GestureDetector(
key: const Key('player-comments-dismiss-layer'),
behavior: HitTestBehavior.opaque,
onTap: _handleDismissLayerTap,
),
),
Align(
alignment: Alignment.bottomCenter,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {},
child: KeyedSubtree(
key: const Key('player-comments-sheet'),
child: SizedBox(
key: const Key('comment-sheet-constraints'),
height: sheetHeight,
child: Container(
decoration: const BoxDecoration(
color: AppColors.card,
borderRadius: BorderRadius.vertical(
top: Radius.circular(_sheetRadius),
),
),
child: SafeArea(
top: false,
child: ListenableBuilder(
listenable: _controller,
builder: (context, _) {
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16,
),
child: _content(context),
);
},
),
),
),
),
),
),
),
),
],
),
],
),
);
}
+30 -4
View File
@@ -234,7 +234,11 @@ void main() {
publishError: const ApiException(400, '评论包含敏感词'),
);
await _pumpSheet(tester, repository: repository);
await _pumpSheet(
tester,
repository: repository,
locale: const Locale('zh'),
);
await tester.tap(find.byKey(const Key('comment-input-placeholder')));
await tester.pumpAndSettle();
await tester.enterText(
@@ -250,6 +254,7 @@ void main() {
find.byKey(const Key('comment-composer-input')),
);
expect(field.controller?.text, 'blocked text');
expect(find.text('发表成功'), findsNothing);
// 发送失败时保留输入与焦点,用户可以直接改完再发。
expect(field.focusNode?.hasFocus, isTrue);
},
@@ -263,7 +268,11 @@ void main() {
countResult: 1,
);
await _pumpSheet(tester, repository: repository);
await _pumpSheet(
tester,
repository: repository,
locale: const Locale('zh'),
);
final input = find.byKey(const Key('comment-composer-input'));
tester.view.viewInsets = const FakeViewPadding(bottom: 180);
await tester.tap(find.byKey(const Key('comment-input-placeholder')));
@@ -278,9 +287,25 @@ void main() {
isNotNull,
);
await tester.tap(find.byKey(const Key('comment-composer-post')));
await tester.pumpAndSettle();
await tester.pump();
await tester.pump(const Duration(milliseconds: 500));
expect(find.byKey(const Key('comment-composer-sheet')), findsNothing);
final successSnackBar = find.byKey(
const Key('comment-post-success-snackbar'),
);
expect(successSnackBar, findsOneWidget);
expect(find.text('发表成功'), findsOneWidget);
final snackBar = tester.widget<SnackBar>(successSnackBar);
expect(snackBar.action, isNull);
expect(snackBar.behavior, SnackBarBehavior.floating);
expect(snackBar.duration, const Duration(milliseconds: 1500));
expect(
tester.getRect(find.text('发表成功')).bottom,
lessThanOrEqualTo(
tester.getRect(find.byKey(const Key('player-comments-sheet'))).top,
),
);
expect(find.byKey(const Key('comment-list')), findsOneWidget);
expect(find.byKey(const Key('comment-count-label')), findsOneWidget);
expect(find.byKey(const Key('comment-input-placeholder')), findsOneWidget);
@@ -669,6 +694,7 @@ Future<AuthController> _pumpSheet(
WidgetTester tester, {
required FakeCommentRepository repository,
FakeHaptics? haptics,
Locale locale = const Locale('en'),
}) async {
final auth = await _auth();
await tester.pumpWidget(
@@ -682,7 +708,7 @@ Future<AuthController> _pumpSheet(
Provider<Haptics>.value(value: haptics ?? FakeHaptics()),
],
child: MaterialApp(
locale: const Locale('en'),
locale: locale,
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
initialRoute: '/comments',