Skip to content

Commit cfc4ab5

Browse files
authored
Merge pull request #27 from headlines-toolkit/feature_settings_enahancement_and_bug_fixe
Feature settings enahancement and bug fixe
2 parents 25715ae + 0bf24ae commit cfc4ab5

15 files changed

+659
-290
lines changed

lib/app/bloc/app_bloc.dart

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,12 @@ class AppBloc extends Bloc<AppEvent, AppState> {
101101
final newAppTextScaleFactor = _mapTextScaleFactor(
102102
userAppSettings.displaySettings.textScaleFactor,
103103
);
104+
// Map language code to Locale
105+
final newLocale = Locale(userAppSettings.language);
106+
107+
print('[AppBloc] _onAppSettingsRefreshed: userAppSettings.fontFamily: ${userAppSettings.displaySettings.fontFamily}');
108+
print('[AppBloc] _onAppSettingsRefreshed: userAppSettings.fontWeight: ${userAppSettings.displaySettings.fontWeight}');
109+
print('[AppBloc] _onAppSettingsRefreshed: newFontFamily mapped to: $newFontFamily');
104110

105111
emit(
106112
state.copyWith(
@@ -109,6 +115,7 @@ class AppBloc extends Bloc<AppEvent, AppState> {
109115
appTextScaleFactor: newAppTextScaleFactor,
110116
fontFamily: newFontFamily,
111117
settings: userAppSettings, // Store the fetched settings
118+
locale: newLocale, // Store the new locale
112119
),
113120
);
114121
} on NotFoundException {
@@ -120,6 +127,7 @@ class AppBloc extends Bloc<AppEvent, AppState> {
120127
themeMode: ThemeMode.system,
121128
flexScheme: FlexScheme.material,
122129
appTextScaleFactor: AppTextScaleFactor.medium, // Default enum value
130+
locale: const Locale('en'), // Default to English if settings not found
123131
settings: UserAppSettings(
124132
id: state.user!.id,
125133
), // Provide default settings
@@ -243,26 +251,17 @@ class AppBloc extends Bloc<AppEvent, AppState> {
243251
}
244252
}
245253

246-
String? _mapFontFamily(String fontFamily) {
247-
// Assuming 'SystemDefault' means use the theme's default font
248-
if (fontFamily == 'SystemDefault') return null;
249-
250-
// Map specific font family names to GoogleFonts
251-
switch (fontFamily) {
252-
case 'Roboto':
253-
return GoogleFonts.roboto().fontFamily;
254-
case 'OpenSans':
255-
return GoogleFonts.openSans().fontFamily;
256-
case 'Lato':
257-
return GoogleFonts.lato().fontFamily;
258-
case 'Montserrat':
259-
return GoogleFonts.montserrat().fontFamily;
260-
case 'Merriweather':
261-
return GoogleFonts.merriweather().fontFamily;
262-
default:
263-
// If an unknown font family is specified, fall back to theme default
264-
return null;
254+
String? _mapFontFamily(String fontFamilyString) {
255+
// If the input is 'SystemDefault', return null so FlexColorScheme uses its default.
256+
if (fontFamilyString == 'SystemDefault') {
257+
print('[AppBloc] _mapFontFamily: Input is SystemDefault, returning null.');
258+
return null;
265259
}
260+
// Otherwise, return the font family string directly.
261+
// The GoogleFonts.xyz().fontFamily getters often return strings like "Roboto-Regular",
262+
// but FlexColorScheme's fontFamily parameter or GoogleFonts.xyzTextTheme() expect simple names.
263+
print('[AppBloc] _mapFontFamily: Input is $fontFamilyString, returning as is.');
264+
return fontFamilyString;
266265
}
267266

268267
// Map AppTextScaleFactor to AppTextScaleFactor (no change needed)

lib/app/bloc/app_state.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ class AppState extends Equatable {
2727
this.fontFamily,
2828
this.status = AppStatus.initial,
2929
this.user, // User is now nullable and defaults to null
30+
this.locale, // Added locale
3031
});
3132

3233
/// The index of the currently selected item in the bottom navigation bar.
@@ -54,6 +55,9 @@ class AppState extends Equatable {
5455
/// User-specific application settings.
5556
final UserAppSettings settings; // Add settings property
5657

58+
/// The current application locale.
59+
final Locale? locale; // Added locale
60+
5761
/// Creates a copy of the current state with updated values.
5862
AppState copyWith({
5963
int? selectedBottomNavigationIndex,
@@ -64,7 +68,9 @@ class AppState extends Equatable {
6468
AppStatus? status,
6569
User? user,
6670
UserAppSettings? settings, // Add settings to copyWith
71+
Locale? locale, // Added locale
6772
bool clearFontFamily = false,
73+
bool clearLocale = false, // Added to allow clearing locale
6874
}) {
6975
return AppState(
7076
selectedBottomNavigationIndex:
@@ -76,6 +82,7 @@ class AppState extends Equatable {
7682
status: status ?? this.status,
7783
user: user ?? this.user,
7884
settings: settings ?? this.settings, // Copy settings
85+
locale: clearLocale ? null : locale ?? this.locale, // Added locale
7986
);
8087
}
8188

@@ -89,5 +96,6 @@ class AppState extends Equatable {
8996
status,
9097
user,
9198
settings, // Include settings in props
99+
locale, // Added locale to props
92100
];
93101
}

lib/app/view/app.dart

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,9 +181,13 @@ class _AppViewState extends State<_AppView> {
181181
previous.themeMode != current.themeMode ||
182182
previous.flexScheme != current.flexScheme ||
183183
previous.fontFamily != current.fontFamily ||
184-
previous.appTextScaleFactor !=
185-
current.appTextScaleFactor, // Use text scale factor
184+
previous.appTextScaleFactor != current.appTextScaleFactor ||
185+
previous.locale != current.locale, // Added locale check
186186
builder: (context, state) {
187+
print('[_AppViewState] Building MaterialApp.router');
188+
print('[_AppViewState] state.fontFamily: ${state.fontFamily}');
189+
print('[_AppViewState] state.settings.displaySettings.fontFamily: ${state.settings.displaySettings.fontFamily}');
190+
print('[_AppViewState] state.settings.displaySettings.fontWeight: ${state.settings.displaySettings.fontWeight}');
187191
return MaterialApp.router(
188192
debugShowCheckedModeBanner: false,
189193
themeMode: state.themeMode,
@@ -192,15 +196,18 @@ class _AppViewState extends State<_AppView> {
192196
scheme: state.flexScheme,
193197
appTextScaleFactor:
194198
state.settings.displaySettings.textScaleFactor,
199+
appFontWeight: state.settings.displaySettings.fontWeight, // Added
195200
fontFamily: state.settings.displaySettings.fontFamily,
196201
),
197202
darkTheme: darkTheme(
198203
scheme: state.flexScheme,
199204
appTextScaleFactor:
200205
state.settings.displaySettings.textScaleFactor,
206+
appFontWeight: state.settings.displaySettings.fontWeight, // Added
201207
fontFamily: state.settings.displaySettings.fontFamily,
202208
),
203209
routerConfig: _router,
210+
locale: state.locale, // Use locale from AppBloc state
204211
localizationsDelegates: AppLocalizations.localizationsDelegates,
205212
supportedLocales: AppLocalizations.supportedLocales,
206213
);

lib/l10n/arb/app_ar.arb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,18 @@
516516
"@settingsAppearanceFontFamilySystemDefault": {
517517
"description": "Label for the system default font family option"
518518
},
519+
"settingsAppearanceThemeSubPageTitle": "إعدادات المظهر",
520+
"@settingsAppearanceThemeSubPageTitle": {
521+
"description": "Title for the theme settings sub-page under appearance"
522+
},
523+
"settingsAppearanceFontSubPageTitle": "إعدادات الخط",
524+
"@settingsAppearanceFontSubPageTitle": {
525+
"description": "Title for the font settings sub-page under appearance"
526+
},
527+
"settingsLanguageTitle": "اللغة",
528+
"@settingsLanguageTitle": {
529+
"description": "Title for the language settings page/section"
530+
},
519531
"emailCodeSentPageTitle": "أدخل الرمز",
520532
"@emailCodeSentPageTitle": {
521533
"description": "AppBar title for the email code verification page"

lib/l10n/arb/app_en.arb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,18 @@
516516
"@settingsAppearanceFontFamilySystemDefault": {
517517
"description": "Label for the system default font family option"
518518
},
519+
"settingsAppearanceThemeSubPageTitle": "Theme Settings",
520+
"@settingsAppearanceThemeSubPageTitle": {
521+
"description": "Title for the theme settings sub-page under appearance"
522+
},
523+
"settingsAppearanceFontSubPageTitle": "Font Settings",
524+
"@settingsAppearanceFontSubPageTitle": {
525+
"description": "Title for the font settings sub-page under appearance"
526+
},
527+
"settingsLanguageTitle": "Language",
528+
"@settingsLanguageTitle": {
529+
"description": "Title for the language settings page/section"
530+
},
519531
"emailCodeSentPageTitle": "Enter Code",
520532
"@emailCodeSentPageTitle": {
521533
"description": "AppBar title for the email code verification page"

lib/router/router.dart

Lines changed: 50 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,11 @@ import 'package:ht_main/router/routes.dart';
3838
import 'package:ht_main/settings/bloc/settings_bloc.dart'; // Added
3939
import 'package:ht_main/settings/view/appearance_settings_page.dart'; // Added
4040
import 'package:ht_main/settings/view/feed_settings_page.dart'; // Added
41+
import 'package:ht_main/settings/view/font_settings_page.dart'; // Added for new page
42+
import 'package:ht_main/settings/view/language_settings_page.dart'; // Added for new page
4143
import 'package:ht_main/settings/view/notification_settings_page.dart'; // Added
4244
import 'package:ht_main/settings/view/settings_page.dart'; // Added
45+
import 'package:ht_main/settings/view/theme_settings_page.dart'; // Added for new page
4346
import 'package:ht_shared/ht_shared.dart'; // Shared models, FromJson, ToJson, etc.
4447

4548
/// Creates and configures the GoRouter instance for the application.
@@ -526,22 +529,19 @@ GoRouter createRouter({
526529
name: Routes.accountName,
527530
builder: (context, state) => const AccountPage(),
528531
routes: [
529-
// Sub-route for settings
530-
GoRoute(
531-
path: Routes.settings, // Relative path 'settings'
532-
name: Routes.settingsName,
533-
builder: (context, state) {
534-
// Provide SettingsBloc here for SettingsPage and its children
535-
// Access AppBloc to get the current user ID
532+
// ShellRoute for settings to provide SettingsBloc to children
533+
ShellRoute(
534+
builder: (BuildContext context, GoRouterState state, Widget child) {
535+
// This builder provides SettingsBloc to all routes within this ShellRoute.
536+
// 'child' will be SettingsPage, AppearanceSettingsPage, etc.
536537
final appBloc = context.read<AppBloc>();
537538
final userId = appBloc.state.user?.id;
538539

539-
return BlocProvider(
540+
return BlocProvider<SettingsBloc>(
540541
create: (context) {
541542
final settingsBloc = SettingsBloc(
542543
userAppSettingsRepository:
543-
context
544-
.read<HtDataRepository<UserAppSettings>>(),
544+
context.read<HtDataRepository<UserAppSettings>>(),
545545
);
546546
// Only load settings if a userId is available
547547
if (userId != null) {
@@ -550,39 +550,55 @@ GoRouter createRouter({
550550
);
551551
} else {
552552
// Handle case where user is unexpectedly null.
553-
// This might involve logging or emitting an error state
554-
// directly in SettingsBloc if it's designed to handle it,
555-
// or simply not loading settings.
556-
// For now, we'll assume router redirects prevent this.
557553
print(
558-
'Warning: User ID is null when creating SettingsBloc. Settings will not be loaded.',
554+
'ShellRoute/SettingsBloc: User ID is null when creating SettingsBloc. Settings will not be loaded.',
559555
);
560556
}
561557
return settingsBloc;
562558
},
563-
child: const SettingsPage(), // Use the actual page
559+
child: child, // child is the actual page widget (SettingsPage, AppearanceSettingsPage, etc.)
564560
);
565561
},
566-
// --- Settings Sub-Routes ---
567562
routes: [
568563
GoRoute(
569-
path: Routes.settingsAppearance, // 'appearance'
570-
name: Routes.settingsAppearanceName,
571-
builder:
572-
(context, state) => const AppearanceSettingsPage(),
573-
// SettingsBloc is inherited from parent route
574-
),
575-
GoRoute(
576-
path: Routes.settingsFeed, // 'feed'
577-
name: Routes.settingsFeedName,
578-
builder: (context, state) => const FeedSettingsPage(),
579-
),
580-
GoRoute(
581-
path: Routes.settingsNotifications, // 'notifications'
582-
name: Routes.settingsNotificationsName,
583-
builder:
584-
(context, state) =>
585-
const NotificationSettingsPage(),
564+
path: Routes.settings, // Relative path 'settings' from /account
565+
name: Routes.settingsName,
566+
builder: (context, state) => const SettingsPage(),
567+
// --- Settings Sub-Routes ---
568+
routes: [
569+
GoRoute(
570+
path: Routes.settingsAppearance, // 'appearance' relative to /account/settings
571+
name: Routes.settingsAppearanceName,
572+
builder: (context, state) => const AppearanceSettingsPage(),
573+
routes: [ // Children of AppearanceSettingsPage
574+
GoRoute(
575+
path: Routes.settingsAppearanceTheme, // 'theme' relative to /account/settings/appearance
576+
name: Routes.settingsAppearanceThemeName,
577+
builder: (context, state) => const ThemeSettingsPage(),
578+
),
579+
GoRoute(
580+
path: Routes.settingsAppearanceFont, // 'font' relative to /account/settings/appearance
581+
name: Routes.settingsAppearanceFontName,
582+
builder: (context, state) => const FontSettingsPage(),
583+
),
584+
],
585+
),
586+
GoRoute(
587+
path: Routes.settingsFeed, // 'feed' relative to /account/settings
588+
name: Routes.settingsFeedName,
589+
builder: (context, state) => const FeedSettingsPage(),
590+
),
591+
GoRoute(
592+
path: Routes.settingsNotifications, // 'notifications' relative to /account/settings
593+
name: Routes.settingsNotificationsName,
594+
builder: (context, state) => const NotificationSettingsPage(),
595+
),
596+
GoRoute(
597+
path: Routes.settingsLanguage, // 'language' relative to /account/settings
598+
name: Routes.settingsLanguageName,
599+
builder: (context, state) => const LanguageSettingsPage(),
600+
),
601+
],
586602
),
587603
],
588604
),

lib/router/routes.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,24 @@ abstract final class Routes {
5555
// --- Settings Sub-Routes (relative to /account/settings) ---
5656
static const settingsAppearance = 'appearance';
5757
static const settingsAppearanceName = 'settingsAppearance';
58+
59+
// --- Appearance Sub-Routes (relative to /account/settings/appearance) ---
60+
static const settingsAppearanceTheme = 'theme'; // Path: /account/settings/appearance/theme
61+
static const settingsAppearanceThemeName = 'settingsAppearanceTheme';
62+
static const settingsAppearanceFont = 'font'; // Path: /account/settings/appearance/font
63+
static const settingsAppearanceFontName = 'settingsAppearanceFont';
64+
5865
static const settingsFeed = 'feed';
5966
static const settingsFeedName = 'settingsFeed';
6067
static const settingsArticle = 'article';
6168
static const settingsArticleName = 'settingsArticle';
6269
static const settingsNotifications = 'notifications';
6370
static const settingsNotificationsName = 'settingsNotifications';
71+
72+
// --- Language Settings Sub-Route (relative to /account/settings) ---
73+
static const settingsLanguage = 'language'; // Path: /account/settings/language
74+
static const settingsLanguageName = 'settingsLanguage';
75+
6476
// Add names for notification sub-selection routes if needed later
6577
// static const settingsNotificationCategories = 'categories';
6678
// static const settingsNotificationCategoriesName = 'settingsNotificationCategories';

lib/settings/bloc/settings_bloc.dart

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ class SettingsBloc extends Bloc<SettingsEvent, SettingsState> {
4848
_onFeedTileTypeChanged,
4949
transformer: sequential(),
5050
);
51+
on<SettingsLanguageChanged>(
52+
_onLanguageChanged,
53+
transformer: sequential(),
54+
);
5155
// SettingsNotificationsEnabledChanged event and handler removed.
5256
}
5357

@@ -156,12 +160,14 @@ class SettingsBloc extends Bloc<SettingsEvent, SettingsState> {
156160
Emitter<SettingsState> emit,
157161
) async {
158162
if (state.userAppSettings == null) return;
163+
print('[SettingsBloc] _onAppFontTypeChanged: Received event.fontType: ${event.fontType}');
159164

160165
final updatedSettings = state.userAppSettings!.copyWith(
161166
displaySettings: state.userAppSettings!.displaySettings.copyWith(
162167
fontFamily: event.fontType,
163168
),
164169
);
170+
print('[SettingsBloc] _onAppFontTypeChanged: Updated settings.fontFamily: ${updatedSettings.displaySettings.fontFamily}');
165171
emit(state.copyWith(userAppSettings: updatedSettings, clearError: true));
166172
await _persistSettings(updatedSettings, emit);
167173
}
@@ -171,12 +177,14 @@ class SettingsBloc extends Bloc<SettingsEvent, SettingsState> {
171177
Emitter<SettingsState> emit,
172178
) async {
173179
if (state.userAppSettings == null) return;
180+
print('[SettingsBloc] _onAppFontWeightChanged: Received event.fontWeight: ${event.fontWeight}');
174181

175182
final updatedSettings = state.userAppSettings!.copyWith(
176183
displaySettings: state.userAppSettings!.displaySettings.copyWith(
177184
fontWeight: event.fontWeight,
178185
),
179186
);
187+
print('[SettingsBloc] _onAppFontWeightChanged: Updated settings.fontWeight: ${updatedSettings.displaySettings.fontWeight}');
180188
emit(state.copyWith(userAppSettings: updatedSettings, clearError: true));
181189
await _persistSettings(updatedSettings, emit);
182190
}
@@ -195,4 +203,17 @@ class SettingsBloc extends Bloc<SettingsEvent, SettingsState> {
195203
emit(state.copyWith(userAppSettings: updatedSettings, clearError: true));
196204
await _persistSettings(updatedSettings, emit);
197205
}
206+
207+
Future<void> _onLanguageChanged(
208+
SettingsLanguageChanged event,
209+
Emitter<SettingsState> emit,
210+
) async {
211+
if (state.userAppSettings == null) return;
212+
213+
final updatedSettings = state.userAppSettings!.copyWith(
214+
language: event.languageCode,
215+
);
216+
emit(state.copyWith(userAppSettings: updatedSettings, clearError: true));
217+
await _persistSettings(updatedSettings, emit);
218+
}
198219
}

0 commit comments

Comments
 (0)