fix(favorite): 统一收藏状态与互动图标
This commit is contained in:
1 parent
4b97f89c0b
commit
d3dbc9f8dc
13 files changed
+347
-213
No files matched your search
@@ -18,6 +18,7 @@ class FavoriteController extends ChangeNotifier {
|
||||
final FavoriteRepository _repository;
|
||||
final int pageSize;
|
||||
final Set<String> _saved = {};
|
||||
final Map<String, int> _favoriteCounts = {};
|
||||
List<Drama> cards = const [];
|
||||
int total = 0;
|
||||
bool loading = false;
|
||||
@@ -34,17 +35,53 @@ class FavoriteController extends ChangeNotifier {
|
||||
|
||||
bool isSaved(String dramaId) => _saved.contains(dramaId);
|
||||
|
||||
/// 用统计快照校准收藏状态;已有列表卡片结构不因一次状态同步而改动。
|
||||
void applyServerState(String dramaId, {required bool saved}) {
|
||||
final changed = _saved.contains(dramaId) != saved;
|
||||
/// 当前剧收藏数;包含本地 toggle 增量。
|
||||
int? favoriteCount(String dramaId) => _favoriteCounts[dramaId];
|
||||
|
||||
/// 首次展示时缓存卡片基数;之后统一由本控制器维护,避免页面各存一份。
|
||||
int? favoriteCountFor(Drama drama) {
|
||||
final current = _favoriteCounts[drama.id];
|
||||
if (current != null) return current;
|
||||
final fallback = drama.favoriteCount;
|
||||
if (fallback == null) return null;
|
||||
final normalized = fallback < 0 ? 0 : fallback;
|
||||
_favoriteCounts[drama.id] = normalized;
|
||||
return normalized;
|
||||
}
|
||||
|
||||
/// 用 stats/detail 的服务端快照校准唯一状态和计数。
|
||||
void applyServerState(
|
||||
String dramaId, {
|
||||
required bool saved,
|
||||
int? favoriteCount,
|
||||
}) {
|
||||
var changed = _saved.contains(dramaId) != saved;
|
||||
if (saved) {
|
||||
_saved.add(dramaId);
|
||||
} else {
|
||||
_saved.remove(dramaId);
|
||||
}
|
||||
if (favoriteCount != null) {
|
||||
final normalized = favoriteCount < 0 ? 0 : favoriteCount;
|
||||
changed = changed || _favoriteCounts[dramaId] != normalized;
|
||||
_favoriteCounts[dramaId] = normalized;
|
||||
}
|
||||
if (changed) notify();
|
||||
}
|
||||
|
||||
/// 详情等只有计数、没有当前收藏状态的接口使用。
|
||||
void applyServerCount(
|
||||
String dramaId,
|
||||
int? favoriteCount, {
|
||||
bool notify = true,
|
||||
}) {
|
||||
if (favoriteCount == null) return;
|
||||
final normalized = favoriteCount < 0 ? 0 : favoriteCount;
|
||||
if (_favoriteCounts[dramaId] == normalized) return;
|
||||
_favoriteCounts[dramaId] = normalized;
|
||||
if (notify) this.notify();
|
||||
}
|
||||
|
||||
void notify() {
|
||||
if (!_disposed) notifyListeners();
|
||||
}
|
||||
@@ -65,6 +102,7 @@ class FavoriteController extends ChangeNotifier {
|
||||
_saved
|
||||
..clear()
|
||||
..addAll(list.map((drama) => drama.id));
|
||||
_rememberCardCounts(list);
|
||||
total = page.total;
|
||||
_pageIndex = 1;
|
||||
hasMore = page.list.isNotEmpty && cards.length < total;
|
||||
@@ -99,6 +137,7 @@ class FavoriteController extends ChangeNotifier {
|
||||
).where((drama) => known.add(drama.id)).toList(growable: false);
|
||||
cards = [...cards, ...appended];
|
||||
_saved.addAll(appended.map((drama) => drama.id));
|
||||
_rememberCardCounts(appended);
|
||||
total = page.total;
|
||||
_pageIndex += 1;
|
||||
hasMore = page.list.isNotEmpty && cards.length < total;
|
||||
@@ -121,12 +160,16 @@ class FavoriteController extends ChangeNotifier {
|
||||
Future<bool> toggle(String dramaId, {Drama? card}) async {
|
||||
lastErrorCode = null;
|
||||
final wasSaved = _saved.contains(dramaId);
|
||||
final previousCount = card == null
|
||||
? favoriteCount(dramaId)
|
||||
: favoriteCountFor(card);
|
||||
final previousCards = cards;
|
||||
final previousTotal = total;
|
||||
final previousHasMore = hasMore;
|
||||
// 乐观更新
|
||||
if (wasSaved) {
|
||||
_saved.remove(dramaId);
|
||||
_stepCount(dramaId, -1);
|
||||
if (cards.any((drama) => drama.id == dramaId)) {
|
||||
cards = cards.where((drama) => drama.id != dramaId).toList();
|
||||
total = total > 0 ? total - 1 : 0;
|
||||
@@ -134,6 +177,7 @@ class FavoriteController extends ChangeNotifier {
|
||||
}
|
||||
} else {
|
||||
_saved.add(dramaId);
|
||||
_stepCount(dramaId, 1);
|
||||
}
|
||||
notify();
|
||||
try {
|
||||
@@ -142,6 +186,7 @@ class FavoriteController extends ChangeNotifier {
|
||||
if (saved) {
|
||||
_saved.add(dramaId);
|
||||
if (wasSaved) {
|
||||
_restoreCount(dramaId, previousCount);
|
||||
cards = previousCards;
|
||||
total = previousTotal;
|
||||
hasMore = previousHasMore;
|
||||
@@ -152,6 +197,9 @@ class FavoriteController extends ChangeNotifier {
|
||||
}
|
||||
} else {
|
||||
_saved.remove(dramaId);
|
||||
if (!wasSaved) {
|
||||
_restoreCount(dramaId, previousCount);
|
||||
}
|
||||
if (cards.any((drama) => drama.id == dramaId)) {
|
||||
cards = cards.where((drama) => drama.id != dramaId).toList();
|
||||
total = total > 0 ? total - 1 : 0;
|
||||
@@ -170,6 +218,7 @@ class FavoriteController extends ChangeNotifier {
|
||||
} else {
|
||||
_saved.remove(dramaId);
|
||||
}
|
||||
_restoreCount(dramaId, previousCount);
|
||||
notify();
|
||||
if (failure is ApiException) lastErrorCode = failure.code;
|
||||
return false;
|
||||
@@ -185,9 +234,13 @@ class FavoriteController extends ChangeNotifier {
|
||||
final previousHasMore = hasMore;
|
||||
final removed = cards.where((drama) => ids.contains(drama.id)).toList();
|
||||
if (removed.isEmpty) return true;
|
||||
final previousCounts = {
|
||||
for (final drama in removed) drama.id: _favoriteCounts[drama.id],
|
||||
};
|
||||
cards = cards.where((drama) => !ids.contains(drama.id)).toList();
|
||||
for (final id in ids) {
|
||||
_saved.remove(id);
|
||||
_stepCount(id, -1);
|
||||
}
|
||||
total = total > removed.length ? total - removed.length : 0;
|
||||
hasMore = cards.length < total;
|
||||
@@ -202,6 +255,9 @@ class FavoriteController extends ChangeNotifier {
|
||||
for (final drama in removed) {
|
||||
_saved.add(drama.id);
|
||||
}
|
||||
for (final entry in previousCounts.entries) {
|
||||
_restoreCount(entry.key, entry.value);
|
||||
}
|
||||
notify();
|
||||
return false;
|
||||
}
|
||||
@@ -212,7 +268,9 @@ class FavoriteController extends ChangeNotifier {
|
||||
final index = cards.indexWhere((drama) => drama.id == dramaId);
|
||||
if (index < 0) return true;
|
||||
final removed = cards[index];
|
||||
final previousCount = _favoriteCounts[dramaId];
|
||||
_saved.remove(dramaId);
|
||||
_stepCount(dramaId, -1);
|
||||
final next = [...cards]..removeAt(index);
|
||||
cards = next;
|
||||
total = total > 0 ? total - 1 : 0;
|
||||
@@ -229,6 +287,7 @@ class FavoriteController extends ChangeNotifier {
|
||||
);
|
||||
cards = restored;
|
||||
_saved.add(dramaId);
|
||||
_restoreCount(dramaId, previousCount);
|
||||
total += 1;
|
||||
hasMore = cards.length < total;
|
||||
notify();
|
||||
@@ -259,6 +318,30 @@ class FavoriteController extends ChangeNotifier {
|
||||
];
|
||||
}
|
||||
|
||||
void _rememberCardCounts(List<Drama> dramas) {
|
||||
for (final drama in dramas) {
|
||||
final count = drama.favoriteCount;
|
||||
if (count != null && !_favoriteCounts.containsKey(drama.id)) {
|
||||
_favoriteCounts[drama.id] = count < 0 ? 0 : count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _stepCount(String dramaId, int delta) {
|
||||
final current = _favoriteCounts[dramaId];
|
||||
if (current == null) return;
|
||||
final next = current + delta;
|
||||
_favoriteCounts[dramaId] = next < 0 ? 0 : next;
|
||||
}
|
||||
|
||||
void _restoreCount(String dramaId, int? count) {
|
||||
if (count == null) {
|
||||
_favoriteCounts.remove(dramaId);
|
||||
} else {
|
||||
_favoriteCounts[dramaId] = count;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_disposed = true;
|
||||
|
||||
Reference in new issue
Block a user