import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:health/health.dart'; import '../../providers/user_provider.dart'; import '../../services/health_service.dart'; class PrivacySettingsScreen extends ConsumerStatefulWidget { const PrivacySettingsScreen({super.key}); @override ConsumerState createState() => _PrivacySettingsScreenState(); } class _PrivacySettingsScreenState extends ConsumerState { bool _hasPermissions = false; final HealthService _healthService = HealthService(); @override void initState() { super.initState(); _checkPermissions(); } Future _checkPermissions() async { final hasPermissions = await _healthService.hasPermissions(_healthService.menstruationDataTypes); setState(() { _hasPermissions = hasPermissions; }); } Future _requestPermissions() async { final authorized = await _healthService.requestAuthorization(_healthService.menstruationDataTypes); if (authorized) { _hasPermissions = true; ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Health app access granted!')), ); } else { _hasPermissions = false; ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Health app access denied.')), ); } setState(() {}); // Rebuild to update UI } Future _syncPeriodDays(bool sync) async { if (sync) { if (!_hasPermissions) { // Request permissions if not already granted final authorized = await _healthService.requestAuthorization(_healthService.menstruationDataTypes); if (!authorized) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Cannot sync without health app permissions.')), ); } return; } _hasPermissions = true; } if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Syncing period data...')), ); } final userProfile = ref.read(userProfileProvider); final cycleEntries = ref.read(cycleEntriesProvider); if (userProfile != null) { final success = await _healthService.writeMenstruationData(cycleEntries); if (mounted) { if (success) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Period data synced successfully!')), ); } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Failed to sync period data.')), ); } } } } else { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Period data sync disabled.')), ); } } setState(() {}); // Rebuild to update UI } Future _setPin() async { final pin = await _showPinDialog(context, title: 'Set New PIN'); if (pin != null && pin.length >= 4) { final user = ref.read(userProfileProvider); if (user != null) { await ref.read(userProfileProvider.notifier).updateProfile(user.copyWith(privacyPin: pin)); if (mounted) ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('PIN Set Successfully'))); } } } Future _removePin() async { final user = ref.read(userProfileProvider); if (user == null) return; // Require current PIN final currentPin = await _showPinDialog(context, title: 'Enter Current PIN'); if (currentPin == user.privacyPin) { await ref.read(userProfileProvider.notifier).updateProfile( // To clear fields, copyWith might need to handle nulls explicitly if written that way, // but here we might pass empty string or handle logic. // Actually, copyWith signature usually ignores nulls. // I'll assume updating with empty string or handle it in provider, // but for now let's just use empty string to signify removal if logic supports it. // Wait, copyWith `privacyPin: privacyPin ?? this.privacyPin`. // If I pass null, it keeps existing. I can't clear it via standard copyWith unless I change copyWith logic or pass emptiness. // I'll update the userProfile object directly and save? No, Hive object. // For now, let's treat "empty string" as no PIN if I can pass it. user.copyWith(privacyPin: '', isBioProtected: false, isHistoryProtected: false) ); if (mounted) ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('PIN Removed'))); } else { if (mounted) ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Incorrect PIN'))); } } Future _showPinDialog(BuildContext context, {required String title}) { final controller = TextEditingController(); return showDialog( context: context, builder: (context) => AlertDialog( title: Text(title), content: TextField( controller: controller, keyboardType: TextInputType.number, obscureText: true, maxLength: 4, decoration: const InputDecoration(hintText: 'Enter 4-digit PIN'), ), actions: [ TextButton(onPressed: () => Navigator.pop(context), child: const Text('Cancel')), ElevatedButton( onPressed: () => Navigator.pop(context, controller.text), child: const Text('OK'), ), ], ), ); } @override Widget build(BuildContext context) { bool syncPeriodToHealth = _hasPermissions; final user = ref.watch(userProfileProvider); final hasPin = user?.privacyPin != null && user!.privacyPin!.isNotEmpty; return Scaffold( appBar: AppBar( title: const Text('Privacy Settings'), ), body: ListView( padding: const EdgeInsets.all(16.0), children: [ // Security Section Text('App Security', style: Theme.of(context).textTheme.titleMedium?.copyWith(color: Theme.of(context).colorScheme.primary)), const SizedBox(height: 8), ListTile( title: const Text('Privacy PIN'), subtitle: Text(hasPin ? 'PIN is set' : 'Protect sensitive data with a PIN'), trailing: hasPin ? const Icon(Icons.lock, color: Colors.green) : const Icon(Icons.lock_open), onTap: () { if (hasPin) { showModalBottomSheet(context: context, builder: (context) => Column( mainAxisSize: MainAxisSize.min, children: [ ListTile( leading: const Icon(Icons.edit), title: const Text('Change PIN'), onTap: () { Navigator.pop(context); _setPin(); }, ), ListTile( leading: const Icon(Icons.delete, color: Colors.red), title: const Text('Remove PIN'), onTap: () { Navigator.pop(context); _removePin(); }, ), ], )); } else { _setPin(); } }, ), if (hasPin) ...[ SwitchListTile( title: const Text('Use Biometrics'), subtitle: const Text('Unlock with FaceID / Fingerprint'), value: user?.isBioProtected ?? false, onChanged: (val) { ref.read(userProfileProvider.notifier).updateProfile(user!.copyWith(isBioProtected: val)); }, ), const Divider(), const Text('Protected Features', style: TextStyle(fontWeight: FontWeight.bold, color: Colors.grey)), SwitchListTile( title: const Text('Daily Logs'), value: user?.isLogProtected ?? false, onChanged: (val) { ref.read(userProfileProvider.notifier).updateProfile(user!.copyWith(isLogProtected: val)); }, ), SwitchListTile( title: const Text('Calendar'), value: user?.isCalendarProtected ?? false, onChanged: (val) { ref.read(userProfileProvider.notifier).updateProfile(user!.copyWith(isCalendarProtected: val)); }, ), SwitchListTile( title: const Text('Supplies / Pad Tracker'), value: user?.isSuppliesProtected ?? false, onChanged: (val) { ref.read(userProfileProvider.notifier).updateProfile(user!.copyWith(isSuppliesProtected: val)); }, ), SwitchListTile( title: const Text('Cycle History'), value: user?.isHistoryProtected ?? false, onChanged: (val) { ref.read(userProfileProvider.notifier).updateProfile(user!.copyWith(isHistoryProtected: val)); }, ), ], const Divider(height: 32), // Health Section Text('Health App Integration', style: Theme.of(context).textTheme.titleMedium?.copyWith(color: Theme.of(context).colorScheme.primary)), const SizedBox(height: 8), ListTile( title: const Text('Health Source'), subtitle: _hasPermissions ? const Text('Connected to Health App.') : const Text('Not connected. Tap to grant access.'), trailing: _hasPermissions ? const Icon(Icons.check_circle, color: Colors.green) : const Icon(Icons.warning, color: Colors.orange), onTap: _requestPermissions, ), SwitchListTile( title: const Text('Sync Period Days'), subtitle: const Text('Automatically sync period dates.'), value: syncPeriodToHealth, onChanged: _hasPermissions ? (value) async { if (value) { await _syncPeriodDays(true); } else { await _syncPeriodDays(false); } setState(() { syncPeriodToHealth = value; // Update local state for toggle }); } : null, ), ], ), ); } }