feat(player): 点赞与评论改为按集
This commit is contained in:
1 parent
e6f34977f5
commit
83822a0cbb
25 files changed
+337
-69
No files matched your search
@@ -20,12 +20,16 @@ void main() {
|
||||
'title': '第 1 集',
|
||||
'isFree': true,
|
||||
'playUrl': '/files/episode/9.mp4',
|
||||
'likeCount': '18',
|
||||
'commentCount': 4,
|
||||
},
|
||||
});
|
||||
|
||||
expect(drama.detailLoaded, isFalse);
|
||||
expect(drama.episodes, hasLength(1));
|
||||
expect(drama.episodes.single.videoUrl, '/files/episode/9.mp4');
|
||||
expect(drama.episodes.single.likeCount, 18);
|
||||
expect(drama.episodes.single.commentCount, 4);
|
||||
expect(drama.episodeCount, 77);
|
||||
expect(drama.isHot, isTrue);
|
||||
expect(drama.language, 'zh-CN');
|
||||
@@ -50,7 +54,14 @@ void main() {
|
||||
'cover': '/files/cover.jpg',
|
||||
'totalEpisodes': 77,
|
||||
'episodes': [
|
||||
{'id': 9, 'number': 1, 'title': '第 1 集', 'isFree': true},
|
||||
{
|
||||
'id': 9,
|
||||
'number': 1,
|
||||
'title': '第 1 集',
|
||||
'isFree': true,
|
||||
'likeCount': '21',
|
||||
'commentCount': '6',
|
||||
},
|
||||
{'id': 10, 'number': 2, 'title': '第 2 集', 'isFree': false},
|
||||
],
|
||||
});
|
||||
@@ -58,5 +69,7 @@ void main() {
|
||||
expect(drama.detailLoaded, isTrue);
|
||||
expect(drama.episodeCount, 77);
|
||||
expect(drama.episodes.first.id, '9');
|
||||
expect(drama.episodes.first.likeCount, 21);
|
||||
expect(drama.episodes.first.commentCount, 6);
|
||||
});
|
||||
}
|
||||
@@ -55,12 +55,18 @@ void main() {
|
||||
ApiClient(baseUrl: 'http://127.0.0.1:8081', adapter: adapter),
|
||||
);
|
||||
|
||||
final page = await api.page(dramaId: '5', cursor: '99', size: 20);
|
||||
final page = await api.page(
|
||||
dramaId: '5',
|
||||
episodeId: '7',
|
||||
cursor: '99',
|
||||
size: 20,
|
||||
);
|
||||
|
||||
expect(adapter.requests.single.method, 'GET');
|
||||
expect(adapter.requests.single.uri.path, '/app/comments');
|
||||
expect(adapter.requests.single.queryParameters, {
|
||||
'dramaId': '5',
|
||||
'episodeId': '7',
|
||||
'cursor': '99',
|
||||
'size': '20',
|
||||
});
|
||||
@@ -93,6 +99,7 @@ void main() {
|
||||
|
||||
final reply = await api.publish(
|
||||
dramaId: '5',
|
||||
episodeId: '7',
|
||||
content: 'great badword',
|
||||
parentId: '2111012753262120962',
|
||||
);
|
||||
@@ -101,6 +108,7 @@ void main() {
|
||||
expect(adapter.requests.single.uri.path, '/app/comments');
|
||||
expect(adapter.requests.single.data, {
|
||||
'dramaId': '5',
|
||||
'episodeId': '7',
|
||||
'content': 'great badword',
|
||||
'parentId': '2111012753262120962',
|
||||
});
|
||||
@@ -135,12 +143,15 @@ void main() {
|
||||
ApiClient(baseUrl: 'http://127.0.0.1:8081', adapter: adapter),
|
||||
);
|
||||
|
||||
final count = await api.count('5');
|
||||
final count = await api.count('5', episodeId: '7');
|
||||
|
||||
expect(count, 17);
|
||||
expect(adapter.requests.single.method, 'GET');
|
||||
expect(adapter.requests.single.uri.path, '/app/comments/count');
|
||||
expect(adapter.requests.single.queryParameters, {'dramaId': '5'});
|
||||
expect(adapter.requests.single.queryParameters, {
|
||||
'dramaId': '5',
|
||||
'episodeId': '7',
|
||||
});
|
||||
});
|
||||
|
||||
test('should_parse_string_comment_count', () async {
|
||||
|
||||
@@ -12,10 +12,12 @@ void main() {
|
||||
CommentController controller(
|
||||
FakeCommentRepository comments, {
|
||||
FakeInteractionRepository? interactions,
|
||||
String? episodeId,
|
||||
}) => CommentController(
|
||||
comments: comments,
|
||||
interactions: interactions ?? FakeInteractionRepository(),
|
||||
dramaId: '5',
|
||||
episodeId: episodeId,
|
||||
);
|
||||
|
||||
test('should_append_next_page_with_cursor_when_has_more_is_true', () async {
|
||||
@@ -63,6 +65,42 @@ void main() {
|
||||
model.dispose();
|
||||
});
|
||||
|
||||
test('should_pass_episode_id_to_every_page_and_publish_request', () async {
|
||||
final root = commentFixture(id: '10', replyCount: 1);
|
||||
final repository = FakeCommentRepository(
|
||||
pages: [
|
||||
CommentPage(
|
||||
comments: [root],
|
||||
nextCursor: '10',
|
||||
hasMore: true,
|
||||
),
|
||||
CommentPage(comments: [commentFixture(id: '9')]),
|
||||
CommentPage(
|
||||
comments: [commentFixture(id: '11', rootId: '10')],
|
||||
nextCursor: '11',
|
||||
hasMore: true,
|
||||
),
|
||||
CommentPage(comments: [commentFixture(id: '12', rootId: '10')]),
|
||||
],
|
||||
publishResult: commentFixture(id: '13', mine: true),
|
||||
);
|
||||
final model = controller(repository, episodeId: 'episode-7');
|
||||
|
||||
await model.load();
|
||||
await model.loadMore();
|
||||
await model.loadReplies('10');
|
||||
await model.loadMoreReplies('10');
|
||||
await model.publish('Nice episode');
|
||||
|
||||
expect(
|
||||
repository.pageCalls.map((call) => call.episodeId),
|
||||
everyElement('episode-7'),
|
||||
);
|
||||
expect(repository.pageCalls, hasLength(4));
|
||||
expect(repository.publishCalls.single.episodeId, 'episode-7');
|
||||
model.dispose();
|
||||
});
|
||||
|
||||
test('should_use_server_content_and_pending_status_when_review_is_returned', () async {
|
||||
final repository = FakeCommentRepository(
|
||||
publishResult: commentFixture(
|
||||
|
||||
@@ -239,6 +239,36 @@ void main() {
|
||||
expect(repository.countCalls, ['5']);
|
||||
});
|
||||
|
||||
testWidgets('should_scope_list_count_and_publish_to_episode', (tester) async {
|
||||
final repository = FakeCommentRepository(
|
||||
pages: [const CommentPage()],
|
||||
countResult: 3,
|
||||
publishResult: commentFixture(
|
||||
id: '11',
|
||||
content: 'Episode comment',
|
||||
mine: true,
|
||||
),
|
||||
);
|
||||
|
||||
await _pumpSheet(tester, repository: repository, episodeId: 'episode-7');
|
||||
|
||||
expect(repository.pageCalls.single.episodeId, 'episode-7');
|
||||
expect(repository.countEpisodeCalls, ['episode-7']);
|
||||
|
||||
await tester.tap(find.byKey(const Key('comment-input-placeholder')));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.enterText(
|
||||
find.byKey(const Key('comment-composer-input')),
|
||||
'Episode comment',
|
||||
);
|
||||
await tester.pump();
|
||||
await tester.tap(find.byKey(const Key('comment-composer-post')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(repository.publishCalls.single.episodeId, 'episode-7');
|
||||
expect(repository.countEpisodeCalls, ['episode-7', 'episode-7']);
|
||||
});
|
||||
|
||||
testWidgets('should_fall_back_to_exact_count_when_server_total_fails', (
|
||||
tester,
|
||||
) async {
|
||||
@@ -964,6 +994,7 @@ Future<AuthController> _pumpSheet(
|
||||
required FakeCommentRepository repository,
|
||||
FakeHaptics? haptics,
|
||||
Locale locale = const Locale('en'),
|
||||
String? episodeId,
|
||||
}) async {
|
||||
final auth = await _auth();
|
||||
await tester.pumpWidget(
|
||||
@@ -983,12 +1014,12 @@ Future<AuthController> _pumpSheet(
|
||||
initialRoute: '/comments',
|
||||
routes: {
|
||||
'/': (_) => const Scaffold(backgroundColor: AppColors.ink),
|
||||
'/comments': (_) => const Scaffold(
|
||||
'/comments': (_) => Scaffold(
|
||||
backgroundColor: AppColors.ink,
|
||||
resizeToAvoidBottomInset: false,
|
||||
body: Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: DramaCommentsSheet(drama: _drama),
|
||||
child: DramaCommentsSheet(drama: _drama, episodeId: episodeId),
|
||||
),
|
||||
),
|
||||
},
|
||||
|
||||
@@ -158,6 +158,8 @@ Drama _detailDrama(Drama card) => Drama(
|
||||
isFree: number == 1,
|
||||
videoUrl: number == 1 ? '/files/${card.id}-1.mp4' : '',
|
||||
locked: number > 1,
|
||||
likeCount: card.likeCount,
|
||||
commentCount: card.commentCount,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
@@ -463,6 +463,8 @@ Drama _detailDrama({
|
||||
durationSec: 135,
|
||||
locked: number > 1,
|
||||
videoUrl: number == 1 ? '/files/detail-k38-1.mp4' : '',
|
||||
likeCount: withCounts ? 12400 : null,
|
||||
commentCount: withCounts ? 96 : null,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
@@ -62,6 +62,56 @@ void main() {
|
||||
controller.dispose();
|
||||
});
|
||||
|
||||
test('should_toggle_episode_like_with_plus_one_and_minus_one', () async {
|
||||
final repository = FakeInteractionRepository(
|
||||
nextLikeResult: const LikeToggleResult(liked: true, count: 8),
|
||||
);
|
||||
final controller = InteractionController(repository);
|
||||
|
||||
await controller.toggleEpisodeLike('episode-5');
|
||||
|
||||
expect(repository.likeTargets, ['EPISODE:episode-5']);
|
||||
expect(controller.isEpisodeLiked('episode-5'), isTrue);
|
||||
expect(controller.episodeLikeCount('episode-5'), 8);
|
||||
expect(controller.isLiked('episode-5'), isFalse);
|
||||
|
||||
repository.nextLikeResult = const LikeToggleResult(liked: false, count: 7);
|
||||
await controller.toggleEpisodeLike('episode-5');
|
||||
|
||||
expect(controller.isEpisodeLiked('episode-5'), isFalse);
|
||||
expect(controller.episodeLikeCount('episode-5'), 7);
|
||||
controller.dispose();
|
||||
});
|
||||
|
||||
test('should_isolate_episode_like_cache_by_episode_and_drama', () async {
|
||||
final repository = FakeInteractionRepository(
|
||||
nextLikeResult: const LikeToggleResult(liked: true, count: 8),
|
||||
);
|
||||
final controller = InteractionController(repository);
|
||||
|
||||
await controller.toggleEpisodeLike('episode-1');
|
||||
repository.nextLikeResult = const LikeToggleResult(liked: true, count: 3);
|
||||
await controller.toggleEpisodeLike('episode-2');
|
||||
repository.nextLikeResult = const LikeToggleResult(liked: false, count: 7);
|
||||
await controller.toggleEpisodeLike('episode-1');
|
||||
repository.nextLikeResult = const LikeToggleResult(liked: true, count: 12);
|
||||
await controller.toggleDramaLike('episode-1');
|
||||
|
||||
expect(controller.isEpisodeLiked('episode-1'), isFalse);
|
||||
expect(controller.episodeLikeCount('episode-1'), 7);
|
||||
expect(controller.isEpisodeLiked('episode-2'), isTrue);
|
||||
expect(controller.episodeLikeCount('episode-2'), 3);
|
||||
expect(controller.isLiked('episode-1'), isTrue);
|
||||
expect(controller.likeCount('episode-1'), 12);
|
||||
expect(repository.likeTargets, [
|
||||
'EPISODE:episode-1',
|
||||
'EPISODE:episode-2',
|
||||
'EPISODE:episode-1',
|
||||
'DRAMA:episode-1',
|
||||
]);
|
||||
controller.dispose();
|
||||
});
|
||||
|
||||
test('should_clear_account_scoped_drama_likes_when_session_changes', () async {
|
||||
final repository = FakeInteractionRepository(
|
||||
nextLikeResult: const LikeToggleResult(liked: true, count: 1),
|
||||
|
||||
@@ -11,9 +11,13 @@ void main() {
|
||||
'accessType': 'FREE',
|
||||
'playUrl': '/files/episode/12.m3u8?e=1&s=abc',
|
||||
'qualities': ['AUTO', '720P', '1080P'],
|
||||
'likeCount': '17',
|
||||
'commentCount': 5,
|
||||
});
|
||||
|
||||
expect(ticket.qualities, ['AUTO', '720P', '1080P']);
|
||||
expect(ticket.likeCount, 17);
|
||||
expect(ticket.commentCount, 5);
|
||||
});
|
||||
|
||||
test('should_default_to_empty_qualities_when_field_is_missing', () {
|
||||
|
||||
@@ -637,12 +637,10 @@ void main() {
|
||||
'auth.refreshToken': 'test-refresh',
|
||||
'auth.user': '{"uid":"9","nickname":"Me","avatarUrl":""}',
|
||||
});
|
||||
await _pumpApp(
|
||||
tester,
|
||||
interactions: FakeInteractionRepository(
|
||||
nextLikeResult: const LikeToggleResult(liked: true, count: 23000),
|
||||
),
|
||||
final interactions = FakeInteractionRepository(
|
||||
nextLikeResult: const LikeToggleResult(liked: true, count: 23000),
|
||||
);
|
||||
await _pumpApp(tester, interactions: interactions);
|
||||
await _openSeriesPlayer(tester);
|
||||
|
||||
String railLabel(Key key) => tester
|
||||
@@ -659,6 +657,7 @@ void main() {
|
||||
await tester.tap(find.byKey(const Key('player-rail-like')));
|
||||
await tester.pumpAndSettle();
|
||||
expect(railLabel(const Key('player-rail-like')), '2.3万');
|
||||
expect(interactions.likeTargets, contains('EPISODE:city-lights-demo-1'));
|
||||
|
||||
await tester.tap(find.byKey(const Key('player-rail-save')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
@@ -95,6 +95,8 @@ Drama _drama(
|
||||
title: 'Episode ${index + 1}',
|
||||
isFree: true,
|
||||
videoUrl: 'https://example.test/$format-$index.mp4',
|
||||
likeCount: likeCount,
|
||||
commentCount: commentCount,
|
||||
),
|
||||
],
|
||||
);
|
||||
@@ -10,12 +10,14 @@ import 'package:fruitshorts/core/data/repositories/interaction_repository.dart';
|
||||
class CommentPageCall {
|
||||
const CommentPageCall({
|
||||
required this.dramaId,
|
||||
required this.episodeId,
|
||||
required this.rootId,
|
||||
required this.cursor,
|
||||
required this.size,
|
||||
});
|
||||
|
||||
final String dramaId;
|
||||
final String? episodeId;
|
||||
final String? rootId;
|
||||
final String? cursor;
|
||||
final int size;
|
||||
@@ -57,11 +59,13 @@ class FakeCommentRepository implements CommentRepository {
|
||||
final List<PublishCall> publishCalls = [];
|
||||
final List<String> deleteCalls = [];
|
||||
final List<String> countCalls = [];
|
||||
final List<String> countEpisodeCalls = [];
|
||||
int _pageIndex = 0;
|
||||
|
||||
@override
|
||||
Future<CommentPage> page({
|
||||
required String dramaId,
|
||||
String? episodeId,
|
||||
String? rootId,
|
||||
String? cursor,
|
||||
int size = commentPageSize,
|
||||
@@ -69,6 +73,7 @@ class FakeCommentRepository implements CommentRepository {
|
||||
pageCalls.add(
|
||||
CommentPageCall(
|
||||
dramaId: dramaId,
|
||||
episodeId: episodeId,
|
||||
rootId: rootId,
|
||||
cursor: cursor,
|
||||
size: size,
|
||||
@@ -81,8 +86,11 @@ class FakeCommentRepository implements CommentRepository {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> count(String dramaId) async {
|
||||
Future<int> count(String dramaId, {String? episodeId}) async {
|
||||
countCalls.add(dramaId);
|
||||
if (episodeId != null && episodeId.isNotEmpty) {
|
||||
countEpisodeCalls.add(episodeId);
|
||||
}
|
||||
final error = countError;
|
||||
if (error != null) throw error;
|
||||
final result = countResult;
|
||||
|
||||
@@ -263,7 +263,7 @@ void main() {
|
||||
|
||||
await tester.tap(find.byKey(const Key('player-rail-like')));
|
||||
await tester.pumpAndSettle();
|
||||
expect(interactions.likeTargets, contains('DRAMA:city-lights-demo'));
|
||||
expect(interactions.likeTargets, contains('EPISODE:city-lights-demo-1'));
|
||||
|
||||
await tester.tap(find.byKey(const Key('player-rail-save')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
Reference in new issue
Block a user