Files
photo-live/apps/mobile/lib/features/albums/providers.dart
T
2026-09-19 01:56:38 +08:00

126 lines
3.7 KiB
Dart

/// 相册列表 Riverpod 状态(P3 计划 Task 4)。
///
/// 首帧后自动拉首页(build 返回加载态,microtask 里触发首次加载,
/// 不在 build 内改 state);游标加载更多(失败保留已加载条目并提供
/// 重试标记);下拉刷新重置到第一页。创建/编辑/删除成功后由调用方
/// 调 [AlbumListNotifier.refresh] 同步。
library;
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../core/api_client.dart';
import '../../core/app_exception.dart';
import '../../core/error_codes.dart';
import 'models.dart';
/// 相册列表状态。
class AlbumListState {
const AlbumListState({
this.items = const <AlbumSummary>[],
this.nextCursor,
this.hasMore = false,
this.loading = false,
this.loadingMore = false,
this.loadMoreFailed = false,
this.errorCode,
});
/// 已加载条目(跨页累积)。
final List<AlbumSummary> items;
/// 下一页游标;null = 没有更多。
final String? nextCursor;
/// 是否还有下一页(与 [nextCursor] 成对)。
final bool hasMore;
/// 首次加载/刷新进行中。
final bool loading;
/// 加载更多进行中。
final bool loadingMore;
/// 最近一次加载更多失败(保留已加载条目,页脚显示重试)。
final bool loadMoreFailed;
/// 首屏/刷新失败的错误码([AppException.code]);null = 无错误。
final String? errorCode;
}
/// 相册列表 Notifier(游标分页状态机)。
class AlbumListNotifier extends Notifier<AlbumListState> {
/// 每页条数(契约列表端点通用上限内取 20)。
static const int pageSize = 20;
@override
AlbumListState build() {
// microtask:确保不在 build 执行期内改 state。
Future<void>.microtask(refresh);
return const AlbumListState(loading: true);
}
/// 重置到第一页(下拉刷新/数据变更后同步)。
Future<void> refresh() async {
state = const AlbumListState(loading: true);
try {
final page = await ref
.read(apiClientProvider)
.listAlbums(limit: pageSize);
state = AlbumListState(
items: page.items,
nextCursor: page.nextCursor,
hasMore: page.hasMore,
);
} on AppException catch (e) {
state = AlbumListState(errorCode: e.code);
} on Object {
state = const AlbumListState(errorCode: ErrorCodes.internalError);
}
}
/// 游标加载下一页(加载中/无更多时为空操作)。
Future<void> loadMore() async {
if (state.loading || state.loadingMore || !state.hasMore) return;
state = AlbumListState(
items: state.items,
nextCursor: state.nextCursor,
hasMore: true,
loadingMore: true,
);
try {
final page = await ref.read(apiClientProvider).listAlbums(
cursor: state.nextCursor,
limit: pageSize,
);
state = AlbumListState(
items: <AlbumSummary>[...state.items, ...page.items],
nextCursor: page.nextCursor,
hasMore: page.hasMore,
);
} on AppException catch (e) {
// 保留已加载条目:页脚给重试入口,错误码用于一次性提示语义。
state = AlbumListState(
items: state.items,
nextCursor: state.nextCursor,
hasMore: true,
loadMoreFailed: true,
errorCode: e.code,
);
} on Object {
state = AlbumListState(
items: state.items,
nextCursor: state.nextCursor,
hasMore: true,
loadMoreFailed: true,
errorCode: ErrorCodes.internalError,
);
}
}
}
/// 相册列表 Provider(首页相册 tab 消费)。
final albumListProvider =
NotifierProvider<AlbumListNotifier, AlbumListState>(
AlbumListNotifier.new,
);