新增:帖子详情页真实数据整页替换——四态/真九宫格大图/评论区/关注双态,Feed 导航与互动全接线,demo 详情退役(T3-15/16)
CI / flutter-gates (push) Failing after 20s
CI / flutter-gates (push) Failing after 20s
- post_detail_page 整页重写:内存副本先渲染 + getPost 拉新、四态 (loading/error+重试/ready/40403 提示后返回 Feed 并刷新);媒体全量 ——单图原比例(高度钳制 [0.75w,1.33w])、多图真九宫格(D11 轮播 弃用,本单裁定)+ InteractiveViewer 全屏大图(03 号拍板 E) - 评论区:游标列表触底补页 + 底部输入条创建(幂等键在数据层)+ 仅本人评论渲染删除入口(40301/40404 服务端兜底);成败埋点对 (durationMs 取输入会话起点、attemptSeq 会话内递增);CommentTile 升共享组件(@ 回复前缀呈现、降级作者「宠友」占位) - 关注双态钮(tonal +关注 / Outlined 已关注 + 取关确认)乐观翻转、 回滚零动画 + SnackBar,user_followed/unfollowed(post_detail) 挂接; 本人帖不渲染 - Feed 接通:整卡与评论钮导航详情(T3-14 占位提示移除)、点赞/收藏 接共享 ToggleSync(详情与卡片同一实例跨页一致)、toggleError 一次性 SnackBar 消费 - 主壳/装配:openPost 按 postId push;create demo 流不再导航已退役的 demo 详情(发布页真实化留 T3-17);AppState 在 post_detail/Feed 面 消费清零 - compose 冒烟(env 门控默认跳过):真链路发帖/点赞幂等/收藏/评论/ 删评一轮 + 断网点赞回滚(生产 ApiClient 异常链) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,7 @@ import 'package:patbond_flutter/core/widgets/feed_skeleton.dart';
|
||||
import 'package:patbond_flutter/core/widgets/inline_error_banner.dart';
|
||||
import 'package:patbond_flutter/core/widgets/post_card.dart';
|
||||
import 'package:patbond_flutter/features/community/community_controller.dart';
|
||||
import 'package:patbond_flutter/features/community/community_interaction_analytics.dart';
|
||||
import 'package:patbond_flutter/features/community/community_models.dart';
|
||||
import 'package:patbond_flutter/features/community/feed_analytics.dart';
|
||||
import 'package:patbond_flutter/features/home/home_page.dart';
|
||||
@@ -39,15 +40,22 @@ void main() {
|
||||
late List<(String, Map<String, dynamic>?)> events;
|
||||
late FeedAnalytics analytics;
|
||||
late int createTaps;
|
||||
late List<String> openedPosts;
|
||||
|
||||
setUp(() {
|
||||
repository = FakeCommunityRepository();
|
||||
controller = CommunityController(repository: repository);
|
||||
events = [];
|
||||
controller = CommunityController(
|
||||
repository: repository,
|
||||
interactionAnalytics: CommunityInteractionAnalytics(
|
||||
(name, [props]) async => events.add((name, props)),
|
||||
),
|
||||
);
|
||||
analytics = FeedAnalytics(
|
||||
(name, [props]) async => events.add((name, props)),
|
||||
);
|
||||
createTaps = 0;
|
||||
openedPosts = [];
|
||||
});
|
||||
|
||||
List<Map<String, dynamic>?> eventsNamed(String name) =>
|
||||
@@ -65,6 +73,7 @@ void main() {
|
||||
isActive: isActive,
|
||||
onOpenServices: (_) {},
|
||||
onOpenCreate: () => createTaps++,
|
||||
onOpenPost: openedPosts.add,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -361,7 +370,7 @@ void main() {
|
||||
expect(eventsNamed('feed_viewed'), hasLength(2));
|
||||
});
|
||||
|
||||
testWidgets('T3-14 取舍:整卡点按提示详情接入中(不导航 demo 详情)', (tester) async {
|
||||
testWidgets('T3-15 导航接通:整卡与评论钮点按回调 onOpenPost(占位提示移除)', (tester) async {
|
||||
repository.onFeed = (_, _) async => feedPage([textCard('p-1')]);
|
||||
|
||||
await pumpHome(tester);
|
||||
@@ -371,7 +380,72 @@ void main() {
|
||||
|
||||
await tester.tap(find.text('动态内容 p-1'));
|
||||
await tester.pump();
|
||||
expect(find.text('帖子详情正在接入真实数据,敬请期待'), findsOneWidget);
|
||||
expect(find.text('帖子详情正在接入真实数据,敬请期待'), findsNothing);
|
||||
expect(openedPosts, ['p-1']);
|
||||
|
||||
await tester.tap(find.byIcon(Icons.chat_bubble_outline));
|
||||
await tester.pump();
|
||||
expect(openedPosts, ['p-1', 'p-1']);
|
||||
});
|
||||
|
||||
testWidgets('T3-16 互动接线:点赞乐观翻转即时显示,成功报 post_liked(source=feed)', (
|
||||
tester,
|
||||
) async {
|
||||
repository.onFeed = (_, _) async => feedPage([
|
||||
FeedCard.fromJson({
|
||||
...sampleFeedCardJson(likeCount: 6),
|
||||
'coverImage': null,
|
||||
'mediaCount': 0,
|
||||
}),
|
||||
]);
|
||||
final like = Completer<LikeState>();
|
||||
repository.onLikeToggle = (_, _) => like.future;
|
||||
|
||||
await pumpHome(tester);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.drag(list(), const Offset(0, -800));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.byIcon(Icons.favorite_border));
|
||||
await tester.pump();
|
||||
// 乐观翻转:请求未回已 +1(同帧反馈)。
|
||||
expect(find.text('7'), findsOneWidget);
|
||||
expect(repository.calls, contains('like:p-1'));
|
||||
|
||||
like.complete(const LikeState(liked: true, likeCount: 7));
|
||||
await tester.pumpAndSettle();
|
||||
expect(eventsNamed('post_liked'), [
|
||||
{'source': 'feed'},
|
||||
]);
|
||||
expect(find.text('7'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('T3-16 互动接线:点赞失败回滚 + SnackBar「操作失败,请重试」', (tester) async {
|
||||
repository.onFeed = (_, _) async => feedPage([
|
||||
FeedCard.fromJson({
|
||||
...sampleFeedCardJson(likeCount: 6),
|
||||
'coverImage': null,
|
||||
'mediaCount': 0,
|
||||
}),
|
||||
]);
|
||||
final like = Completer<LikeState>();
|
||||
repository.onLikeToggle = (_, _) => like.future;
|
||||
|
||||
await pumpHome(tester);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.drag(list(), const Offset(0, -800));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.byIcon(Icons.favorite_border));
|
||||
await tester.pump();
|
||||
expect(find.text('7'), findsOneWidget);
|
||||
|
||||
like.completeError(const ApiNetworkException());
|
||||
await tester.pumpAndSettle();
|
||||
// 回滚成对恢复 + SnackBar;不报 post_liked。
|
||||
expect(find.text('6'), findsOneWidget);
|
||||
expect(find.text('操作失败,请重试'), findsOneWidget);
|
||||
expect(eventsNamed('post_liked'), isEmpty);
|
||||
});
|
||||
|
||||
testWidgets('搜索词过滤已加载卡片;无命中显搜索空态', (tester) async {
|
||||
|
||||
Reference in New Issue
Block a user