222 lines
6.4 KiB
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);
|
|
});
|