feat(player): 点赞与评论改为按集

This commit is contained in:
Zhou Weidong committed 2026-10-21 16:15:29 +08:00
1 parent e6f34977f5
commit 83822a0cbb
25 files changed
+337 -69

No files matched your search

@@ -116,6 +116,12 @@ class _DramaDetailPageState extends State<DramaDetailPage> {
final saved = context.watch<FavoriteController>().isSaved(drama.id);
final continueIndex = logic.continueIndex;
final episodesReady = drama.detailLoaded && drama.episodes.isNotEmpty;
final statsEpisode = episodesReady
? drama.episodes[continueIndex != null &&
continueIndex < drama.episodes.length
? continueIndex
: 0]
: null;
return Scaffold(
backgroundColor: AppColors.ink,
bottomNavigationBar: DramaDetailBottomBar(
@@ -146,6 +152,7 @@ class _DramaDetailPageState extends State<DramaDetailPage> {
l10n: l10n,
drama: drama,
favoriteCount: logic.adjustedFavoriteCount,
likeCount: statsEpisode?.likeCount,
),
),
SliverToBoxAdapter(
@@ -34,11 +34,13 @@ class DramaStatsCard extends StatelessWidget {
required this.l10n,
required this.drama,
this.favoriteCount,
this.likeCount,
});
final AppLocalizations l10n;
final Drama drama;
final int? favoriteCount;
final int? likeCount;
@override
Widget build(BuildContext context) {
@@ -46,9 +48,9 @@ class DramaStatsCard extends StatelessWidget {
final favorite = currentFavoriteCount == null
? l10n.episodeCountLabel(drama.episodeCount)
: '${formatInteractionCount(currentFavoriteCount)} ${l10n.favorite}';
final like = drama.likeCount == null
final like = likeCount == null
? '—'
: '${formatInteractionCount(drama.likeCount!)} ${l10n.like}';
: '${formatInteractionCount(likeCount!)} ${l10n.like}';
return Container(
key: const Key('detail-stats'),
margin: const EdgeInsets.fromLTRB(
+11 -3
View File
@@ -230,7 +230,11 @@ class PlayerLogic extends ChangeNotifier {
qualities = ticket.qualities;
if (ticket.accessible && ticket.playUrl.isNotEmpty) {
// 签名地址原样写回,不做任何拼接/改写(AppConfig.mediaUrl 只补 host)
sessionEpisodes[index] = episode.withVideoUrl(ticket.playUrl);
sessionEpisodes[index] = episode.withVideoUrl(
ticket.playUrl,
likeCount: ticket.likeCount,
commentCount: ticket.commentCount,
);
return true;
}
blockedTicket = ticket;
@@ -797,7 +801,11 @@ class PlayerLogic extends ChangeNotifier {
notify();
}
Future<void> showComments(BuildContext context, Drama drama) async {
Future<void> showComments(
BuildContext context,
Drama drama, {
String? episodeId,
}) async {
session.setOverlayOpen(true);
await session.playbackSettled;
if (!context.mounted) {
@@ -812,7 +820,7 @@ class PlayerLogic extends ChangeNotifier {
showDragHandle: false,
isDismissible: false,
enableDrag: false,
builder: (_) => DramaCommentsSheet(drama: drama),
builder: (_) => DramaCommentsSheet(drama: drama, episodeId: episodeId),
);
} finally {
session.setOverlayOpen(false);
+27 -14
View File
@@ -205,13 +205,22 @@ class _PlayerPageState extends State<PlayerPage>
});
}
Future<void> _openComments(BuildContext context, Drama drama) async {
Future<void> _openComments(
BuildContext context,
Drama drama,
Episode episode,
) async {
final comments = context.read<CommentRepository>();
await openCommentsWithAuth(context, drama, show: logic.showComments);
await openCommentsWithAuth(
context,
drama,
show: (context, drama) =>
logic.showComments(context, drama, episodeId: episode.id),
);
try {
final count = await comments.count(drama.id);
final count = await comments.count(drama.id, episodeId: episode.id);
if (!mounted) return;
setState(() => _commentCounts[drama.id] = count);
setState(() => _commentCounts[episode.id] = count);
} catch (_) {
// 评论面板已经关闭,计数刷新失败时保留旧值即可。
}
@@ -456,18 +465,19 @@ class _PlayerPageState extends State<PlayerPage>
onBoostEnd: _endBoost,
onTogglePlay: () =>
logic.session.setUserWantsPlay(!logic.session.userWantsPlay),
liked: interactions.isLiked(drama.id),
likeCount: interactions.likeCount(drama.id) ?? drama.likeCount,
liked: interactions.isEpisodeLiked(episode.id),
likeCount:
interactions.episodeLikeCount(episode.id) ?? episode.likeCount,
favoriteCount: drama.favoriteCount == null
? null
: drama.favoriteCount! + (_favoriteDelta[drama.id] ?? 0),
commentCount: _commentCounts[drama.id] ?? drama.commentCount,
commentCount: _commentCounts[episode.id] ?? episode.commentCount,
saved: context.watch<FavoriteController>().isSaved(drama.id),
onLike: () async {
if (await ensureSignedIn(context)) {
if (!context.mounted) return;
unawaited(context.read<Haptics>().lightImpact());
final result = await interactions.toggleDramaLike(drama.id);
final result = await interactions.toggleEpisodeLike(episode.id);
if (!result.isSuccess && context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
@@ -481,7 +491,7 @@ class _PlayerPageState extends State<PlayerPage>
}
},
onSave: () => unawaited(_toggleSave(context, drama)),
onComments: () => unawaited(_openComments(context, drama)),
onComments: () => unawaited(_openComments(context, drama, episode)),
onShare: () => logic.shareDrama(context, drama),
onDetail: () => unawaited(logic.openDetail(context)),
onOpenFullPlayer: () async {
@@ -502,9 +512,11 @@ class _PlayerPageState extends State<PlayerPage>
drama: drama,
episode: episode,
controller: ready ? videoPort!.controller : null,
liked: interactions.isLiked(drama.id),
likeCount: interactions.likeCount(drama.id) ?? drama.likeCount,
commentCount: _commentCounts[drama.id] ?? drama.commentCount,
liked: interactions.isEpisodeLiked(episode.id),
likeCount:
interactions.episodeLikeCount(episode.id) ??
episode.likeCount,
commentCount: _commentCounts[episode.id] ?? episode.commentCount,
favoriteCount: drama.favoriteCount == null
? null
: drama.favoriteCount! + (_favoriteDelta[drama.id] ?? 0),
@@ -525,9 +537,10 @@ class _PlayerPageState extends State<PlayerPage>
onDetail: () => unawaited(logic.openDetail(context)),
onSpeed: () => unawaited(_openSpeedSheet(context)),
onEpisodes: () => logic.showEpisodes(context),
onComments: () => unawaited(_openComments(context, drama)),
onComments: () =>
unawaited(_openComments(context, drama, episode)),
onLike: () =>
unawaited(_toggleLike(context, interactions, drama)),
unawaited(_toggleLike(context, interactions, episode)),
onSave: () => unawaited(_toggleSave(context, drama)),
onShare: () => logic.shareDrama(context, drama),
onMore: () => _openMore(context, episode),
+2 -2
View File
@@ -81,11 +81,11 @@ extension _PlayerViewActions on _PlayerPageState {
Future<void> _toggleLike(
BuildContext context,
InteractionController interactions,
Drama drama,
Episode episode,
) async {
if (!await ensureSignedIn(context) || !context.mounted) return;
unawaited(context.read<Haptics>().lightImpact());
final result = await interactions.toggleDramaLike(drama.id);
final result = await interactions.toggleEpisodeLike(episode.id);
if (result.isSuccess || !context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
@@ -31,9 +31,10 @@ const double _composerTopGap = 8;
/// 服务端评论底部弹窗:匿名列表、游标翻页、回复与登录发布。
class DramaCommentsSheet extends StatefulWidget {
const DramaCommentsSheet({super.key, required this.drama});
const DramaCommentsSheet({super.key, required this.drama, this.episodeId});
final Drama drama;
final String? episodeId;
@override
State<DramaCommentsSheet> createState() => _DramaCommentsSheetState();
@@ -56,6 +57,7 @@ class _DramaCommentsSheetState extends State<DramaCommentsSheet> {
comments: _comments,
interactions: context.read<InteractionRepository>(),
dramaId: widget.drama.id,
episodeId: widget.episodeId,
);
unawaited(_controller.load());
unawaited(_refreshCount());
@@ -77,7 +79,10 @@ class _DramaCommentsSheetState extends State<DramaCommentsSheet> {
Future<void> _refreshCount() async {
final request = ++_countRequest;
try {
final count = await _comments.count(widget.drama.id);
final count = await _comments.count(
widget.drama.id,
episodeId: widget.episodeId,
);
if (!mounted || request != _countRequest) return;
setState(() => _serverCount = count);
} catch (_) {
@@ -464,8 +469,9 @@ class _CommentThread extends StatelessWidget {
Text(
expanded
? AppLocalizations.of(context)!.hideReplies
: AppLocalizations.of(context)!
.viewReplies(comment.replyCount),
: AppLocalizations.of(
context,
)!.viewReplies(comment.replyCount),
style: const TextStyle(
color: AppColors.muted,
fontSize: 12,