- 「下午好,豆豆」改真实展示名(`nickname ?? username`,与资料页共用同一个 ProfileController:一次 /me 供两个消费点,改昵称后两处一起变)。资料未到手 时退化为不称名的「下午好 👋」,不编造假名。 - 按 ADR-022 决策 D3.5-1 钉死范围,**其余首页 demo 一律不动**,并在代码里逐项 标注为「刻意保留的 demo 占位」:天气/位置(需接外部服务)、圈子=话题 (ADR-018 剪出)、促销卡(M5 服务域)、搜索与本地服务段。 widget 测试同时钉住「保留项仍在」,避免后续以「顺手清理」越出拍板范围。 - 新增 compose 真链路桌面实测脚本 `integration_test/profile_avatar_live_test.dart` (默认跳过,`PATBOND_PROFILE_LIVE=1 ... -d linux` 开启):UI 注册 → 登录 → 资料真实化 → 设昵称 → 传用户头像 → Feed 作者名同步 → 宠物头像,逐步截图。 只有选图与压缩两层是桌面替身,createUpload / 预签名 PUT / confirm / PATCH 全为生产实现。两处实测坑写进注释:Feed 作者名有服务端 60s 缓存滞后; 1×1 的极小图能上传能下载但 Flutter 解码器拒绝,会被误判成「没传上」。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,7 @@ import 'package:patbond_flutter/features/community/community_display.dart';
|
||||
import 'package:patbond_flutter/features/community/community_models.dart';
|
||||
import 'package:patbond_flutter/features/community/feed_analytics.dart';
|
||||
import 'package:patbond_flutter/features/community/feed_exposure.dart';
|
||||
import 'package:patbond_flutter/features/profile/profile_controller.dart';
|
||||
import 'package:patbond_flutter/models/models.dart';
|
||||
import 'package:patbond_flutter/state/app_state.dart';
|
||||
import 'package:patbond_flutter/widgets/common.dart';
|
||||
@@ -17,8 +18,19 @@ import 'package:patbond_flutter/widgets/common.dart';
|
||||
enum HomeSegment { feed, services }
|
||||
|
||||
/// 首页 Tab:Feed 段自 T3-14 起消费 [CommunityController] 真实数据
|
||||
/// (四态 + 游标翻页 + 聚合曝光埋点);天气条/问候卡/搜索/服务段与
|
||||
/// `_StoryRow` 家具保留 demo 形态(03 号评估 §1.1 判定)。
|
||||
/// (四态 + 游标翻页 + 聚合曝光埋点);问候语自 T3.5-10 起用真实展示名。
|
||||
///
|
||||
/// **刻意保留的 demo 占位**(ADR-022 决策 D3.5-1 钉死范围,留待对应里程碑;
|
||||
/// 在此登记以免实测重复反馈):
|
||||
/// - **天气条与地区选择**([_WeatherStatusBar] / [_AreaPickerSheet] /
|
||||
/// [_WeatherDetailsSheet]):需接外部天气服务(含 API key 与配额管理),
|
||||
/// 不属本迭代。
|
||||
/// - **story 环的「柴犬圈 / 猫咪圈 / 救助站」**([_StoryRow]):实为话题,
|
||||
/// ADR-018 已把话题剪出 M3 范围;环内「发布」是真入口。
|
||||
/// - **促销卡「新用户首单立减 ¥20」**([_PromoCard]):属 M5 服务域
|
||||
/// (优惠/订单能力尚不存在)。
|
||||
/// - **搜索框与「本地服务」段**:搜索只过滤已加载页(契约无检索端点);
|
||||
/// 服务商数据为 demo 常量,同属 M5。
|
||||
class HomePage extends StatefulWidget {
|
||||
const HomePage({
|
||||
required this.appState,
|
||||
@@ -26,6 +38,7 @@ class HomePage extends StatefulWidget {
|
||||
required this.onOpenServices,
|
||||
required this.onOpenCompose,
|
||||
super.key,
|
||||
this.profileController,
|
||||
this.onOpenPost,
|
||||
this.feedAnalytics,
|
||||
this.isActive = true,
|
||||
@@ -36,6 +49,11 @@ class HomePage extends StatefulWidget {
|
||||
/// Feed 数据源(Tab 级单例,app.dart 装配注入)。
|
||||
final CommunityController communityController;
|
||||
|
||||
/// 展示名数据源(T3.5-10):与资料页**共用同一控制器**,
|
||||
/// 故改昵称后两处一起变,且全程只发一次 `/me`。
|
||||
/// 未注入或资料尚未到手时问候语不带名字(不编造「豆豆」这类假名)。
|
||||
final ProfileController? profileController;
|
||||
|
||||
final ValueChanged<bool> onOpenServices;
|
||||
|
||||
/// 发布页入口(T3-17:story 环「发布」与空态 CTA 均 push 真实发布页,
|
||||
@@ -79,6 +97,8 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
// 展示名变化(资料首次到手 / 用户改昵称后返回)即重建问候语。
|
||||
widget.profileController?.addListener(_onProfileChanged);
|
||||
// 主壳挂载即预取(pets 先例);重登后控制器已 reset 回 initial。
|
||||
// 首屏自动预取不计入浏览段 refreshCount(非用户动作)。
|
||||
if (_feed.phase == FeedPhase.initial) {
|
||||
@@ -87,6 +107,10 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
|
||||
if (_feedSurfaceVisible) _startSegment();
|
||||
}
|
||||
|
||||
void _onProfileChanged() {
|
||||
if (mounted) setState(() {});
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(HomePage oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
@@ -114,6 +138,7 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
|
||||
@override
|
||||
void dispose() {
|
||||
_settleSegment();
|
||||
widget.profileController?.removeListener(_onProfileChanged);
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
super.dispose();
|
||||
}
|
||||
@@ -341,7 +366,10 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
|
||||
const SizedBox(height: 12),
|
||||
_PetGreetingCard(
|
||||
greeting: greeting,
|
||||
petName: widget.appState.pet.name,
|
||||
// T3.5-10:真实展示名(`nickname ?? username`,与资料页
|
||||
// 同源同规则)取代硬编码的 demo 宠物名「豆豆」。
|
||||
// 资料未到手时为 null → 只问候不称名,不编造假名。
|
||||
displayName: widget.profileController?.displayName,
|
||||
petAvatar: widget.appState.pet.avatarUrl,
|
||||
advice: widget.appState.locationWeather.petAdvice,
|
||||
),
|
||||
@@ -552,6 +580,8 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
|
||||
}
|
||||
}
|
||||
|
||||
/// **刻意保留的 demo 占位**(ADR-022 决策 D3.5-1):定位与天气需接外部
|
||||
/// 服务(含 API key 与配额管理),本迭代不做;地区选择只改本机 demo 数据。
|
||||
class _WeatherStatusBar extends StatelessWidget {
|
||||
const _WeatherStatusBar({
|
||||
required this.weather,
|
||||
@@ -641,14 +671,23 @@ class _WeatherStatusBar extends StatelessWidget {
|
||||
class _PetGreetingCard extends StatelessWidget {
|
||||
const _PetGreetingCard({
|
||||
required this.greeting,
|
||||
required this.petName,
|
||||
required this.displayName,
|
||||
required this.petAvatar,
|
||||
required this.advice,
|
||||
});
|
||||
|
||||
final String greeting;
|
||||
final String petName;
|
||||
|
||||
/// 当前用户展示名;null 即资料未到手(问候语退化为不称名形态)。
|
||||
final String? displayName;
|
||||
|
||||
/// **刻意保留的 demo 占位**:卡右侧大图仍是 demo 宠物图(用户头像与宠物
|
||||
/// 头像本单已真实化,但「首页该显示哪只宠物」需要一个『当前宠物』概念,
|
||||
/// 尚不存在——不在 ADR-022 范围内)。
|
||||
final String petAvatar;
|
||||
|
||||
/// **刻意保留的 demo 占位**:养宠建议来自 demo 天气数据(外部天气服务
|
||||
/// 未接,ADR-022 决策 D3.5-1)。
|
||||
final String advice;
|
||||
|
||||
@override
|
||||
@@ -691,7 +730,9 @@ class _PetGreetingCard extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'$greeting,$petName 👋',
|
||||
displayName == null
|
||||
? '$greeting 👋'
|
||||
: '$greeting,$displayName 👋',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
@@ -886,6 +927,9 @@ Color _weatherColor(WeatherCondition condition) {
|
||||
};
|
||||
}
|
||||
|
||||
/// **刻意保留的 demo 占位**(ADR-022 决策 D3.5-1):「柴犬圈 / 猫咪圈 /
|
||||
/// 救助站」三个圈子实为**话题**,ADR-018 已把话题能力剪出 M3 范围,
|
||||
/// 留待话题里程碑。首格「发布」是真入口(T3-17 起 push 真实发布页)。
|
||||
class _StoryRow extends StatelessWidget {
|
||||
const _StoryRow({required this.onCreate});
|
||||
|
||||
@@ -956,6 +1000,8 @@ class _StoryRow extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// **刻意保留的 demo 占位**(ADR-022 决策 D3.5-1):促销文案与「去使用」
|
||||
/// 属 **M5 服务域**——优惠券与订单能力尚不存在,当前点按只跳本地服务段。
|
||||
class _PromoCard extends StatelessWidget {
|
||||
const _PromoCard({required this.onTap});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user