f655adfbea
Migrate app code into canonical feature slices, add phone-only AI digest scheduling and review, wire local notification/background task support, and cover the flow with tests.
108 lines
3.1 KiB
Dart
108 lines
3.1 KiB
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
|
|
|
const String _llmApiKeyStorageKey = 'llm_api_key';
|
|
const String _llmProviderStorageKey = 'llm_provider';
|
|
|
|
enum LlmProvider { openai, anthropic, google }
|
|
|
|
class LlmConfigState {
|
|
const LlmConfigState({
|
|
this.apiKeyConfigured = false,
|
|
this.provider = LlmProvider.openai,
|
|
this.isConfigured = false,
|
|
});
|
|
|
|
final bool apiKeyConfigured;
|
|
final LlmProvider provider;
|
|
final bool isConfigured;
|
|
|
|
LlmConfigState copyWith({
|
|
bool? apiKeyConfigured,
|
|
LlmProvider? provider,
|
|
bool? isConfigured,
|
|
}) {
|
|
return LlmConfigState(
|
|
apiKeyConfigured: apiKeyConfigured ?? this.apiKeyConfigured,
|
|
provider: provider ?? this.provider,
|
|
isConfigured: isConfigured ?? this.isConfigured,
|
|
);
|
|
}
|
|
}
|
|
|
|
class LlmConfigNotifier extends Notifier<LlmConfigState> {
|
|
Future<void>? _initializeFuture;
|
|
|
|
@override
|
|
LlmConfigState build() {
|
|
return const LlmConfigState();
|
|
}
|
|
|
|
Future<void> initialize() async {
|
|
final Future<void>? inFlight = _initializeFuture;
|
|
if (inFlight != null) {
|
|
return inFlight;
|
|
}
|
|
_initializeFuture = _initializeFromStorage();
|
|
return _initializeFuture;
|
|
}
|
|
|
|
Future<void> _initializeFromStorage() async {
|
|
const FlutterSecureStorage secureStorage = FlutterSecureStorage();
|
|
final String? storedKey = await secureStorage.read(
|
|
key: _llmApiKeyStorageKey,
|
|
);
|
|
final String? storedProvider = await secureStorage.read(
|
|
key: _llmProviderStorageKey,
|
|
);
|
|
|
|
state = LlmConfigState(
|
|
apiKeyConfigured: storedKey != null && storedKey.isNotEmpty,
|
|
provider: storedProvider != null
|
|
? LlmProvider.values.firstWhere(
|
|
(LlmProvider p) => p.name == storedProvider,
|
|
orElse: () => LlmProvider.openai,
|
|
)
|
|
: LlmProvider.openai,
|
|
isConfigured: storedKey != null && storedKey.isNotEmpty,
|
|
);
|
|
}
|
|
|
|
Future<void> setApiKey(String apiKey) async {
|
|
const FlutterSecureStorage secureStorage = FlutterSecureStorage();
|
|
if (apiKey.isEmpty) {
|
|
await secureStorage.delete(key: _llmApiKeyStorageKey);
|
|
} else {
|
|
await secureStorage.write(key: _llmApiKeyStorageKey, value: apiKey);
|
|
}
|
|
state = state.copyWith(
|
|
apiKeyConfigured: apiKey.isNotEmpty,
|
|
isConfigured: apiKey.isNotEmpty,
|
|
);
|
|
}
|
|
|
|
Future<void> setProvider(LlmProvider provider) async {
|
|
const FlutterSecureStorage secureStorage = FlutterSecureStorage();
|
|
await secureStorage.write(
|
|
key: _llmProviderStorageKey,
|
|
value: provider.name,
|
|
);
|
|
state = state.copyWith(provider: provider);
|
|
}
|
|
|
|
Future<String?> getApiKey() async {
|
|
const FlutterSecureStorage secureStorage = FlutterSecureStorage();
|
|
return secureStorage.read(key: _llmApiKeyStorageKey);
|
|
}
|
|
|
|
Future<void> clearApiKey() async {
|
|
const FlutterSecureStorage secureStorage = FlutterSecureStorage();
|
|
await secureStorage.delete(key: _llmApiKeyStorageKey);
|
|
state = state.copyWith(apiKeyConfigured: false, isConfigured: false);
|
|
}
|
|
}
|
|
|
|
final llmConfigProvider = NotifierProvider<LlmConfigNotifier, LlmConfigState>(
|
|
LlmConfigNotifier.new,
|
|
);
|