Files
kooltab/lib/viewmodels/pin_lock_view_model.dart
T
2026-10-08 04:12:36 +02:00

167 lines
3.7 KiB
Dart

import 'package:flutter/foundation.dart';
import '../models/app_error.dart';
import '../services/pin_lock_service.dart';
import '../services/settings_service.dart';
class PinLockViewModel extends ChangeNotifier {
final PinLockService pinLockService;
final SettingsService? settingsService;
PinLockViewModel({required this.pinLockService, this.settingsService});
bool _isPinSet = false;
bool _isUnlocked = false;
bool _isLoading = false;
bool _hasLoaded = false;
bool _pinRequired = true;
AppError? _error;
bool get isPinSet => _isPinSet;
bool get isUnlocked => _isUnlocked;
bool get isLoading => _isLoading;
bool get hasLoaded => _hasLoaded;
bool get pinRequired => _pinRequired;
AppError? get error => _error;
void setPinRequired(bool value) {
if (_pinRequired == value) return;
_pinRequired = value;
notifyListeners();
}
Future<void> ensureLoaded() async {
if (_hasLoaded || _isLoading) return;
await load();
}
// Checks whether a pin has already been configured on this device.
Future<void> load() async {
if (_isLoading) return;
_isLoading = true;
_error = null;
notifyListeners();
try {
_isPinSet = await pinLockService.hasPin();
final svc = settingsService;
if (svc != null) {
final settings = await svc.getSettings();
_pinRequired = settings.pinRequired;
}
} catch (e) {
debugPrint('PinLockViewModel: load error: $e');
_error = AppError.checkPinStatus;
} finally {
_hasLoaded = true;
_isLoading = false;
notifyListeners();
}
}
Future<bool> setPin(String pin) async {
_error = null;
try {
await pinLockService.setPin(pin);
_isPinSet = true;
_isUnlocked = true;
_pinRequired = true;
final svc = settingsService;
if (svc != null) {
final current = await svc.getSettings();
await svc.saveSettings(current.copyWith(pinRequired: true));
}
notifyListeners();
return true;
} catch (e) {
debugPrint('PinLockViewModel: setPin error: $e');
_error = AppError.savePin;
notifyListeners();
return false;
}
}
Future<bool> verify(String pin) async {
_error = null;
try {
final matches = await pinLockService.verifyPin(pin);
if (matches) {
_isUnlocked = true;
notifyListeners();
return true;
}
_error = AppError.incorrectPin;
notifyListeners();
return false;
} on PinLockedOutException {
_error = AppError.tooManyPinAttempts;
notifyListeners();
return false;
} catch (e) {
debugPrint('PinLockViewModel: verify error: $e');
_error = AppError.verifyPin;
notifyListeners();
return false;
}
}
Future<bool> changePin({
required String currentPin,
required String newPin,
}) async {
if (!await _verifyCurrentPin(currentPin)) return false;
return setPin(newPin);
}
Future<bool> disablePin(String currentPin) async {
if (!await _verifyCurrentPin(currentPin)) return false;
await pinLockService.clearPin();
_isPinSet = false;
_pinRequired = false;
final svc = settingsService;
if (svc != null) {
final current = await svc.getSettings();
await svc.saveSettings(current.copyWith(pinRequired: false));
}
notifyListeners();
return true;
}
Future<bool> _verifyCurrentPin(String pin) async {
try {
if (await pinLockService.verifyPin(pin)) return true;
_error = AppError.currentPinIncorrect;
} on PinLockedOutException {
_error = AppError.tooManyPinAttempts;
}
notifyListeners();
return false;
}
void lock() {
if (!_isUnlocked) return;
_isUnlocked = false;
notifyListeners();
}
}