Files
ai-contract-platform/mobile/lib/shared/storage/session_store.dart
T
yuqei 3df85fdb04 feat: land phase15 legal docs and commercial ops
Includes legal document generation workflows, prompt governance runtime wiring, legal change impact scans, operation KPI dashboards, invoice files, sales follow-up events, mobile review polish, deployment automation, and updated project docs.
2026-05-21 14:42:44 +08:00

169 lines
4.5 KiB
Dart

import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../features/auth/config/auth_config.dart';
import '../../features/auth/domain/auth_session.dart';
abstract class SessionStorageDriver {
const SessionStorageDriver();
Future<String?> read(String key);
Future<void> write(String key, String value);
Future<void> delete(String key);
}
class SecureSessionStorageDriver implements SessionStorageDriver {
const SecureSessionStorageDriver();
static const FlutterSecureStorage _storage = FlutterSecureStorage();
@override
Future<String?> read(String key) {
return _storage.read(key: key);
}
@override
Future<void> write(String key, String value) {
return _storage.write(key: key, value: value);
}
@override
Future<void> delete(String key) {
return _storage.delete(key: key);
}
}
class InMemorySessionStorageDriver implements SessionStorageDriver {
InMemorySessionStorageDriver({Map<String, String>? initialValues})
: _values =
Map<String, String>.from(initialValues ?? const <String, String>{});
final Map<String, String> _values;
@override
Future<String?> read(String key) async {
return _values[key];
}
@override
Future<void> write(String key, String value) async {
_values[key] = value;
}
@override
Future<void> delete(String key) async {
_values.remove(key);
}
}
class SessionStore {
SessionStore._();
static final ValueNotifier<AuthSession?> _current =
ValueNotifier<AuthSession?>(null);
static SessionStorageDriver _storage = const SecureSessionStorageDriver();
static ValueListenable<AuthSession?> get listenable => _current;
static AuthSession? get current => _current.value;
static bool get isSignedIn => _current.value?.isAuthenticated ?? false;
@visibleForTesting
static void useStorageDriverForTesting(SessionStorageDriver storage) {
_storage = storage;
_current.value = null;
}
@visibleForTesting
static void resetStorageDriverForTesting() {
_storage = const SecureSessionStorageDriver();
_current.value = null;
}
static Future<void> hydrate() async {
if (!AuthConfig.persistSession) {
return;
}
final raw = await _loadPersistedSession();
if (raw == null || raw.isEmpty) {
return;
}
try {
final decoded = jsonDecode(raw);
if (decoded is Map<String, dynamic>) {
final session = AuthSession.fromJson(decoded);
if (session.isAuthenticated) {
_current.value = session;
} else {
_current.value = null;
await _deletePersistedSession();
}
}
} catch (_) {
_current.value = null;
await _deletePersistedSession();
}
}
static Future<void> save(AuthSession session) async {
_current.value = session;
if (!AuthConfig.persistSession) {
return;
}
await _storage.write(
AuthConfig.sessionStorageKey, jsonEncode(session.toJson()));
if (_storage is SecureSessionStorageDriver) {
await _deleteLegacySession();
}
}
static Future<void> clear() async {
_current.value = null;
if (!AuthConfig.persistSession) {
return;
}
await _storage.delete(AuthConfig.sessionStorageKey);
if (_storage is SecureSessionStorageDriver) {
await _deleteLegacySession();
}
}
static Future<String?> _loadPersistedSession() async {
final raw = await _storage.read(AuthConfig.sessionStorageKey);
if (raw != null && raw.isNotEmpty) {
return raw;
}
try {
final preferences = await SharedPreferences.getInstance();
final legacyRaw = preferences.getString(AuthConfig.sessionStorageKey);
if (legacyRaw == null || legacyRaw.isEmpty) {
return null;
}
await _storage.write(AuthConfig.sessionStorageKey, legacyRaw);
await preferences.remove(AuthConfig.sessionStorageKey);
return legacyRaw;
} on MissingPluginException {
return null;
}
}
static Future<void> _deletePersistedSession() async {
await _storage.delete(AuthConfig.sessionStorageKey);
await _deleteLegacySession();
}
static Future<void> _deleteLegacySession() async {
try {
final preferences = await SharedPreferences.getInstance();
await preferences.remove(AuthConfig.sessionStorageKey);
} on MissingPluginException {
// Unit tests that use an in-memory driver do not register the legacy plugin.
}
}
}