Skip to content

Commit 5ff70a0

Browse files
committed
refactor(app): improve AppConfig fetching logic
- Introduce background check option for AppConfig fetching - Optimize state transitions and error handling - Simplify event classes for better maintainability - Enhance logging for background fetch operations
1 parent ea3858f commit 5ff70a0

File tree

3 files changed

+79
-105
lines changed

3 files changed

+79
-105
lines changed

lib/app/bloc/app_bloc.dart

Lines changed: 46 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -411,28 +411,31 @@ class AppBloc extends Bloc<AppEvent, AppState> {
411411
return;
412412
}
413413

414-
print(
415-
'[AppBloc] Attempting to fetch AppConfig for user: ${state.user!.id}...',
416-
);
417-
emit(
418-
state.copyWith(
419-
status: AppStatus.configFetching,
420-
remoteConfig: null,
421-
clearAppConfig: true,
422-
),
423-
);
414+
// For background checks, we don't want to show a loading screen.
415+
// Only for the initial fetch should we set the status to configFetching.
416+
if (!event.isBackgroundCheck) {
417+
print(
418+
'[AppBloc] Initial config fetch. Setting status to configFetching.',
419+
);
420+
emit(
421+
state.copyWith(
422+
status: AppStatus.configFetching,
423+
),
424+
);
425+
} else {
426+
print('[AppBloc] Background config fetch. Proceeding silently.');
427+
}
424428

