import 'dart:convert'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; /// A guardian login request waiting for its code or link. Persisted because /// Android often kills the app while the user reads the mail. class PendingGuardianRequest { static const int defaultCodeLength = 6; final String requestId; final String email; final String deviceSecret; final DateTime expiresAt; final DateTime resendAvailableAt; final int codeLength; const PendingGuardianRequest({ required this.requestId, required this.email, required this.deviceSecret, required this.expiresAt, required this.resendAvailableAt, this.codeLength = defaultCodeLength, }); bool isExpired(DateTime now) => !now.isBefore(expiresAt); Map toJson() => { 'requestId': requestId, 'email': email, 'deviceSecret': deviceSecret, 'expiresAt': expiresAt.toIso8601String(), 'resendAvailableAt': resendAvailableAt.toIso8601String(), 'codeLength': codeLength, }; static PendingGuardianRequest? fromJson(Map json) { try { return PendingGuardianRequest( requestId: json['requestId'] as String, email: json['email'] as String, deviceSecret: json['deviceSecret'] as String, expiresAt: DateTime.parse(json['expiresAt'] as String), resendAvailableAt: DateTime.parse(json['resendAvailableAt'] as String), codeLength: json['codeLength'] as int? ?? defaultCodeLength, ); } on Object { return null; } } } class PendingGuardianRequestStore { static const _key = 'guardian_login_pending_request'; static const FlutterSecureStorage _storage = FlutterSecureStorage( iOptions: IOSOptions(accessibility: KeychainAccessibility.first_unlock), ); const PendingGuardianRequestStore(); Future read() async { try { final raw = await _storage.read(key: _key); if (raw == null) return null; return PendingGuardianRequest.fromJson( jsonDecode(raw) as Map, ); } on Object { return null; } } Future write(PendingGuardianRequest request) => _storage.write(key: _key, value: jsonEncode(request.toJson())); Future clear() async { try { await _storage.delete(key: _key); } on Object { // Nothing stored or keystore unavailable. } } }