Files
lixi f873acf9a3
CI / flutter-gates (push) Failing after 20s
新增:帖子详情页真实数据整页替换——四态/真九宫格大图/评论区/关注双态,Feed 导航与互动全接线,demo 详情退役(T3-15/16)
- 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>
2026-09-09 13:39:10 +08:00

142 lines
5.6 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:flutter/material.dart';
import 'package:patbond_flutter/core/theme/app_theme.dart';
import 'package:patbond_flutter/core/widgets/pet_avatar.dart';
import 'package:patbond_flutter/features/community/community_display.dart';
import 'package:patbond_flutter/features/community/community_models.dart';
/// 评论条目(05 号规范 §3.4,demo 气泡形态升共享):
/// `PetAvatar sm32` + 10 + 气泡(`surface` 底、`border` 1px、圆角 16、
/// padding 12)——作者名 13/w700 → 4 → 内容 bodyMedium → 6 → 底行
/// (时间 11 `inkSoft` + 仅本人评论的「删除」入口)。
///
/// - @ 回复(单层平铺,契约 replyToUser)以「回复 @昵称:」前缀呈现;
/// 降级作者统一「宠友」占位(isDegraded 一个判定口)。
/// - [onDelete] 非 null 才渲染删除入口——**权限判定在调用方**(17 号
/// 后端语义:仅评论作者可删,他人可见评论 403/40301);[deleting]
/// 期间入口替换为 14 转圈防重复提交。
/// - 评论点赞(§3.4 底行右端)无契约端点,M3 不渲染。
class CommentTile extends StatelessWidget {
const CommentTile({
required this.comment,
super.key,
this.onDelete,
this.deleting = false,
this.now,
});
final PostComment comment;
/// 删除回调;null = 非本人评论,不渲染删除入口。
final VoidCallback? onDelete;
/// 删除请求在途(入口转圈锁定)。
final bool deleting;
/// 相对时间的参考时钟(测试注入;缺省取当前时间)。
final DateTime? now;
@override
Widget build(BuildContext context) {
final replyTo = comment.replyToUser;
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
PetAvatar(size: PetAvatarSize.sm, url: comment.author.avatarUrl),
const SizedBox(width: 10),
Expanded(
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: AppColors.surface,
border: Border.all(color: AppColors.border),
borderRadius: BorderRadius.circular(16),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
authorDisplayName(comment.author),
style: const TextStyle(
color: AppColors.ink,
fontSize: 13,
fontWeight: FontWeight.w700,
),
),
const SizedBox(height: 4),
Text.rich(
TextSpan(
children: [
if (replyTo != null)
TextSpan(
text: '回复 @${authorDisplayName(replyTo)}',
style: const TextStyle(
color: AppColors.inkSoft,
fontWeight: FontWeight.w600,
),
),
TextSpan(text: comment.content),
],
),
style: Theme.of(context).textTheme.bodyMedium,
),
const SizedBox(height: 6),
Row(
children: [
Text(
feedRelativeTime(comment.createdAt, now: now),
style: const TextStyle(
color: AppColors.inkSoft,
fontSize: 11,
),
),
const Spacer(),
if (onDelete != null)
deleting
? const Padding(
padding: EdgeInsets.all(4),
child: SizedBox(
width: 14,
height: 14,
child: CircularProgressIndicator(
strokeWidth: 2,
color: AppColors.inkSoft,
),
),
)
: Semantics(
label: '删除评论',
button: true,
child: InkWell(
onTap: onDelete,
borderRadius: BorderRadius.circular(
AppRadius.pill,
),
// 视觉 11 字,触控由 padding 撑到 ≥32
//(气泡内行高受限,不足 44 以热区扩展补偿)。
child: const Padding(
padding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 8,
),
child: Text(
'删除',
style: TextStyle(
color: AppColors.inkSoft,
fontSize: 11,
fontWeight: FontWeight.w600,
),
),
),
),
),
],
),
],
),
),
),
],
);
}
}