新增:互动埋点与乐观更新视觉基建——字典 v3 互动八事件封装 + ToggleSync 成功上报与触点归因 + LikeButton 三层闪烁抑制动画(T3-16)
- community_interaction_analytics:post_liked/unliked、post_favorited/unfavorited、 comment_create_succeeded/failed、user_followed/unfollowed 强类型封装(22 号 白名单键集逐一对齐;textLengthBucket 分桶、httpStatus 由五位码推导、 会话失效不上报);comment_create_started 锁死 unknown 不封装 - CommunityController:toggleLike/toggleBookmark 增 source 触点归因 (feed / post_detail),成功响应后经注入的 interactionAnalytics 上报 (乐观翻转与失败不报);adjustCommentCount 评论计数同源写入 (详情副本与 Feed 卡片一并更新) - LikeButton 升 Stateful:点按激活 240ms 弹性缩放 + 120ms 图标淡入、 取消仅颜色渐出;回滚/对账零动画直接跳变、计数与状态成对更新、 激活动画未播完等播完再跳(05 号 §3.5/§4);减弱动态设置降级瞬变 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:patbond_flutter/core/network/api_exception.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/community_repository.dart';
|
||||
import 'package:patbond_flutter/features/community/toggle_sync.dart';
|
||||
@@ -18,7 +19,7 @@ enum LoadMorePhase { idle, loading, error }
|
||||
/// 评论列表只属详情页,按「页面级状态按页自建」纪律经 [repository]
|
||||
/// 自取,不膨胀本控制器。服务端是唯一事实来源,内存副本仅作展示缓存。
|
||||
class CommunityController extends ChangeNotifier {
|
||||
CommunityController({required this._repository}) {
|
||||
CommunityController({required this._repository, this._interactionAnalytics}) {
|
||||
_likeSync = ToggleSync(
|
||||
read: (id) {
|
||||
final post = _postCache[id];
|
||||
@@ -35,6 +36,11 @@ class CommunityController extends ChangeNotifier {
|
||||
final state = target
|
||||
? await _repository.likePost(id)
|
||||
: await _repository.unlikePost(id);
|
||||
// 成功响应后上报(06 §1.4 口径;乐观翻转与失败均不报)。
|
||||
final source = _likeSources[id] ?? InteractionSource.feed;
|
||||
target
|
||||
? _interactionAnalytics?.postLiked(source: source)
|
||||
: _interactionAnalytics?.postUnliked(source: source);
|
||||
return ToggleOutcome(active: state.liked, count: state.likeCount);
|
||||
},
|
||||
generation: () => _generation,
|
||||
@@ -62,6 +68,10 @@ class CommunityController extends ChangeNotifier {
|
||||
final state = target
|
||||
? await _repository.bookmarkPost(id)
|
||||
: await _repository.unbookmarkPost(id);
|
||||
final source = _bookmarkSources[id] ?? InteractionSource.feed;
|
||||
target
|
||||
? _interactionAnalytics?.postFavorited(source: source)
|
||||
: _interactionAnalytics?.postUnfavorited(source: source);
|
||||
return ToggleOutcome(
|
||||
active: state.bookmarked,
|
||||
count: state.bookmarkCount,
|
||||
@@ -73,6 +83,12 @@ class CommunityController extends ChangeNotifier {
|
||||
}
|
||||
|
||||
final CommunityRepository _repository;
|
||||
final CommunityInteractionAnalytics? _interactionAnalytics;
|
||||
|
||||
/// 各帖最近一次 toggle 的触点来源(Feed 卡片 / 详情页共享同一实例,
|
||||
/// 成功响应上报时按发起触点归因)。
|
||||
final Map<String, InteractionSource> _likeSources = {};
|
||||
final Map<String, InteractionSource> _bookmarkSources = {};
|
||||
|
||||
/// 页面级状态(评论列表、我的帖子、收藏页等)按页直接经仓库取数。
|
||||
CommunityRepository get repository => _repository;
|
||||
@@ -193,10 +209,34 @@ class CommunityController extends ChangeNotifier {
|
||||
}
|
||||
|
||||
/// 点赞/取消点赞(乐观翻转,终态由 [ToggleSync] 对账收敛,不外抛)。
|
||||
void toggleLike(String postId) => _likeSync.toggle(postId);
|
||||
/// [source] 为触点来源(埋点归因),Feed 卡片缺省 feed、详情页传
|
||||
/// post_detail。
|
||||
void toggleLike(
|
||||
String postId, {
|
||||
InteractionSource source = InteractionSource.feed,
|
||||
}) {
|
||||
_likeSources[postId] = source;
|
||||
_likeSync.toggle(postId);
|
||||
}
|
||||
|
||||
/// 收藏/取消收藏(与点赞同构)。
|
||||
void toggleBookmark(String postId) => _bookmarkSync.toggle(postId);
|
||||
void toggleBookmark(
|
||||
String postId, {
|
||||
InteractionSource source = InteractionSource.feed,
|
||||
}) {
|
||||
_bookmarkSources[postId] = source;
|
||||
_bookmarkSync.toggle(postId);
|
||||
}
|
||||
|
||||
/// 评论创建/删除后的计数调整(详情页与 Feed 卡片同源写入,
|
||||
/// 服务端 comment_count 由写侧同事务维护,本地只做展示对齐)。
|
||||
void adjustCommentCount(String postId, int delta) {
|
||||
final current =
|
||||
_postCache[postId]?.commentCount ?? _cardOrNull(postId)?.commentCount;
|
||||
if (current == null) return;
|
||||
final next = current + delta;
|
||||
_writeInteraction(postId, commentCount: next < 0 ? 0 : next);
|
||||
}
|
||||
|
||||
/// 消费一次性 toggle 错误(SnackBar 展示后清除)。
|
||||
void clearToggleError() => _toggleError = null;
|
||||
@@ -215,6 +255,8 @@ class CommunityController extends ChangeNotifier {
|
||||
_loadMoreError = null;
|
||||
_toggleError = null;
|
||||
_postCache.clear();
|
||||
_likeSources.clear();
|
||||
_bookmarkSources.clear();
|
||||
_likeSync.reset();
|
||||
_bookmarkSync.reset();
|
||||
_notify();
|
||||
@@ -234,6 +276,7 @@ class CommunityController extends ChangeNotifier {
|
||||
int? likeCount,
|
||||
bool? bookmarkedByMe,
|
||||
int? bookmarkCount,
|
||||
int? commentCount,
|
||||
}) {
|
||||
final post = _postCache[postId];
|
||||
if (post != null) {
|
||||
@@ -242,6 +285,7 @@ class CommunityController extends ChangeNotifier {
|
||||
likeCount: likeCount,
|
||||
bookmarkedByMe: bookmarkedByMe,
|
||||
bookmarkCount: bookmarkCount,
|
||||
commentCount: commentCount,
|
||||
);
|
||||
}
|
||||
final index = _feed.indexWhere((card) => card.id == postId);
|
||||
@@ -252,6 +296,7 @@ class CommunityController extends ChangeNotifier {
|
||||
likeCount: likeCount,
|
||||
bookmarkedByMe: bookmarkedByMe,
|
||||
bookmarkCount: bookmarkCount,
|
||||
commentCount: commentCount,
|
||||
);
|
||||
}
|
||||
if (post != null || index != -1) _notify();
|
||||
|
||||
Reference in New Issue
Block a user