spotube/lib/provider/metadata_plugin/utils/paginated.dart

84 lines
2.7 KiB
Dart

import 'dart:async';
import 'dart:math';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/models/metadata/metadata.dart';
// ignore: implementation_imports
import 'package:riverpod/src/async_notifier.dart';
import 'package:spotube/provider/metadata_plugin/utils/common.dart';
import 'package:spotube/services/logger/logger.dart';
mixin PaginatedAsyncNotifierMixin<K>
// ignore: invalid_use_of_internal_member
on AsyncNotifierBase<SpotubePaginationResponseObject<K>> {
Future<SpotubePaginationResponseObject<K>> fetch(int offset, int limit);
Future<void> fetchMore() async {
if (state.value == null || !state.value!.hasMore) return;
final oldState = state.value;
try {
state = AsyncLoadingNext(state.asData!.value);
final newState = await fetch(
state.value!.nextOffset!,
state.value!.limit,
);
final oldItems =
state.value!.items.isEmpty ? <K>[] : state.value!.items.cast<K>();
final items = newState.items.isEmpty ? <K>[] : newState.items.cast<K>();
state = AsyncData(newState.copyWith(items: <K>[...oldItems, ...items]));
} catch (e, stack) {
AppLogger.reportError(e, stack);
state = AsyncData(oldState!);
}
}
Future<List<K>> fetchAll() async {
if (state.value == null) return [];
if (!state.value!.hasMore) return state.value!.items.cast<K>();
bool hasMore = true;
while (hasMore) {
final newState = await fetch(
state.value!.nextOffset!,
max(state.value!.limit, 100),
)
.catchError(
(e) => fetch(state.value!.nextOffset!, max(state.value!.limit, 50)),
)
.catchError(
(e) => fetch(state.value!.nextOffset!, state.value!.limit),
)
.catchError(
(e) async {
await Future.delayed(const Duration(milliseconds: 500));
return fetch(state.value!.nextOffset!, state.value!.limit);
},
);
hasMore = newState.hasMore;
final oldItems =
state.value!.items.isEmpty ? <K>[] : state.value!.items.cast<K>();
final items = newState.items.isEmpty ? <K>[] : newState.items.cast<K>();
state = AsyncData(
newState.copyWith(items: [...oldItems, ...items]),
);
}
return state.value!.items.cast<K>();
}
}
abstract class PaginatedAsyncNotifier<K>
extends AsyncNotifier<SpotubePaginationResponseObject<K>>
with PaginatedAsyncNotifierMixin<K>, MetadataPluginMixin<K> {}
abstract class AutoDisposePaginatedAsyncNotifier<K>
extends AutoDisposeAsyncNotifier<SpotubePaginationResponseObject<K>>
with PaginatedAsyncNotifierMixin<K>, MetadataPluginMixin<K> {}