425429
try {
426430
final remoteConfig = await _appConfigRepository.read(id: kRemoteConfigId);
427431
print(
428432
'[AppBloc] Remote Config fetched successfully. ID: ${remoteConfig.id} for user: ${state.user!.id}',
429433
);
430434

431-
// --- CRITICAL STARTUP SEQUENCE EVALUATION ---
432-
// After successfully fetching the remote configuration, we must
433-
// evaluate the app's status in a specific order before allowing
434-
// the main UI to be built.
435-
435+
// --- CRITICAL STATUS EVALUATION ---
436+
// For both initial and background checks, if a critical status is found,
437+
// we must update the app status immediately to lock the UI.
438+
436439
// 1. Check for Maintenance Mode. This has the highest priority.
437440
if (remoteConfig.appStatus.isUnderMaintenance) {
438441
emit(
@@ -456,36 +459,43 @@ class AppBloc extends Bloc<AppEvent, AppState> {
456459
return;
457460
}
458461

459-
// 3. If no critical status is active, proceed to the normal app state.
460-
// The status is determined by the user's role (authenticated/anonymous).
461-
final finalStatus = state.user!.appRole == AppUserRole.standardUser
462-
? AppStatus.authenticated
463-
: AppStatus.anonymous;
464-
465-
emit(state.copyWith(remoteConfig: remoteConfig, status: finalStatus));
462+
// --- POST-CHECK STATE RESOLUTION ---
463+
// If no critical status was found, we resolve the final state.
464+
465+
// For an initial fetch, we transition from configFetching to the correct
466+
// authenticated/anonymous state.
467+
if (!event.isBackgroundCheck) {
468+
final finalStatus = state.user!.appRole == AppUserRole.standardUser
469+
? AppStatus.authenticated
470+
: AppStatus.anonymous;
471+
emit(state.copyWith(remoteConfig: remoteConfig, status: finalStatus));
472+
} else {
473+
// For a background check, the status is already correct (e.g., authenticated).
474+
// We just need to update the remoteConfig in the state silently.
475+
// The status does not need to change, preventing a disruptive UI rebuild.
476+
emit(state.copyWith(remoteConfig: remoteConfig));
477+
}
466478
} on HttpException catch (e) {
467479
print(
468480
'[AppBloc] Failed to fetch AppConfig (HttpException) for user ${state.user?.id}: ${e.runtimeType} - ${e.message}',
469481
);
470-
emit(
471-
state.copyWith(
472-
status: AppStatus.configFetchFailed,
473-
remoteConfig: null,
474-
clearAppConfig: true,
475-
),
476-
);
482+
// Only show a failure screen on an initial fetch.
483+
// For background checks, we fail silently to avoid disruption.
484+
if (!event.isBackgroundCheck) {
485+
emit(state.copyWith(status: AppStatus.configFetchFailed));
486+
} else {
487+
print('[AppBloc] Silent failure on background config fetch.');
488+
}
477489
} catch (e, s) {
478490
print(
479491
'[AppBloc] Unexpected error fetching AppConfig for user ${state.user?.id}: $e',
480492
);
481493
print('[AppBloc] Stacktrace: $s');
482-
emit(
483-
state.copyWith(
484-
status: AppStatus.configFetchFailed,
485-
remoteConfig: null,
486-
clearAppConfig: true,
487-
),
488-
);
494+
if (!event.isBackgroundCheck) {
495+
emit(state.copyWith(status: AppStatus.configFetchFailed));
496+
} else {
497+
print('[AppBloc] Silent failure on background config fetch.');
498+
}
489499
}
490500
}
491501

lib/app/bloc/app_event.dart

Lines changed: 27 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ abstract class AppEvent extends Equatable {
77
List<Object?> get props => [];
88
}
99

10+
/// Dispatched when the authentication state changes (e.g., user logs in/out).
1011
class AppUserChanged extends AppEvent {
1112
const AppUserChanged(this.user);
1213

@@ -16,122 +17,81 @@ class AppUserChanged extends AppEvent {
1617
List<Object?> get props => [user];
1718
}
1819

19-
/// {@template app_settings_refreshed}
20-
/// Internal event to trigger reloading of settings within AppBloc.
21-
/// Added when user changes or upon explicit request.
22-
/// {@endtemplate}
20+
/// Dispatched to request a refresh of the user's application settings.
2321
class AppSettingsRefreshed extends AppEvent {
24-
/// {@macro app_settings_refreshed}
2522
const AppSettingsRefreshed();
2623
}
2724

28-
/// {@template app_logout_requested}
29-
/// Event to request user logout.
30-
/// {@endtemplate}
25+
/// Dispatched to fetch the remote application configuration.
26+
class AppConfigFetchRequested extends AppEvent {
27+
const AppConfigFetchRequested({this.isBackgroundCheck = false});
28+
29+
/// Whether this fetch is a silent background check.
30+
///
31+
/// If `true`, the BLoC will not enter a visible loading state.
32+
/// If `false` (default), it's treated as an initial fetch that shows a
33+
/// loading UI.
34+
final bool isBackgroundCheck;
35+
36+
@override
37+
List<Object> get props => [isBackgroundCheck];
38+
}
39+
40+
/// Dispatched when the user logs out.
3141
class AppLogoutRequested extends AppEvent {
32-
/// {@macro app_logout_requested}
3342
const AppLogoutRequested();
3443
}
3544

36-
/// {@template app_theme_mode_changed}
37-
/// Event to change the application's theme mode.
38-
/// {@endtemplate}
45+
/// Dispatched when the theme mode (light/dark/system) changes.
3946
class AppThemeModeChanged extends AppEvent {
40-
/// {@macro app_theme_mode_changed}
4147
const AppThemeModeChanged(this.themeMode);
42-
4348
final ThemeMode themeMode;
44-
4549
@override
46-
List<Object?> get props => [themeMode];
50+
List<Object> get props => [themeMode];
4751
}
4852

49-
/// {@template app_flex_scheme_changed}
50-
/// Event to change the application's FlexColorScheme.
51-
/// {@endtemplate}
53+
/// Dispatched when the accent color theme changes.
5254
class AppFlexSchemeChanged extends AppEvent {
53-
/// {@macro app_flex_scheme_changed}
5455
const AppFlexSchemeChanged(this.flexScheme);
55-
5656
final FlexScheme flexScheme;
57-
5857
@override
59-
List<Object?> get props => [flexScheme];
58+
List<Object> get props => [flexScheme];
6059
}
6160

62-
/// {@template app_font_family_changed}
63-
/// Event to change the application's font family.
64-
/// {@endtemplate}
61+
/// Dispatched when the font family changes.
6562
class AppFontFamilyChanged extends AppEvent {
66-
/// {@macro app_font_family_changed}
6763
const AppFontFamilyChanged(this.fontFamily);
68-
6964
final String? fontFamily;
70-
7165
@override
7266
List<Object?> get props => [fontFamily];
7367
}
7468

75-
/// {@template app_text_scale_factor_changed}
76-
/// Event to change the application's text scale factor.
77-
/// {@endtemplate}
69+
/// Dispatched when the text scale factor changes.
7870
class AppTextScaleFactorChanged extends AppEvent {
79-
/// {@macro app_text_scale_factor_changed}
8071
const AppTextScaleFactorChanged(this.appTextScaleFactor);
81-
8272
final AppTextScaleFactor appTextScaleFactor;
83-
8473
@override
85-
List<Object?> get props => [appTextScaleFactor];
74+
List<Object> get props => [appTextScaleFactor];
8675
}
8776

88-
/// {@template app_font_weight_changed}
89-
/// Event to change the application's font weight.
90-
/// {@endtemplate}
77+
/// Dispatched when the font weight changes.
9178
class AppFontWeightChanged extends AppEvent {
92-
/// {@macro app_font_weight_changed}
9379
const AppFontWeightChanged(this.fontWeight);
94-
95-
/// The new font weight to apply.
9680
final AppFontWeight fontWeight;
97-
9881
@override
9982
List<Object> get props => [fontWeight];
10083
}
10184

102-
/// {@template app_config_fetch_requested}
103-
/// Event to trigger fetching of the global AppConfig.
104-
/// {@endtemplate}
105-
class AppConfigFetchRequested extends AppEvent {
106-
/// {@macro app_config_fetch_requested}
107-
const AppConfigFetchRequested();
108-
}
109-
110-
/// {@template app_opened}
111-
/// Event triggered when the application is opened.
112-
/// Used to check for required updates or maintenance mode.
113-
/// {@endtemplate}
114-
class AppOpened extends AppEvent {
115-
/// {@macro app_opened}
116-
const AppOpened();
117-
}
118-
119-
/// {@template app_user_account_action_shown}
120-
/// Event triggered when an AccountAction has been shown to the user,
121-
/// prompting an update to their `lastAccountActionShownAt` timestamp.
122-
/// {@endtemplate}
85+
/// Dispatched when a one-time user account action has been shown.
12386
class AppUserAccountActionShown extends AppEvent {
124-
/// {@macro app_user_account_action_shown}
12587
const AppUserAccountActionShown({
12688
required this.userId,
12789
required this.feedActionType,
12890
required this.isCompleted,
12991
});
130-
13192
final String userId;
13293
final FeedActionType feedActionType;
13394
final bool isCompleted;
134-
13595
@override
13696
List<Object> get props => [userId, feedActionType, isCompleted];
13797
}

lib/app/services/app_status_service.dart

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,9 @@ class AppStatusService with WidgetsBindingObserver {
7171
'[AppStatusService] Periodic check triggered. Requesting AppConfig fetch.',
7272
);
7373
// Add the event to the AppBloc to fetch the latest config.
74-
_context.read<AppBloc>().add(const AppConfigFetchRequested());
74+
_context
75+
.read<AppBloc>()
76+
.add(const AppConfigFetchRequested(isBackgroundCheck: true));
7577
});
7678
}
7779

@@ -96,7 +98,9 @@ class AppStatusService with WidgetsBindingObserver {
9698
// When the app comes to the foreground, immediately trigger a check.
9799
// This is crucial for catching maintenance mode that was enabled
98100
// while the app was in the background.
99-
_context.read<AppBloc>().add(const AppConfigFetchRequested());
101+
_context
102+
.read<AppBloc>()
103+
.add(const AppConfigFetchRequested(isBackgroundCheck: true));
100104
}
101105
}
102106

0 commit comments

Comments
 (0)