Files
supplements/lib/providers/settings_provider.dart

323 lines
10 KiB
Dart

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
enum ThemeOption {
system,
light,
dark,
}
class SettingsProvider extends ChangeNotifier {
ThemeOption _themeOption = ThemeOption.system;
// Profile fields
DateTime? _dateOfBirth;
String? _gender;
// Time range settings (stored as hours, 0-23)
int _morningStart = 5;
int _morningEnd = 10;
int _afternoonStart = 11;
int _afternoonEnd = 16;
int _eveningStart = 17;
int _eveningEnd = 22;
int _nightStart = 23;
int _nightEnd = 4;
// Persistent reminder settings
bool _persistentReminders = true;
int _reminderRetryInterval = 5; // minutes
int _maxRetryAttempts = 3;
// Auto-sync settings
bool _autoSyncEnabled = false;
int _autoSyncDebounceSeconds = 5;
ThemeOption get themeOption => _themeOption;
// Profile getters
DateTime? get dateOfBirth => _dateOfBirth;
String? get gender => _gender;
int? get age {
if (_dateOfBirth == null) return null;
final now = DateTime.now();
int years = now.year - _dateOfBirth!.year;
final hasHadBirthday = (now.month > _dateOfBirth!.month) ||
(now.month == _dateOfBirth!.month && now.day >= _dateOfBirth!.day);
if (!hasHadBirthday) years--;
return years;
}
// Time range getters
int get morningStart => _morningStart;
int get morningEnd => _morningEnd;
int get afternoonStart => _afternoonStart;
int get afternoonEnd => _afternoonEnd;
int get eveningStart => _eveningStart;
int get eveningEnd => _eveningEnd;
int get nightStart => _nightStart;
int get nightEnd => _nightEnd;
// Persistent reminder getters
bool get persistentReminders => _persistentReminders;
int get reminderRetryInterval => _reminderRetryInterval;
int get maxRetryAttempts => _maxRetryAttempts;
// Auto-sync getters
bool get autoSyncEnabled => _autoSyncEnabled;
int get autoSyncDebounceSeconds => _autoSyncDebounceSeconds;
// Helper method to get formatted time ranges for display
String get morningRange => '${_formatHour(_morningStart)} - ${_formatHour((_morningEnd + 1) % 24)}';
String get afternoonRange => '${_formatHour(_afternoonStart)} - ${_formatHour((_afternoonEnd + 1) % 24)}';
String get eveningRange => '${_formatHour(_eveningStart)} - ${_formatHour((_eveningEnd + 1) % 24)}';
String get nightRange => '${_formatHour(_nightStart)} - ${_formatHour((_nightEnd + 1) % 24)}';
String _formatHour(int hour) {
return '${hour.toString().padLeft(2, '0')}:00';
}
ThemeMode get themeMode {
switch (_themeOption) {
case ThemeOption.light:
return ThemeMode.light;
case ThemeOption.dark:
return ThemeMode.dark;
case ThemeOption.system:
return ThemeMode.system;
}
}
Future<void> initialize() async {
final prefs = await SharedPreferences.getInstance();
final themeIndex = prefs.getInt('theme_option') ?? 0;
_themeOption = ThemeOption.values[themeIndex];
// Load profile fields
final dobString = prefs.getString('date_of_birth');
if (dobString != null) {
_dateOfBirth = DateTime.tryParse(dobString);
}
_gender = prefs.getString('gender');
// Load time range settings
_morningStart = prefs.getInt('morning_start') ?? 5;
_morningEnd = prefs.getInt('morning_end') ?? 10;
_afternoonStart = prefs.getInt('afternoon_start') ?? 11;
_afternoonEnd = prefs.getInt('afternoon_end') ?? 16;
_eveningStart = prefs.getInt('evening_start') ?? 17;
_eveningEnd = prefs.getInt('evening_end') ?? 22;
_nightStart = prefs.getInt('night_start') ?? 23;
_nightEnd = prefs.getInt('night_end') ?? 4;
// Load persistent reminder settings
_persistentReminders = prefs.getBool('persistent_reminders') ?? true;
_reminderRetryInterval = prefs.getInt('reminder_retry_interval') ?? 5;
_maxRetryAttempts = prefs.getInt('max_retry_attempts') ?? 3;
// Load auto-sync settings
_autoSyncEnabled = prefs.getBool('auto_sync_enabled') ?? false;
_autoSyncDebounceSeconds = prefs.getInt('auto_sync_debounce_seconds') ?? 30;
notifyListeners();
}
Future<void> setThemeOption(ThemeOption option) async {
_themeOption = option;
notifyListeners();
final prefs = await SharedPreferences.getInstance();
await prefs.setInt('theme_option', option.index);
}
Future<void> setDateOfBirthAndGender(DateTime dateOfBirth, String gender) async {
_dateOfBirth = dateOfBirth;
_gender = gender;
notifyListeners();
final prefs = await SharedPreferences.getInstance();
await prefs.setString('date_of_birth', dateOfBirth.toIso8601String());
await prefs.setString('gender', gender);
}
Future<void> setTimeRanges({
required int morningStart,
required int morningEnd,
required int afternoonStart,
required int afternoonEnd,
required int eveningStart,
required int eveningEnd,
required int nightStart,
required int nightEnd,
}) async {
// Validate ranges don't overlap (simplified validation)
if (!_areTimeRangesValid(
morningStart, morningEnd,
afternoonStart, afternoonEnd,
eveningStart, eveningEnd,
nightStart, nightEnd,
)) {
throw ArgumentError('Time ranges overlap or are invalid');
}
_morningStart = morningStart;
_morningEnd = morningEnd;
_afternoonStart = afternoonStart;
_afternoonEnd = afternoonEnd;
_eveningStart = eveningStart;
_eveningEnd = eveningEnd;
_nightStart = nightStart;
_nightEnd = nightEnd;
notifyListeners();
final prefs = await SharedPreferences.getInstance();
await prefs.setInt('morning_start', morningStart);
await prefs.setInt('morning_end', morningEnd);
await prefs.setInt('afternoon_start', afternoonStart);
await prefs.setInt('afternoon_end', afternoonEnd);
await prefs.setInt('evening_start', eveningStart);
await prefs.setInt('evening_end', eveningEnd);
await prefs.setInt('night_start', nightStart);
await prefs.setInt('night_end', nightEnd);
}
bool _areTimeRangesValid(
int morningStart, int morningEnd,
int afternoonStart, int afternoonEnd,
int eveningStart, int eveningEnd,
int nightStart, int nightEnd,
) {
// Basic validation - ensure start < end for non-wrapping periods
if (morningStart > morningEnd) return false;
if (afternoonStart > afternoonEnd) return false;
if (eveningStart > eveningEnd) return false;
// Night can wrap around midnight, so we allow nightStart > nightEnd
// Check for overlaps in sequential periods
if (morningEnd >= afternoonStart) return false;
if (afternoonEnd >= eveningStart) return false;
if (eveningEnd >= nightStart) return false;
return true;
}
// Method to determine time category based on current settings
String determineTimeCategory(List<String> reminderTimes) {
if (reminderTimes.isEmpty) {
return 'anytime';
}
// Convert reminder times to hours for categorization
final hours = reminderTimes.map((time) {
final parts = time.split(':');
return int.tryParse(parts[0]) ?? 12;
}).toList();
// Count how many times fall into each category
int morningCount = 0;
int afternoonCount = 0;
int eveningCount = 0;
int nightCount = 0;
for (final hour in hours) {
if (hour >= _morningStart && hour <= _morningEnd) {
morningCount++;
} else if (hour >= _afternoonStart && hour <= _afternoonEnd) {
afternoonCount++;
} else if (hour >= _eveningStart && hour <= _eveningEnd) {
eveningCount++;
} else if (_isInNightRange(hour)) {
nightCount++;
}
}
// If supplement is taken throughout the day (has times in multiple periods)
final periodsCount = (morningCount > 0 ? 1 : 0) +
(afternoonCount > 0 ? 1 : 0) +
(eveningCount > 0 ? 1 : 0) +
(nightCount > 0 ? 1 : 0);
if (periodsCount >= 2) {
// Categorize based on the earliest reminder time for consistency
final earliestHour = hours.reduce((a, b) => a < b ? a : b);
if (earliestHour >= _morningStart && earliestHour <= _morningEnd) {
return 'morning';
} else if (earliestHour >= _afternoonStart && earliestHour <= _afternoonEnd) {
return 'afternoon';
} else if (earliestHour >= _eveningStart && earliestHour <= _eveningEnd) {
return 'evening';
} else if (_isInNightRange(earliestHour)) {
return 'night';
}
}
// If all times are in one period, categorize accordingly
if (morningCount > 0) {
return 'morning';
} else if (afternoonCount > 0) {
return 'afternoon';
} else if (eveningCount > 0) {
return 'evening';
} else if (nightCount > 0) {
return 'night';
} else {
return 'anytime';
}
}
bool _isInNightRange(int hour) {
// Night range can wrap around midnight
if (_nightStart <= _nightEnd) {
// Normal range (doesn't wrap around midnight)
return hour >= _nightStart && hour <= _nightEnd;
} else {
// Wrapping range (e.g., 23:00 to 4:00)
return hour >= _nightStart || hour <= _nightEnd;
}
}
// Persistent reminder setters
Future<void> setPersistentReminders(bool enabled) async {
_persistentReminders = enabled;
notifyListeners();
final prefs = await SharedPreferences.getInstance();
await prefs.setBool('persistent_reminders', enabled);
}
Future<void> setReminderRetryInterval(int minutes) async {
_reminderRetryInterval = minutes;
notifyListeners();
final prefs = await SharedPreferences.getInstance();
await prefs.setInt('reminder_retry_interval', minutes);
}
Future<void> setMaxRetryAttempts(int attempts) async {
_maxRetryAttempts = attempts;
notifyListeners();
final prefs = await SharedPreferences.getInstance();
await prefs.setInt('max_retry_attempts', attempts);
}
// Auto-sync setters
Future<void> setAutoSyncEnabled(bool enabled) async {
_autoSyncEnabled = enabled;
notifyListeners();
final prefs = await SharedPreferences.getInstance();
await prefs.setBool('auto_sync_enabled', enabled);
}
Future<void> setAutoSyncDebounceSeconds(int seconds) async {
_autoSyncDebounceSeconds = seconds;
notifyListeners();
final prefs = await SharedPreferences.getInstance();
await prefs.setInt('auto_sync_debounce_seconds', seconds);
}
}