|
1 | 1 | import 'dart:async';
|
2 | 2 |
|
| 3 | +import 'dart:async'; |
| 4 | + |
3 | 5 | import 'package:bloc/bloc.dart';
|
4 | 6 | import 'package:equatable/equatable.dart';
|
| 7 | +import 'package:flex_color_scheme/flex_color_scheme.dart'; // Added |
5 | 8 | import 'package:flutter/material.dart';
|
| 9 | +import 'package:google_fonts/google_fonts.dart'; // Added for font mapping |
6 | 10 | import 'package:ht_authentication_client/ht_authentication_client.dart';
|
7 | 11 | import 'package:ht_authentication_repository/ht_authentication_repository.dart';
|
| 12 | +import 'package:ht_preferences_client/ht_preferences_client.dart'; // Added |
| 13 | +import 'package:ht_preferences_repository/ht_preferences_repository.dart'; // Added |
8 | 14 |
|
9 | 15 | part 'app_event.dart';
|
10 | 16 | part 'app_state.dart';
|
11 | 17 |
|
12 | 18 | class AppBloc extends Bloc<AppEvent, AppState> {
|
13 |
| - AppBloc({required HtAuthenticationRepository authenticationRepository}) |
14 |
| - : _authenticationRepository = authenticationRepository, |
15 |
| - super(AppState()) { |
16 |
| - on<AppThemeChanged>(_onAppThemeChanged); |
| 19 | + AppBloc({ |
| 20 | + required HtAuthenticationRepository authenticationRepository, |
| 21 | + required HtPreferencesRepository preferencesRepository, // Added |
| 22 | + }) : _authenticationRepository = authenticationRepository, |
| 23 | + _preferencesRepository = preferencesRepository, // Added |
| 24 | + // Initialize with default state, load settings after user is known |
| 25 | + super(AppState()) { |
17 | 26 | on<AppUserChanged>(_onAppUserChanged);
|
| 27 | + // Add handler for explicitly refreshing settings if needed later |
| 28 | + on<AppSettingsRefreshed>(_onAppSettingsRefreshed); |
18 | 29 |
|
| 30 | + // Listen directly to the user stream |
19 | 31 | _userSubscription = _authenticationRepository.user.listen(
|
20 |
| - (user) => add(AppUserChanged(user)), |
| 32 | + (User user) => add(AppUserChanged(user)), // Explicitly type user |
21 | 33 | );
|
22 | 34 | }
|
23 | 35 |
|
24 | 36 | final HtAuthenticationRepository _authenticationRepository;
|
| 37 | + final HtPreferencesRepository _preferencesRepository; // Added |
25 | 38 | late final StreamSubscription<User> _userSubscription;
|
26 | 39 |
|
27 |
| - void _onAppThemeChanged(AppThemeChanged event, Emitter<AppState> emit) { |
28 |
| - emit( |
29 |
| - state.copyWith( |
30 |
| - themeMode: |
31 |
| - state.themeMode == ThemeMode.system |
32 |
| - ? ThemeMode.system |
33 |
| - : state.themeMode == ThemeMode.dark |
34 |
| - ? ThemeMode.dark |
35 |
| - : ThemeMode.light, |
36 |
| - ), |
37 |
| - ); |
38 |
| - } |
| 40 | + // Removed _onAppThemeChanged |
39 | 41 |
|
40 |
| - void _onAppUserChanged(AppUserChanged event, Emitter<AppState> emit) { |
| 42 | + /// Handles user changes and loads initial settings once user is available. |
| 43 | + Future<void> _onAppUserChanged( |
| 44 | + AppUserChanged event, |
| 45 | + Emitter<AppState> emit, |
| 46 | + ) async { |
41 | 47 | // Determine the AppStatus based on the user's AuthenticationStatus
|
42 | 48 | final AppStatus status;
|
43 | 49 | switch (event.user.authenticationStatus) {
|
44 | 50 | case AuthenticationStatus.unauthenticated:
|
45 | 51 | status = AppStatus.unauthenticated;
|
| 52 | + // Emit status change immediately for unauthenticated users |
| 53 | + emit(state.copyWith(status: status, user: event.user)); |
| 54 | + return; // Don't load settings for unauthenticated users |
46 | 55 | case AuthenticationStatus.anonymous:
|
47 | 56 | status = AppStatus.anonymous;
|
| 57 | + break; // Continue to load settings for anonymous |
48 | 58 | case AuthenticationStatus.authenticated:
|
49 | 59 | status = AppStatus.authenticated;
|
50 |
| - // Or handle as error |
| 60 | + break; // Continue to load settings for authenticated |
51 | 61 | }
|
52 |
| - // Emit the new state including both the updated status and the user object |
| 62 | + |
| 63 | + // Emit user and status update first |
53 | 64 | emit(state.copyWith(status: status, user: event.user));
|
| 65 | + |
| 66 | + // Load settings now that we have a user (anonymous or authenticated) |
| 67 | + // Use a separate event to avoid complexity within this handler |
| 68 | + add(const AppSettingsRefreshed()); |
| 69 | + } |
| 70 | + |
| 71 | + /// Handles refreshing/loading app settings (theme, font). |
| 72 | + Future<void> _onAppSettingsRefreshed( |
| 73 | + AppSettingsRefreshed event, |
| 74 | + Emitter<AppState> emit, |
| 75 | + ) async { |
| 76 | + // Avoid loading if user is unauthenticated (shouldn't happen if logic is correct) |
| 77 | + if (state.status == AppStatus.unauthenticated) return; |
| 78 | + |
| 79 | + try { |
| 80 | + // Fetch relevant settings |
| 81 | + final themeSettings = |
| 82 | + await _tryFetch(_preferencesRepository.getThemeSettings); |
| 83 | + final appSettings = |
| 84 | + await _tryFetch(_preferencesRepository.getAppSettings); |
| 85 | + |
| 86 | + // Map settings to AppState properties |
| 87 | + final newThemeMode = _mapAppThemeMode( |
| 88 | + themeSettings?.themeMode ?? AppThemeMode.system, // Default |
| 89 | + ); |
| 90 | + final newFlexScheme = _mapAppThemeName( |
| 91 | + themeSettings?.themeName ?? AppThemeName.grey, // Default |
| 92 | + ); |
| 93 | + final newFontFamily = _mapAppFontType( |
| 94 | + appSettings?.appFontType, // Nullable, default handled in theme |
| 95 | + ); |
| 96 | + |
| 97 | + emit( |
| 98 | + state.copyWith( |
| 99 | + themeMode: newThemeMode, |
| 100 | + flexScheme: newFlexScheme, |
| 101 | + fontFamily: newFontFamily, |
| 102 | + // Use clearFontFamily flag if appSettings was null and we want to reset |
| 103 | + clearFontFamily: appSettings == null, |
| 104 | + ), |
| 105 | + ); |
| 106 | + } catch (e) { |
| 107 | + // Handle potential errors during settings fetch |
| 108 | + // Optionally emit a failure state or log the error |
| 109 | + print('Error loading app settings in AppBloc: $e'); |
| 110 | + // Keep the existing theme/font state on error |
| 111 | + } |
| 112 | + } |
| 113 | + |
| 114 | + /// Helper to fetch a setting and handle PreferenceNotFoundException gracefully. |
| 115 | + Future<T?> _tryFetch<T>(Future<T> Function() fetcher) async { |
| 116 | + try { |
| 117 | + return await fetcher(); |
| 118 | + } on PreferenceNotFoundException { |
| 119 | + return null; // Setting not found, return null to use default |
| 120 | + } catch (e) { |
| 121 | + // Rethrow other errors to be caught by the caller |
| 122 | + rethrow; |
| 123 | + } |
| 124 | + } |
| 125 | + |
| 126 | + // --- Settings Mapping Helpers --- |
| 127 | + |
| 128 | + ThemeMode _mapAppThemeMode(AppThemeMode mode) { |
| 129 | + switch (mode) { |
| 130 | + case AppThemeMode.light: |
| 131 | + return ThemeMode.light; |
| 132 | + case AppThemeMode.dark: |
| 133 | + return ThemeMode.dark; |
| 134 | + case AppThemeMode.system: |
| 135 | + default: |
| 136 | + return ThemeMode.system; |
| 137 | + } |
| 138 | + } |
| 139 | + |
| 140 | + FlexScheme _mapAppThemeName(AppThemeName name) { |
| 141 | + switch (name) { |
| 142 | + case AppThemeName.red: |
| 143 | + return FlexScheme.red; |
| 144 | + case AppThemeName.blue: |
| 145 | + return FlexScheme.blue; |
| 146 | + case AppThemeName.grey: |
| 147 | + default: |
| 148 | + return FlexScheme.material; // Default grey maps to material |
| 149 | + } |
| 150 | + } |
| 151 | + |
| 152 | + String? _mapAppFontType(AppFontType? type) { |
| 153 | + if (type == null) return null; // Use theme default if null |
| 154 | + |
| 155 | + switch (type) { |
| 156 | + case AppFontType.roboto: |
| 157 | + return GoogleFonts.roboto().fontFamily; |
| 158 | + case AppFontType.openSans: |
| 159 | + return GoogleFonts.openSans().fontFamily; |
| 160 | + case AppFontType.lato: |
| 161 | + return GoogleFonts.lato().fontFamily; |
| 162 | + case AppFontType.montserrat: |
| 163 | + return GoogleFonts.montserrat().fontFamily; |
| 164 | + case AppFontType.merriweather: |
| 165 | + return GoogleFonts.merriweather().fontFamily; |
| 166 | + // Add other fonts if necessary |
| 167 | + default: |
| 168 | + return null; // Fallback to theme default |
| 169 | + } |
54 | 170 | }
|
55 | 171 |
|
56 | 172 | @override
|
|
0 commit comments