ponshu-room-lite/lib/providers/theme_provider.dart

222 lines
6.4 KiB
Dart

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<Box<UserProfile>>((ref) {
// Directly access the open box (Splash Screen ensures this is open)
return Hive.box<UserProfile>('user_profile');
});
// Central Notifier for User Profile (Font, Theme, Identity)
final userProfileProvider = NotifierProvider<UserProfileNotifier, UserProfile>(UserProfileNotifier.new);
class UserProfileNotifier extends Notifier<UserProfile> {
late Box<UserProfile> _box;
@override
UserProfile build() {
// Phase 1 Optimization: Access box directly as it's guaranteed to be open by SplashScreen
_box = Hive.box<UserProfile>('user_profile');
// Return existing profile or create default
return _box.get('current_user') ??
UserProfile(createdAt: DateTime.now());
}
Future<void> _save(UserProfile profile) async {
state = profile; // Update UI immediately
await _box.put('current_user', profile);
}
Future<void> setFontPreference(String preference) async {
final newState = state.copyWith(
fontPreference: preference,
updatedAt: DateTime.now(),
);
await _save(newState);
}
Future<void> setThemeMode(String mode) async {
final newState = state.copyWith(
themeMode: mode,
updatedAt: DateTime.now(),
);
await _save(newState);
}
Future<void> 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<void> setSakePersonaMbti(String? persona) async {
final newState = state.copyWith(
sakePersonaMbti: persona,
updatedAt: DateTime.now(),
);
await _save(newState);
}
Future<void> toggleBusinessMode() async {
final newState = state.copyWith(
isBusinessMode: !state.isBusinessMode,
updatedAt: DateTime.now(),
);
await _save(newState);
}
Future<void> setDefaultMarkup(double value) async {
final newState = state.copyWith(
defaultMarkup: value,
updatedAt: DateTime.now(),
);
await _save(newState);
}
Future<void> completeOnboarding() async {
final newState = state.copyWith(
hasCompletedOnboarding: true,
updatedAt: DateTime.now(),
);
await _save(newState);
}
Future<void> updateTotalExp(int newExp) async {
final newState = state.copyWith(
totalExp: newExp,
updatedAt: DateTime.now(),
);
await _save(newState);
}
Future<void> updateUnlockedBadges(List<String> 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<void> setLocale(String locale) async {
final newState = state.copyWith(
locale: locale,
updatedAt: DateTime.now(),
);
await _save(newState);
}
Future<void> setColorVariant(String variant) async {
final newState = state.copyWith(
colorVariant: variant,
updatedAt: DateTime.now(),
);
await _save(newState);
}
}
// Helper Providers for easy access
final fontPreferenceProvider = Provider<String>((ref) {
return ref.watch(userProfileProvider).fontPreference;
});
// v2.0: Enhanced Theme Logic
final themeModeProvider = Provider<ThemeMode>((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<String>((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<ThemeData>((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<ThemeData>((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<Locale>((ref) {
final localeCode = ref.watch(userProfileProvider.select((p) => p.locale));
// Support: 'ja', 'en', 'fr', 'de'
return Locale(localeCode);
});