feat(home): 首页按内容类型分频道 Feed 并支持分页

This commit is contained in:
Zhou Weidong committed 2026-10-21 17:33:52 +08:00
1 parent 4d0be47ee3
commit ae61d9ec4c
22 files changed
+1001 -71

No files matched your search

@@ -4,6 +4,7 @@ import 'dart:async';
import 'package:flutter_test/flutter_test.dart';
import 'package:fruitshorts/core/data/models/drama.dart';
import 'package:fruitshorts/core/data/models/feed_page.dart';
import 'package:fruitshorts/core/data/models/home_feed.dart';
import 'package:fruitshorts/core/data/repositories/catalog_repository.dart';
import 'package:fruitshorts/core/data/services/catalog_controller.dart';
@@ -136,6 +137,13 @@ class ScriptedCatalogRepository implements CatalogRepository {
@override
Future<HomeFeed> fetchDiscoverFeed() async => feed;
@override
Future<FeedPage> fetchFeed({
List<String> types = const [],
String? cursor,
int size = 10,
}) async => const FeedPage();
@override
Future<Drama> fetchDrama(String dramaId) async {
detailCalls++;
+78
View File
@@ -0,0 +1,78 @@
// Author: fruit.com.cn
import 'dart:convert';
import 'package:flutter_test/flutter_test.dart';
import 'package:fruitshorts/core/data/remote/api_client.dart';
import 'package:fruitshorts/core/data/remote/catalog_api.dart';
import '../support/fake_http_adapter.dart';
void main() {
String envelope(Object? data) =>
jsonEncode({'code': 0, 'message': 'ok', 'data': data});
test('should_send_types_and_cursor_when_feed_query_has_filters', () async {
final adapter = FakeHttpAdapter(
(_) async => FakeHttpResponse(
envelope({
'list': [
{
'id': '11',
'title': 'Anime',
'cover': '/files/anime.png',
'preview': {
'id': '12',
'number': 1,
'title': 'Episode 1',
'isFree': true,
},
},
],
'nextCursor': 10,
'hasMore': true,
}),
),
);
final api = CatalogApi(
ApiClient(baseUrl: 'http://127.0.0.1:8081', adapter: adapter),
);
final page = await api.feed(
types: const ['ANIME', 'LIVE'],
cursor: '9',
size: 20,
);
expect(adapter.requests.single.uri.path, '/app/feed');
expect(adapter.requests.single.queryParameters, {
'types': 'ANIME,LIVE',
'cursor': '9',
'size': '20',
});
expect(page.dramas.single.id, '11');
expect(page.nextCursor, '10');
expect(page.hasMore, isTrue);
});
test(
'should_omit_empty_types_and_treat_null_cursor_string_as_empty',
() async {
final adapter = FakeHttpAdapter(
(_) async => FakeHttpResponse(
envelope({'list': [], 'nextCursor': 'null', 'hasMore': false}),
),
);
final api = CatalogApi(
ApiClient(baseUrl: 'http://127.0.0.1:8081', adapter: adapter),
);
final page = await api.feed();
expect(adapter.requests.single.queryParameters, {'size': '10'});
expect(page.dramas, isEmpty);
expect(page.nextCursor, isNull);
expect(page.hasMore, isFalse);
},
);
}