import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter/material.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'app_lifecycle_provider.dart'; // v2.0: Battery-efficient observer import '../models/user_profile.dart'; import '../theme/app_theme.dart'; // v2.0: Theme Mode Enum enum AppThemeMode { light, dark, system, autoTime, // Night Mode (20:00 - 06:00) } // Provider for the UserProfile box // Provider for the UserProfile box final userProfileBoxProvider = Provider>((ref) { // Directly access the open box (Splash Screen ensures this is open) return Hive.box('user_profile'); }); // Central Notifier for User Profile (Font, Theme, Identity) final userProfileProvider = NotifierProvider(UserProfileNotifier.new); class UserProfileNotifier extends Notifier { late Box _box; @override UserProfile build() { // Phase 1 Optimization: Access box directly as it's guaranteed to be open by SplashScreen _box = Hive.box('user_profile'); // Return existing profile or create default return _box.get('current_user') ?? UserProfile(createdAt: DateTime.now()); } Future _save(UserProfile profile) async { state = profile; // Update UI immediately await _box.put('current_user', profile); } Future setFontPreference(String preference) async { final newState = state.copyWith( fontPreference: preference, updatedAt: DateTime.now(), ); await _save(newState); } Future setThemeMode(String mode) async { final newState = state.copyWith( themeMode: mode, updatedAt: DateTime.now(), ); await _save(newState); } Future setIdentity({String? mbti, String? sakePersonaMbti, DateTime? birthdate, String? nickname, String? gender}) async { final newState = state.copyWith( mbti: mbti ?? state.mbti, sakePersonaMbti: sakePersonaMbti ?? state.sakePersonaMbti, birthdate: birthdate ?? state.birthdate, nickname: nickname ?? state.nickname, gender: gender ?? state.gender, updatedAt: DateTime.now(), ); await _save(newState); } Future setSakePersonaMbti(String? persona) async { final newState = state.copyWith( sakePersonaMbti: persona, updatedAt: DateTime.now(), ); await _save(newState); } Future toggleBusinessMode() async { final newState = state.copyWith( isBusinessMode: !state.isBusinessMode, updatedAt: DateTime.now(), ); await _save(newState); } Future setDefaultMarkup(double value) async { final newState = state.copyWith( defaultMarkup: value, updatedAt: DateTime.now(), ); await _save(newState); } Future completeOnboarding() async { final newState = state.copyWith( hasCompletedOnboarding: true, updatedAt: DateTime.now(), ); await _save(newState); } Future updateTotalExp(int newExp) async { final newState = state.copyWith( totalExp: newExp, updatedAt: DateTime.now(), ); await _save(newState); } Future updateUnlockedBadges(List badges) async { final newState = state.copyWith( unlockedBadges: badges, updatedAt: DateTime.now(), ); await _save(newState); } // REMOVED: completeTutorial() - Tutorial system removed in favor of guide screen Future setLocale(String locale) async { final newState = state.copyWith( locale: locale, updatedAt: DateTime.now(), ); await _save(newState); } Future setColorVariant(String variant) async { final newState = state.copyWith( colorVariant: variant, updatedAt: DateTime.now(), ); await _save(newState); } } // Helper Providers for easy access final fontPreferenceProvider = Provider((ref) { return ref.watch(userProfileProvider).fontPreference; }); // v2.0: Enhanced Theme Logic final themeModeProvider = Provider((ref) { final modeString = ref.watch(userProfileProvider).themeMode; // Mapping String -> Enum logic if (modeString == 'auto_time') { // Dependency on lifecycle to trigger updates when app resumes ref.watch(appLifecycleProvider); final hour = DateTime.now().hour; // 20:00 - 06:00 is Night final isNight = hour >= 20 || hour < 6; return isNight ? ThemeMode.dark : ThemeMode.light; } switch (modeString) { case 'light': return ThemeMode.light; case 'dark': return ThemeMode.dark; default: return ThemeMode.system; } }); // Helper Providers final colorVariantProvider = Provider((ref) { return ref.watch(userProfileProvider).colorVariant; }); // Helper function to map font string to enum AppFontStyle _mapFontString(String fontString) { switch (fontString) { case 'sans': return AppFontStyle.sans; case 'serif': return AppFontStyle.serif; case 'pottaOne': return AppFontStyle.pottaOne; case 'digital': return AppFontStyle.digital; default: return AppFontStyle.sans; // Default to sans } } // Helper function to map color variant string to enum ColorVariant _mapColorVariant(String variantString) { switch (variantString) { case 'washi_sumi_kohaku': return ColorVariant.washiSumiKohaku; case 'current': return ColorVariant.current; default: return ColorVariant.washiSumiKohaku; // Default to Theme A } } // Theme Data Providers final lightThemeProvider = Provider((ref) { final fontString = ref.watch(fontPreferenceProvider); final fontStyle = _mapFontString(fontString); final variantString = ref.watch(colorVariantProvider); final colorVariant = _mapColorVariant(variantString); return AppTheme.createTheme(fontStyle, Brightness.light, colorVariant); }); final darkThemeProvider = Provider((ref) { final fontString = ref.watch(fontPreferenceProvider); final fontStyle = _mapFontString(fontString); final variantString = ref.watch(colorVariantProvider); final colorVariant = _mapColorVariant(variantString); return AppTheme.createTheme(fontStyle, Brightness.dark, colorVariant); }); // Locale Provider final localeProvider = Provider((ref) { final localeCode = ref.watch(userProfileProvider.select((p) => p.locale)); // Support: 'ja', 'en', 'fr', 'de' return Locale(localeCode); });