fix(following): 支持未登录追剧与历史
This commit is contained in:
1 parent
0d785bf1f8
commit
06a2f6d7c0
4 files changed
+292
-53
No files matched your search
@@ -314,6 +314,23 @@ class WatchRecordsController extends ChangeNotifier {
|
|||||||
if (!_disposed) notifyListeners();
|
if (!_disposed) notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 切换账号/设备身份时清除内存数据,不删除服务端记录。
|
||||||
|
void reset() {
|
||||||
|
_requestSeq += 1;
|
||||||
|
records = const [];
|
||||||
|
total = 0;
|
||||||
|
loading = false;
|
||||||
|
loadingMore = false;
|
||||||
|
hasMore = false;
|
||||||
|
clearing = false;
|
||||||
|
error = null;
|
||||||
|
loadMoreError = null;
|
||||||
|
actionError = null;
|
||||||
|
lastErrorCode = null;
|
||||||
|
_pageIndex = 0;
|
||||||
|
_notify();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import '../../core/data/services/watch_records_controller.dart';
|
|||||||
import '../../core/formatting/error_text.dart';
|
import '../../core/formatting/error_text.dart';
|
||||||
import '../../core/theme/app_colors.dart';
|
import '../../core/theme/app_colors.dart';
|
||||||
import '../../core/theme/app_dimens.dart';
|
import '../../core/theme/app_dimens.dart';
|
||||||
import '../../core/widgets/auth_gate.dart';
|
|
||||||
import '../../core/widgets/empty_state.dart';
|
import '../../core/widgets/empty_state.dart';
|
||||||
import '../../l10n/app_localizations.dart';
|
import '../../l10n/app_localizations.dart';
|
||||||
import '../player/open_player.dart';
|
import '../player/open_player.dart';
|
||||||
@@ -67,7 +66,7 @@ class _FollowingPageState extends State<FollowingPage> {
|
|||||||
history: context.read<WatchHistoryRepository>(),
|
history: context.read<WatchHistoryRepository>(),
|
||||||
catalog: context.read<CatalogRepository>(),
|
catalog: context.read<CatalogRepository>(),
|
||||||
);
|
);
|
||||||
if (_wasSignedIn) unawaited(_history.load());
|
unawaited(_history.load());
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
if (mounted) unawaited(widget.favoriteController.load());
|
if (mounted) unawaited(widget.favoriteController.load());
|
||||||
});
|
});
|
||||||
@@ -75,8 +74,16 @@ class _FollowingPageState extends State<FollowingPage> {
|
|||||||
|
|
||||||
void _onAuthChanged() {
|
void _onAuthChanged() {
|
||||||
final signedIn = _auth.isSignedIn;
|
final signedIn = _auth.isSignedIn;
|
||||||
if (signedIn && !_wasSignedIn) unawaited(_history.load());
|
if (signedIn == _wasSignedIn) return;
|
||||||
_wasSignedIn = signedIn;
|
_wasSignedIn = signedIn;
|
||||||
|
_leaveEditing();
|
||||||
|
_history.reset();
|
||||||
|
widget.favoriteController.clear();
|
||||||
|
unawaited(_reloadForIdentity());
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _reloadForIdentity() async {
|
||||||
|
await Future.wait([_history.load(), widget.favoriteController.load()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -104,7 +111,6 @@ class _FollowingPageState extends State<FollowingPage> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final l10n = AppLocalizations.of(context)!;
|
final l10n = AppLocalizations.of(context)!;
|
||||||
final signedIn = context.watch<AuthController>().isSignedIn;
|
|
||||||
return ColoredBox(
|
return ColoredBox(
|
||||||
key: const Key('following-page'),
|
key: const Key('following-page'),
|
||||||
color: AppColors.appBackground,
|
color: AppColors.appBackground,
|
||||||
@@ -114,47 +120,44 @@ class _FollowingPageState extends State<FollowingPage> {
|
|||||||
children: [
|
children: [
|
||||||
_header(l10n),
|
_header(l10n),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: !signedIn
|
child: PageView(
|
||||||
? const AuthGatePanel()
|
key: const Key('following-tab-pages'),
|
||||||
: PageView(
|
controller: _pageController,
|
||||||
key: const Key('following-tab-pages'),
|
onPageChanged: _onPageChanged,
|
||||||
controller: _pageController,
|
children: [
|
||||||
onPageChanged: _onPageChanged,
|
FollowingKeepAliveTab(
|
||||||
children: [
|
child: FollowingFavoritesGrid(
|
||||||
FollowingKeepAliveTab(
|
controller: widget.favoriteController,
|
||||||
child: FollowingFavoritesGrid(
|
selection: FollowingFavoriteSelection(
|
||||||
controller: widget.favoriteController,
|
editing: _editing,
|
||||||
selection: FollowingFavoriteSelection(
|
selected: _selected,
|
||||||
editing: _editing,
|
onToggle: _toggleDrama,
|
||||||
selected: _selected,
|
),
|
||||||
onToggle: _toggleDrama,
|
scrollController: _favoriteScroll,
|
||||||
),
|
onGoHome: widget.onGoHome,
|
||||||
scrollController: _favoriteScroll,
|
|
||||||
onGoHome: widget.onGoHome,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
FollowingKeepAliveTab(
|
|
||||||
child: _HistoryList(
|
|
||||||
controller: _history,
|
|
||||||
editing: _editing,
|
|
||||||
scrollController: _historyScroll,
|
|
||||||
onListEmptied: _exitEditingIfEmpty,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (signedIn)
|
FollowingKeepAliveTab(
|
||||||
FollowingEditSlot(
|
child: _HistoryList(
|
||||||
tab: _tab,
|
controller: _history,
|
||||||
editing: _editing,
|
editing: _editing,
|
||||||
favorites: widget.favoriteController,
|
scrollController: _historyScroll,
|
||||||
selected: _selected,
|
onListEmptied: _exitEditingIfEmpty,
|
||||||
actions: FollowingEditActions(
|
),
|
||||||
onToggleAll: _toggleAll,
|
),
|
||||||
onRemove: () => unawaited(_cancelSelected()),
|
],
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
FollowingEditSlot(
|
||||||
|
tab: _tab,
|
||||||
|
editing: _editing,
|
||||||
|
favorites: widget.favoriteController,
|
||||||
|
selected: _selected,
|
||||||
|
actions: FollowingEditActions(
|
||||||
|
onToggleAll: _toggleAll,
|
||||||
|
onRemove: () => unawaited(_cancelSelected()),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -297,7 +300,7 @@ class _FollowingPageState extends State<FollowingPage> {
|
|||||||
final tab = FollowingTab.values[index];
|
final tab = FollowingTab.values[index];
|
||||||
_leaveEditing();
|
_leaveEditing();
|
||||||
_tab.value = tab;
|
_tab.value = tab;
|
||||||
if (tab == FollowingTab.history && _auth.isSignedIn) {
|
if (tab == FollowingTab.history) {
|
||||||
unawaited(_history.load());
|
unawaited(_history.load());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import '../../core/platform/haptics.dart';
|
|||||||
import '../../core/theme/app_colors.dart';
|
import '../../core/theme/app_colors.dart';
|
||||||
import '../../core/theme/app_theme.dart';
|
import '../../core/theme/app_theme.dart';
|
||||||
import '../../core/widgets/app_pull_to_refresh.dart';
|
import '../../core/widgets/app_pull_to_refresh.dart';
|
||||||
import '../../core/widgets/auth_gate.dart';
|
|
||||||
import '../../core/widgets/empty_state.dart';
|
import '../../core/widgets/empty_state.dart';
|
||||||
import '../../core/widgets/paged_list_footer.dart';
|
import '../../core/widgets/paged_list_footer.dart';
|
||||||
import '../../core/widgets/subpage_header.dart';
|
import '../../core/widgets/subpage_header.dart';
|
||||||
@@ -86,6 +85,7 @@ class _WatchRecordsBodyState extends State<_WatchRecordsBody> {
|
|||||||
final signedIn = _auth.isSignedIn;
|
final signedIn = _auth.isSignedIn;
|
||||||
if (_signedIn == signedIn || !mounted) return;
|
if (_signedIn == signedIn || !mounted) return;
|
||||||
_signedIn = signedIn;
|
_signedIn = signedIn;
|
||||||
|
controller.reset();
|
||||||
unawaited(controller.load());
|
unawaited(controller.load());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,9 +106,6 @@ class _WatchRecordsBodyState extends State<_WatchRecordsBody> {
|
|||||||
|
|
||||||
Widget _content(BuildContext context) {
|
Widget _content(BuildContext context) {
|
||||||
final l10n = AppLocalizations.of(context)!;
|
final l10n = AppLocalizations.of(context)!;
|
||||||
if (controller.lastErrorCode == 200007) {
|
|
||||||
return const AuthGatePanel();
|
|
||||||
}
|
|
||||||
if (controller.loading && controller.records.isEmpty) {
|
if (controller.loading && controller.records.isEmpty) {
|
||||||
return const Center(
|
return const Center(
|
||||||
child: CircularProgressIndicator(key: Key('watch-records-loading')),
|
child: CircularProgressIndicator(key: Key('watch-records-loading')),
|
||||||
|
|||||||
@@ -4,12 +4,16 @@ import 'package:flutter/material.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/drama.dart';
|
import 'package:fruitshorts/core/data/models/drama.dart';
|
||||||
|
import 'package:fruitshorts/core/data/models/page_result.dart';
|
||||||
import 'package:fruitshorts/core/data/models/watch_progress.dart';
|
import 'package:fruitshorts/core/data/models/watch_progress.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/core/data/services/auth_controller.dart';
|
||||||
import 'package:fruitshorts/core/theme/app_colors.dart';
|
import 'package:fruitshorts/core/theme/app_colors.dart';
|
||||||
import 'package:fruitshorts/core/theme/app_dimens.dart';
|
import 'package:fruitshorts/core/theme/app_dimens.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.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';
|
||||||
|
|
||||||
@@ -165,7 +169,8 @@ void main() {
|
|||||||
testWidgets('should_unfollow_every_selected_drama_when_edit_confirms', (
|
testWidgets('should_unfollow_every_selected_drama_when_edit_confirms', (
|
||||||
tester,
|
tester,
|
||||||
) async {
|
) async {
|
||||||
_signIn();
|
SharedPreferencesAsyncPlatform.instance =
|
||||||
|
InMemorySharedPreferencesAsync.empty();
|
||||||
final catalog = FakeCatalogRepository();
|
final catalog = FakeCatalogRepository();
|
||||||
final dramas = catalog.dramas.take(2).toList();
|
final dramas = catalog.dramas.take(2).toList();
|
||||||
final favorites = InMemoryFavoriteRepository(
|
final favorites = InMemoryFavoriteRepository(
|
||||||
@@ -203,16 +208,119 @@ void main() {
|
|||||||
expect(favorites.removeManyCalls, 1);
|
expect(favorites.removeManyCalls, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('should_show_auth_gate_when_signed_out', (tester) async {
|
testWidgets('should_show_device_favorites_and_history_when_signed_out', (
|
||||||
|
tester,
|
||||||
|
) async {
|
||||||
SharedPreferencesAsyncPlatform.instance =
|
SharedPreferencesAsyncPlatform.instance =
|
||||||
InMemorySharedPreferencesAsync.empty();
|
InMemorySharedPreferencesAsync.empty();
|
||||||
await tester.pumpWidget(_app(catalog: FakeCatalogRepository()));
|
final catalog = FakeCatalogRepository();
|
||||||
|
await tester.pumpWidget(
|
||||||
|
_app(
|
||||||
|
catalog: catalog,
|
||||||
|
favorites: InMemoryFavoriteRepository(
|
||||||
|
cards: catalog.dramas.take(1).toList(),
|
||||||
|
saved: {catalog.dramas.first.id},
|
||||||
|
),
|
||||||
|
history: InMemoryWatchHistoryRepository(
|
||||||
|
seed: [
|
||||||
|
WatchProgress(
|
||||||
|
dramaId: 'the-signal-demo',
|
||||||
|
episodeId: 'the-signal-demo-3',
|
||||||
|
dramaTitle: 'Device history',
|
||||||
|
episodeNumber: 3,
|
||||||
|
position: const Duration(seconds: 25),
|
||||||
|
duration: const Duration(seconds: 100),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
await tester.tap(find.byKey(const Key('tab-follow')));
|
await tester.tap(find.byKey(const Key('tab-follow')));
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
expect(find.byKey(const Key('auth-gate-panel')), findsOneWidget);
|
expect(find.byKey(const Key('auth-gate-panel')), findsNothing);
|
||||||
expect(find.byKey(const Key('following-favorites-grid')), findsNothing);
|
expect(find.byKey(const Key('following-favorites-grid')), findsOneWidget);
|
||||||
|
expect(
|
||||||
|
find.byKey(const Key('following-card-the-signal-demo')),
|
||||||
|
findsOneWidget,
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.tap(find.byKey(const Key('following-tab-history')));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
find.byKey(const Key('following-history-card-the-signal-demo')),
|
||||||
|
findsOneWidget,
|
||||||
|
);
|
||||||
|
expect(find.text('Device history'), findsOneWidget);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('should_reload_identity_data_after_login_and_logout', (
|
||||||
|
tester,
|
||||||
|
) async {
|
||||||
|
SharedPreferencesAsyncPlatform.instance =
|
||||||
|
InMemorySharedPreferencesAsync.empty();
|
||||||
|
final catalog = FakeCatalogRepository();
|
||||||
|
final favorites = _IdentityFavoriteRepository(
|
||||||
|
device: [catalog.dramas[0]],
|
||||||
|
account: [catalog.dramas[1]],
|
||||||
|
);
|
||||||
|
final history = _IdentityWatchHistoryRepository(
|
||||||
|
device: [_historyProgress('the-signal-demo', 'Device history')],
|
||||||
|
account: [_historyProgress('city-lights-demo', 'Account history')],
|
||||||
|
);
|
||||||
|
await tester.pumpWidget(
|
||||||
|
_app(catalog: catalog, favorites: favorites, history: history),
|
||||||
|
);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
await tester.tap(find.byKey(const Key('tab-follow')));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
find.byKey(const Key('following-card-the-signal-demo')),
|
||||||
|
findsOneWidget,
|
||||||
|
);
|
||||||
|
final initialFavoriteCalls = favorites.pageCalls;
|
||||||
|
final initialHistoryCalls = history.recentCalls;
|
||||||
|
final auth = Provider.of<AuthController>(
|
||||||
|
tester.element(find.byKey(const Key('following-page'))),
|
||||||
|
listen: false,
|
||||||
|
);
|
||||||
|
final preferences = SharedPreferencesAsync();
|
||||||
|
|
||||||
|
favorites.scope = 'account';
|
||||||
|
history.scope = 'account';
|
||||||
|
await _setSignedIn(preferences);
|
||||||
|
await auth.load();
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(auth.isSignedIn, isTrue);
|
||||||
|
expect(favorites.pageCalls, greaterThan(initialFavoriteCalls));
|
||||||
|
expect(history.recentCalls, greaterThan(initialHistoryCalls));
|
||||||
|
expect(
|
||||||
|
find.byKey(const Key('following-card-the-signal-demo')),
|
||||||
|
findsNothing,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
find.byKey(const Key('following-card-city-lights-demo')),
|
||||||
|
findsOneWidget,
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.tap(find.byKey(const Key('following-tab-history')));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(find.text('Account history'), findsOneWidget);
|
||||||
|
expect(find.text('Device history'), findsNothing);
|
||||||
|
|
||||||
|
favorites.scope = 'device';
|
||||||
|
history.scope = 'device';
|
||||||
|
await _clearSignedIn(preferences);
|
||||||
|
await auth.load();
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(auth.isSignedIn, isFalse);
|
||||||
|
expect(find.text('Device history'), findsOneWidget);
|
||||||
|
expect(find.text('Account history'), findsNothing);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('should_show_empty_favorites_and_history', (tester) async {
|
testWidgets('should_show_empty_favorites_and_history', (tester) async {
|
||||||
@@ -407,6 +515,21 @@ void _signIn() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _setSignedIn(SharedPreferencesAsync preferences) async {
|
||||||
|
await preferences.setString('auth.accessToken', 'access');
|
||||||
|
await preferences.setString('auth.refreshToken', 'refresh');
|
||||||
|
await preferences.setString(
|
||||||
|
'auth.user',
|
||||||
|
'{"uid":"9","nickname":"Viewer","avatarUrl":""}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _clearSignedIn(SharedPreferencesAsync preferences) async {
|
||||||
|
await preferences.remove('auth.accessToken');
|
||||||
|
await preferences.remove('auth.refreshToken');
|
||||||
|
await preferences.remove('auth.user');
|
||||||
|
}
|
||||||
|
|
||||||
Widget _app({
|
Widget _app({
|
||||||
required FakeCatalogRepository catalog,
|
required FakeCatalogRepository catalog,
|
||||||
FavoriteRepository? favorites,
|
FavoriteRepository? favorites,
|
||||||
@@ -433,3 +556,102 @@ class _CountingCatalogRepository extends FakeCatalogRepository {
|
|||||||
return super.fetchDrama(dramaId);
|
return super.fetchDrama(dramaId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WatchProgress _historyProgress(String dramaId, String title) {
|
||||||
|
return WatchProgress(
|
||||||
|
dramaId: dramaId,
|
||||||
|
episodeId: '$dramaId-3',
|
||||||
|
dramaTitle: title,
|
||||||
|
episodeNumber: 3,
|
||||||
|
position: const Duration(seconds: 25),
|
||||||
|
duration: const Duration(seconds: 100),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class _IdentityFavoriteRepository implements FavoriteRepository {
|
||||||
|
_IdentityFavoriteRepository({
|
||||||
|
required List<Drama> device,
|
||||||
|
required List<Drama> account,
|
||||||
|
}) : _cards = {'device': device, 'account': account};
|
||||||
|
|
||||||
|
final Map<String, List<Drama>> _cards;
|
||||||
|
String scope = 'device';
|
||||||
|
int pageCalls = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<PageResult<Drama>> page({int pageIndex = 1, int pageSize = 20}) async {
|
||||||
|
pageCalls += 1;
|
||||||
|
final cards = _cards[scope]!;
|
||||||
|
final offset = (pageIndex - 1) * pageSize;
|
||||||
|
return PageResult(
|
||||||
|
total: cards.length,
|
||||||
|
list: offset >= cards.length
|
||||||
|
? const []
|
||||||
|
: cards.skip(offset).take(pageSize).toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> remove(String dramaId) async {
|
||||||
|
_cards[scope]!.removeWhere((drama) => drama.id == dramaId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> removeMany(List<String> dramaIds) async {
|
||||||
|
final ids = dramaIds.toSet();
|
||||||
|
_cards[scope]!.removeWhere((drama) => ids.contains(drama.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> toggle(String dramaId) async => true;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _IdentityWatchHistoryRepository implements WatchHistoryRepository {
|
||||||
|
_IdentityWatchHistoryRepository({
|
||||||
|
required List<WatchProgress> device,
|
||||||
|
required List<WatchProgress> account,
|
||||||
|
}) : _records = {'device': device, 'account': account};
|
||||||
|
|
||||||
|
final Map<String, List<WatchProgress>> _records;
|
||||||
|
String scope = 'device';
|
||||||
|
int recentCalls = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> clear() async => _records[scope]!.clear();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<WatchProgress?> progressFor(String dramaId) async {
|
||||||
|
return _records[scope]!
|
||||||
|
.where((record) => record.dramaId == dramaId)
|
||||||
|
.firstOrNull;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<PageResult<WatchProgress>> recent({
|
||||||
|
int pageIndex = 1,
|
||||||
|
int pageSize = 20,
|
||||||
|
}) async {
|
||||||
|
recentCalls += 1;
|
||||||
|
final records = _records[scope]!;
|
||||||
|
final offset = (pageIndex - 1) * pageSize;
|
||||||
|
return PageResult(
|
||||||
|
total: records.length,
|
||||||
|
list: offset >= records.length
|
||||||
|
? const []
|
||||||
|
: records.skip(offset).take(pageSize).toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> remove(String dramaId) async {
|
||||||
|
_records[scope]!.removeWhere((record) => record.dramaId == dramaId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<WatchProgress?> save({
|
||||||
|
required String episodeId,
|
||||||
|
required Duration position,
|
||||||
|
Duration? duration,
|
||||||
|
bool finished = false,
|
||||||
|
}) async => null;
|
||||||
|
}
|
||||||
Reference in new issue
Block a user