bbea1dd364
Co-Authored-By: Claude Code <noreply@anthropic.com>
126 lines
3.7 KiB
Dart
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,
|
|
);
|