merge: 合并 K95.1 首次切换频道无感
This commit is contained in:
commit
838ca2ce2a
5 files changed
+242
-27
No files matched your search
@@ -52,6 +52,14 @@ class HomeFeedController extends ChangeNotifier {
|
|||||||
_notifyIfCurrent(HomeFeedChannel.recommend);
|
_notifyIfCurrent(HomeFeedChannel.recommend);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 后台预取漫剧与真人剧首屏,不切换频道,也不影响当前列表。
|
||||||
|
Future<void> prefetchChannels() async {
|
||||||
|
await Future.wait([
|
||||||
|
_prefetchChannel(HomeFeedChannel.anime),
|
||||||
|
_prefetchChannel(HomeFeedChannel.live),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
/// 同步切到目标频道;有缓存先渲染,无缓存时复用或等待在飞请求。
|
/// 同步切到目标频道;有缓存先渲染,无缓存时复用或等待在飞请求。
|
||||||
Future<void> selectChannel(HomeFeedChannel next) async {
|
Future<void> selectChannel(HomeFeedChannel next) async {
|
||||||
if (next == _channel) return;
|
if (next == _channel) return;
|
||||||
@@ -59,7 +67,9 @@ class HomeFeedController extends ChangeNotifier {
|
|||||||
_notify();
|
_notify();
|
||||||
final state = _state(next);
|
final state = _state(next);
|
||||||
if (state.dramas.isNotEmpty) {
|
if (state.dramas.isNotEmpty) {
|
||||||
if (!state.loading) {
|
if (state.skipNextRefresh) {
|
||||||
|
state.skipNextRefresh = false;
|
||||||
|
} else if (!state.loading) {
|
||||||
unawaited(_refresh(next, keepData: true));
|
unawaited(_refresh(next, keepData: true));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -67,6 +77,7 @@ class HomeFeedController extends ChangeNotifier {
|
|||||||
if (state.loading) {
|
if (state.loading) {
|
||||||
final inFlight = state.inFlight;
|
final inFlight = state.inFlight;
|
||||||
if (inFlight != null) await inFlight;
|
if (inFlight != null) await inFlight;
|
||||||
|
state.skipNextRefresh = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await _refresh(next, keepData: false);
|
await _refresh(next, keepData: false);
|
||||||
@@ -126,6 +137,13 @@ class HomeFeedController extends ChangeNotifier {
|
|||||||
return operation;
|
return operation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _prefetchChannel(HomeFeedChannel channel) async {
|
||||||
|
final state = _state(channel);
|
||||||
|
if (state.loaded || state.loading) return;
|
||||||
|
final success = await _refresh(channel, keepData: false);
|
||||||
|
if (success && !_disposed) state.skipNextRefresh = true;
|
||||||
|
}
|
||||||
|
|
||||||
Future<bool> _performRefresh(
|
Future<bool> _performRefresh(
|
||||||
HomeFeedChannel channel, {
|
HomeFeedChannel channel, {
|
||||||
required bool keepData,
|
required bool keepData,
|
||||||
@@ -200,4 +218,5 @@ class _ChannelFeedState {
|
|||||||
String? lastError;
|
String? lastError;
|
||||||
int requestSeq = 0;
|
int requestSeq = 0;
|
||||||
Future<bool>? inFlight;
|
Future<bool>? inFlight;
|
||||||
|
bool skipNextRefresh = false;
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,7 @@ import 'package:flutter/services.dart';
|
|||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import '../../core/config/app_config.dart';
|
import '../../core/config/app_config.dart';
|
||||||
|
import '../../core/data/models/drama.dart';
|
||||||
import '../../core/data/services/home_feed_controller.dart';
|
import '../../core/data/services/home_feed_controller.dart';
|
||||||
import '../../core/data/services/catalog_controller.dart';
|
import '../../core/data/services/catalog_controller.dart';
|
||||||
import '../../core/data/services/catalog_retry.dart';
|
import '../../core/data/services/catalog_retry.dart';
|
||||||
@@ -141,7 +142,7 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
|
|||||||
_recommendSeeded = true;
|
_recommendSeeded = true;
|
||||||
homeFeed.seedRecommend(catalog.dramas);
|
homeFeed.seedRecommend(catalog.dramas);
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
if (mounted) unawaited(homeFeed.refreshCurrent());
|
if (mounted) unawaited(_refreshRecommendAndPrefetch(homeFeed));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return _HomeTabs(key: const Key('home-tabs'), feedController: homeFeed);
|
return _HomeTabs(key: const Key('home-tabs'), feedController: homeFeed);
|
||||||
@@ -151,6 +152,12 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
|
|||||||
return _homeFeed ??= HomeFeedController(catalog.repository);
|
return _homeFeed ??= HomeFeedController(catalog.repository);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _refreshRecommendAndPrefetch(HomeFeedController homeFeed) async {
|
||||||
|
if (await homeFeed.refreshCurrent()) {
|
||||||
|
await homeFeed.prefetchChannels();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Widget _startupSplash() {
|
Widget _startupSplash() {
|
||||||
final isAndroid = defaultTargetPlatform == TargetPlatform.android;
|
final isAndroid = defaultTargetPlatform == TargetPlatform.android;
|
||||||
final size = isAndroid ? _androidSplashSize : _defaultSplashSize;
|
final size = isAndroid ? _androidSplashSize : _defaultSplashSize;
|
||||||
@@ -222,11 +229,15 @@ class _HomeTabs extends StatefulWidget {
|
|||||||
|
|
||||||
class _HomeTabsState extends State<_HomeTabs> {
|
class _HomeTabsState extends State<_HomeTabs> {
|
||||||
late final HomeLogic logic;
|
late final HomeLogic logic;
|
||||||
|
List<Drama>? _visibleDramas;
|
||||||
|
HomeFeedChannel? _visibleChannel;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
logic = HomeBinding().create(widget.feedController.dramas);
|
logic = HomeBinding().create(widget.feedController.dramas);
|
||||||
|
_visibleDramas = widget.feedController.dramas;
|
||||||
|
_visibleChannel = widget.feedController.channel;
|
||||||
widget.feedController.addListener(_onFeedChanged);
|
widget.feedController.addListener(_onFeedChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,26 +302,70 @@ class _HomeTabsState extends State<_HomeTabs> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _home(BuildContext context) {
|
Widget _home(BuildContext context) {
|
||||||
final dramas = widget.feedController.dramas;
|
final currentDramas = widget.feedController.dramas;
|
||||||
if (dramas.isEmpty) return _feedChannelPlaceholder(context);
|
final currentChannel = widget.feedController.channel;
|
||||||
return PlayerPage(
|
if (currentDramas.isNotEmpty) {
|
||||||
homeMode: true,
|
_visibleDramas = currentDramas;
|
||||||
embedded: true,
|
_visibleChannel = currentChannel;
|
||||||
active: logic.tab == 0,
|
}
|
||||||
drama: dramas.first,
|
if (currentDramas.isEmpty && !widget.feedController.loadingFirstPage) {
|
||||||
initialIndex: 0,
|
return _feedChannelPlaceholder(context);
|
||||||
feedDramas: dramas,
|
}
|
||||||
feedChannel: widget.feedController.channel,
|
final visibleDramas = _visibleDramas;
|
||||||
onOpenFullPlayer: (drama, episode) =>
|
final visibleChannel = _visibleChannel;
|
||||||
logic.playDrama(context, drama, episodeId: episode.id),
|
final switching =
|
||||||
onRefreshFeed: widget.feedController.refreshCurrent,
|
currentDramas.isEmpty && widget.feedController.loadingFirstPage;
|
||||||
onLoadMore: widget.feedController.loadMore,
|
if (visibleDramas == null ||
|
||||||
onChannelSelected: _selectFeedChannel,
|
visibleDramas.isEmpty ||
|
||||||
onDramaChanged: logic.onDramaChanged,
|
visibleChannel == null) {
|
||||||
onImmersiveChanged: logic.onImmersiveChanged,
|
return _feedChannelPlaceholder(context);
|
||||||
onSearch: () => logic.select(1, search: true),
|
}
|
||||||
onDiscover: () => logic.select(1),
|
final playerDramas = currentDramas.isEmpty ? visibleDramas : currentDramas;
|
||||||
onMy: () => logic.select(3),
|
final playerChannel = currentDramas.isEmpty
|
||||||
|
? visibleChannel
|
||||||
|
: currentChannel;
|
||||||
|
return Stack(
|
||||||
|
fit: StackFit.expand,
|
||||||
|
children: [
|
||||||
|
PlayerPage(
|
||||||
|
homeMode: true,
|
||||||
|
embedded: true,
|
||||||
|
active: logic.tab == 0,
|
||||||
|
drama: playerDramas.first,
|
||||||
|
initialIndex: 0,
|
||||||
|
feedDramas: playerDramas,
|
||||||
|
feedChannel: playerChannel,
|
||||||
|
headerChannel: currentChannel,
|
||||||
|
onOpenFullPlayer: (drama, episode) =>
|
||||||
|
logic.playDrama(context, drama, episodeId: episode.id),
|
||||||
|
onRefreshFeed: widget.feedController.refreshCurrent,
|
||||||
|
onLoadMore: widget.feedController.loadMore,
|
||||||
|
onChannelSelected: _selectFeedChannel,
|
||||||
|
onDramaChanged: logic.onDramaChanged,
|
||||||
|
onImmersiveChanged: logic.onImmersiveChanged,
|
||||||
|
onSearch: () => logic.select(1, search: true),
|
||||||
|
onDiscover: () => logic.select(1),
|
||||||
|
onMy: () => logic.select(3),
|
||||||
|
),
|
||||||
|
if (switching)
|
||||||
|
Positioned.fill(
|
||||||
|
child: IgnorePointer(
|
||||||
|
key: const Key('home-feed-channel-loading'),
|
||||||
|
child: ColoredBox(
|
||||||
|
color: Colors.black26,
|
||||||
|
child: Center(
|
||||||
|
child: SizedBox.square(
|
||||||
|
dimension: 24,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
strokeWidth: 2.4,
|
||||||
|
color: Colors.white70,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ class PlayerPage extends StatefulWidget {
|
|||||||
this.onMenu,
|
this.onMenu,
|
||||||
this.feedDramas,
|
this.feedDramas,
|
||||||
this.feedChannel,
|
this.feedChannel,
|
||||||
|
this.headerChannel,
|
||||||
this.onDramaChanged,
|
this.onDramaChanged,
|
||||||
this.onImmersiveChanged,
|
this.onImmersiveChanged,
|
||||||
this.onOpenFullPlayer,
|
this.onOpenFullPlayer,
|
||||||
@@ -82,6 +83,7 @@ class PlayerPage extends StatefulWidget {
|
|||||||
final VoidCallback? onMenu;
|
final VoidCallback? onMenu;
|
||||||
final List<Drama>? feedDramas;
|
final List<Drama>? feedDramas;
|
||||||
final HomeFeedChannel? feedChannel;
|
final HomeFeedChannel? feedChannel;
|
||||||
|
final HomeFeedChannel? headerChannel;
|
||||||
final ValueChanged<Drama>? onDramaChanged;
|
final ValueChanged<Drama>? onDramaChanged;
|
||||||
final ValueChanged<bool>? onImmersiveChanged;
|
final ValueChanged<bool>? onImmersiveChanged;
|
||||||
final Future<void> Function(Drama drama, Episode episode)? onOpenFullPlayer;
|
final Future<void> Function(Drama drama, Episode episode)? onOpenFullPlayer;
|
||||||
@@ -295,7 +297,10 @@ class _PlayerPageState extends State<PlayerPage>
|
|||||||
bottom: false,
|
bottom: false,
|
||||||
child: HomeFeedHeader(
|
child: HomeFeedHeader(
|
||||||
l10n: l10n,
|
l10n: l10n,
|
||||||
channel: widget.feedChannel ?? HomeFeedChannel.recommend,
|
channel:
|
||||||
|
widget.headerChannel ??
|
||||||
|
widget.feedChannel ??
|
||||||
|
HomeFeedChannel.recommend,
|
||||||
onChannelSelected: widget.onChannelSelected ?? (_) {},
|
onChannelSelected: widget.onChannelSelected ?? (_) {},
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
final ok = await widget.onRefreshFeed?.call() ?? true;
|
final ok = await widget.onRefreshFeed?.call() ?? true;
|
||||||
|
|||||||
@@ -27,6 +27,67 @@ void main() {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
'should_use_prefetched_channel_without_another_request_on_first_select',
|
||||||
|
() async {
|
||||||
|
final repository = _ScriptedFeedRepository()
|
||||||
|
..enqueue(() async => FeedPage(dramas: [_drama('anime')]))
|
||||||
|
..enqueue(() async => FeedPage(dramas: [_drama('live')]));
|
||||||
|
final controller = HomeFeedController(repository);
|
||||||
|
controller.seedRecommend([_drama('seed')]);
|
||||||
|
|
||||||
|
await controller.prefetchChannels();
|
||||||
|
await controller.selectChannel(HomeFeedChannel.anime);
|
||||||
|
|
||||||
|
expect(controller.dramas.single.id, 'anime');
|
||||||
|
expect(repository.calls.map((call) => call.types), [
|
||||||
|
['ANIME'],
|
||||||
|
['LIVE'],
|
||||||
|
]);
|
||||||
|
controller.dispose();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
test('should_keep_recommend_when_prefetch_fails', () async {
|
||||||
|
final repository = _ScriptedFeedRepository()
|
||||||
|
..enqueue(() async => throw StateError('offline'))
|
||||||
|
..enqueue(() async => FeedPage(dramas: [_drama('live')]));
|
||||||
|
final controller = HomeFeedController(repository);
|
||||||
|
controller.seedRecommend([_drama('seed')]);
|
||||||
|
|
||||||
|
await controller.prefetchChannels();
|
||||||
|
|
||||||
|
expect(controller.channel, HomeFeedChannel.recommend);
|
||||||
|
expect(controller.dramas.single.id, 'seed');
|
||||||
|
expect(controller.lastError, isNull);
|
||||||
|
controller.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'should_wait_prefetch_without_duplicate_request_when_channel_is_selected',
|
||||||
|
() async {
|
||||||
|
final anime = Completer<FeedPage>();
|
||||||
|
final repository = _ScriptedFeedRepository()
|
||||||
|
..enqueue(() => anime.future)
|
||||||
|
..enqueue(() async => FeedPage(dramas: [_drama('live')]));
|
||||||
|
final controller = HomeFeedController(repository);
|
||||||
|
controller.seedRecommend([_drama('seed')]);
|
||||||
|
|
||||||
|
final prefetch = controller.prefetchChannels();
|
||||||
|
final select = controller.selectChannel(HomeFeedChannel.anime);
|
||||||
|
anime.complete(FeedPage(dramas: [_drama('anime')]));
|
||||||
|
await prefetch;
|
||||||
|
await select;
|
||||||
|
|
||||||
|
expect(
|
||||||
|
repository.calls.where((call) => call.types.contains('ANIME')),
|
||||||
|
hasLength(1),
|
||||||
|
);
|
||||||
|
expect(controller.dramas.single.id, 'anime');
|
||||||
|
controller.dispose();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
test('should_request_anime_live_and_recommend_with_expected_types', () async {
|
test('should_request_anime_live_and_recommend_with_expected_types', () async {
|
||||||
final repository = _ScriptedFeedRepository()
|
final repository = _ScriptedFeedRepository()
|
||||||
..enqueue(() async => FeedPage(dramas: [_drama('anime')]))
|
..enqueue(() async => FeedPage(dramas: [_drama('anime')]))
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
// Author: fruit.com.cn
|
// Author: fruit.com.cn
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:fruitshorts/app.dart';
|
import 'package:fruitshorts/app.dart';
|
||||||
|
import 'package:fruitshorts/core/data/models/feed_page.dart';
|
||||||
import 'package:fruitshorts/core/data/repositories/favorite_repository.dart';
|
import 'package:fruitshorts/core/data/repositories/favorite_repository.dart';
|
||||||
import 'package:fruitshorts/core/data/repositories/playback_repository.dart';
|
import 'package:fruitshorts/core/data/repositories/playback_repository.dart';
|
||||||
import 'package:fruitshorts/core/data/repositories/watch_history_repository.dart';
|
import 'package:fruitshorts/core/data/repositories/watch_history_repository.dart';
|
||||||
|
import 'package:fruitshorts/pages/home/home_feed_channel.dart';
|
||||||
import 'package:fruitshorts/pages/player/player_view.dart';
|
import 'package:fruitshorts/pages/player/player_view.dart';
|
||||||
import 'package:shared_preferences_platform_interface/in_memory_shared_preferences_async.dart';
|
import 'package:shared_preferences_platform_interface/in_memory_shared_preferences_async.dart';
|
||||||
import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart';
|
import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart';
|
||||||
@@ -130,6 +134,44 @@ void main() {
|
|||||||
expect(find.byKey(const Key('home-feed-title')), findsOneWidget);
|
expect(find.byKey(const Key('home-feed-title')), findsOneWidget);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('should_keep_player_when_first_anime_switch_is_loading', (
|
||||||
|
tester,
|
||||||
|
) async {
|
||||||
|
final catalog = _PendingAnimeCatalog();
|
||||||
|
await _pumpApp(tester, catalog: catalog);
|
||||||
|
for (var attempt = 0; attempt < 5 && catalog.animeCalls == 0; attempt++) {
|
||||||
|
await tester.pump();
|
||||||
|
}
|
||||||
|
expect(catalog.animeCalls, 1);
|
||||||
|
|
||||||
|
await tester.tap(find.byKey(const Key('home-feed-tab-anime')));
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(find.byType(PlayerPage), findsOneWidget);
|
||||||
|
expect(find.byKey(const Key('home-feed-channel-loading')), findsOneWidget);
|
||||||
|
|
||||||
|
catalog.anime.complete(FeedPage(dramas: [demoDramas[1]]));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(find.byKey(const Key('home-feed-channel-loading')), findsNothing);
|
||||||
|
expect(
|
||||||
|
tester.widget<PlayerPage>(find.byType(PlayerPage)).feedChannel,
|
||||||
|
HomeFeedChannel.anime,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('should_show_empty_state_when_anime_has_no_dramas', (
|
||||||
|
tester,
|
||||||
|
) async {
|
||||||
|
await _pumpApp(tester, catalog: _EmptyAnimeCatalog());
|
||||||
|
|
||||||
|
await tester.tap(find.byKey(const Key('home-feed-tab-anime')));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(find.byType(PlayerPage), findsNothing);
|
||||||
|
expect(find.text('No stories available yet.'), findsOneWidget);
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('should_refresh_feed_without_blank_loading', (tester) async {
|
testWidgets('should_refresh_feed_without_blank_loading', (tester) async {
|
||||||
await _pumpApp(tester);
|
await _pumpApp(tester);
|
||||||
|
|
||||||
@@ -446,19 +488,20 @@ bool _hasLightStatusBar(WidgetTester tester) => tester
|
|||||||
Future<void> _pumpApp(
|
Future<void> _pumpApp(
|
||||||
WidgetTester tester, {
|
WidgetTester tester, {
|
||||||
Size size = const Size(390, 844),
|
Size size = const Size(390, 844),
|
||||||
|
FakeCatalogRepository? catalog,
|
||||||
}) async {
|
}) async {
|
||||||
tester.view.physicalSize = size;
|
tester.view.physicalSize = size;
|
||||||
tester.view.devicePixelRatio = 1;
|
tester.view.devicePixelRatio = 1;
|
||||||
addTearDown(tester.view.resetPhysicalSize);
|
addTearDown(tester.view.resetPhysicalSize);
|
||||||
addTearDown(tester.view.resetDevicePixelRatio);
|
addTearDown(tester.view.resetDevicePixelRatio);
|
||||||
final catalog = FakeCatalogRepository();
|
final repository = catalog ?? FakeCatalogRepository();
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
LumaDramaApp(
|
LumaDramaApp(
|
||||||
playerFactory: createWidgetPlayer,
|
playerFactory: createWidgetPlayer,
|
||||||
catalogRepository: catalog,
|
catalogRepository: repository,
|
||||||
categoryRepository: FakeCategoryRepository(catalog: catalog),
|
categoryRepository: FakeCategoryRepository(catalog: repository),
|
||||||
watchHistoryRepository: InMemoryWatchHistoryRepository(),
|
watchHistoryRepository: InMemoryWatchHistoryRepository(),
|
||||||
favoriteRepository: InMemoryFavoriteRepository(cards: catalog.dramas),
|
favoriteRepository: InMemoryFavoriteRepository(cards: repository.dramas),
|
||||||
playbackRepository: FakePlaybackRepository(),
|
playbackRepository: FakePlaybackRepository(),
|
||||||
commentRepository: FakeCommentRepository(),
|
commentRepository: FakeCommentRepository(),
|
||||||
interactionRepository: FakeInteractionRepository(),
|
interactionRepository: FakeInteractionRepository(),
|
||||||
@@ -467,3 +510,35 @@ Future<void> _pumpApp(
|
|||||||
);
|
);
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _PendingAnimeCatalog extends FakeCatalogRepository {
|
||||||
|
final anime = Completer<FeedPage>();
|
||||||
|
int animeCalls = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<FeedPage> fetchFeed({
|
||||||
|
List<String> types = const [],
|
||||||
|
String? cursor,
|
||||||
|
int size = 10,
|
||||||
|
}) {
|
||||||
|
if (types.contains('ANIME')) {
|
||||||
|
animeCalls++;
|
||||||
|
return anime.future;
|
||||||
|
}
|
||||||
|
return super.fetchFeed(types: types, cursor: cursor, size: size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _EmptyAnimeCatalog extends FakeCatalogRepository {
|
||||||
|
@override
|
||||||
|
Future<FeedPage> fetchFeed({
|
||||||
|
List<String> types = const [],
|
||||||
|
String? cursor,
|
||||||
|
int size = 10,
|
||||||
|
}) {
|
||||||
|
if (types.contains('ANIME')) {
|
||||||
|
return Future.value(const FeedPage(dramas: []));
|
||||||
|
}
|
||||||
|
return super.fetchFeed(types: types, cursor: cursor, size: size);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user