78 lines
2.9 KiB
Dart
78 lines
2.9 KiB
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
import '../api/ai_api.dart';
|
|
import '../core/storage/local_storage.dart';
|
|
import '../models/ai_persona.dart';
|
|
|
|
/// 可选的人设列表(含虚拟的「默认」项)
|
|
///
|
|
/// 与 [aiConfigsProvider] 完全平行:一个拉模型配置,一个拉人设。
|
|
final aiPersonasProvider = FutureProvider.autoDispose<List<AiPersonaItem>>((ref) {
|
|
return ref.watch(aiApiProvider).personas();
|
|
});
|
|
|
|
/// 当前选中的人设。
|
|
///
|
|
/// **不设 autoDispose**:理由和 [SelectedAiConfigNotifier] 一样 ——
|
|
/// 选完回对话页要立刻生效,切页面不该丢。
|
|
class SelectedAiPersonaNotifier extends Notifier<int?> {
|
|
@override
|
|
int? build() {
|
|
// 存的是「真实 id」,null = 用内置默认。
|
|
// 默认项在列表里的 id 也是 null,两边语义天然一致。
|
|
return LocalStorage.instance.getAiPersonaId();
|
|
}
|
|
|
|
void select(int? id) {
|
|
state = id;
|
|
LocalStorage.instance.saveAiPersonaId(id);
|
|
}
|
|
}
|
|
|
|
final selectedAiPersonaProvider =
|
|
NotifierProvider<SelectedAiPersonaNotifier, int?>(SelectedAiPersonaNotifier.new);
|
|
|
|
/// 从人设列表里挑出当前选中的那一条。
|
|
///
|
|
/// 三种情况都要兜住:
|
|
/// · 还没选过 → 用列表第一项(也就是「默认」)
|
|
/// · 选过但那条被删了 → 退回第一项,并顺手把本地选择改掉
|
|
/// · 列表还没加载出来 → 返回 null,界面显示「--」
|
|
///
|
|
/// 注意这里和服务端的降级是**两套独立的安全网**:服务端拿到过期 id 也会
|
|
/// 退回内置默认,所以就算这里没来得及纠正,对话也不会挂。
|
|
final activeAiPersonaProvider = Provider.autoDispose<AiPersonaItem?>((ref) {
|
|
final personas = ref.watch(aiPersonasProvider).value;
|
|
if (personas == null || personas.isEmpty) return null;
|
|
|
|
final selectedId = ref.watch(selectedAiPersonaProvider);
|
|
if (selectedId == null) return personas.first;
|
|
|
|
for (final item in personas) {
|
|
if (item.selectionKey == selectedId) return item;
|
|
}
|
|
|
|
// 选中的那条已经不存在了(被删了)。退回默认。
|
|
// 放在微任务里改 state —— build 过程中不能同步改另一个 provider。
|
|
Future.microtask(() {
|
|
ref.read(selectedAiPersonaProvider.notifier).select(null);
|
|
});
|
|
return personas.first;
|
|
});
|
|
|
|
/// 内置默认人设文本,供编辑页「恢复默认」按钮使用。
|
|
///
|
|
/// 从列表的默认项里取(服务端下发),拿不到时返回 null ——
|
|
/// 这时按钮会提示「还没加载出来」,而不是塞一份 App 里硬编码的文本进去
|
|
/// (那样两边迟早不一致)。
|
|
final defaultPersonaPromptProvider = Provider.autoDispose<String?>((ref) {
|
|
final personas = ref.watch(aiPersonasProvider).value;
|
|
if (personas == null) return null;
|
|
for (final p in personas) {
|
|
if (p.isDefault && (p.defaultPrompt?.isNotEmpty ?? false)) {
|
|
return p.defaultPrompt;
|
|
}
|
|
}
|
|
return null;
|
|
});
|