Skip to content

Conversation

@fulleni
Copy link
Member

@fulleni fulleni commented Oct 18, 2025

Status

IN DEVELOPMENT

Description

This pull request significantly refactors the application's core startup and authentication mechanisms to enhance robustness and predictability. By introducing a dedicated initialization service and streamlining the AppBloc architecture, the app's lifecycle, from launch to user login/logout and data loading, is now more stable and less prone to race conditions. This results in a more reliable and consistent user experience across all environments, with improved error handling and a cleaner separation of concerns within the codebase.

Type of Change

  • ✨ New feature (non-breaking change which adds functionality)
  • 🛠️ Bug fix (non-breaking change which fixes an issue)
  • ❌ Breaking change (fix or feature that would cause existing functionality to change)
  • 🧹 Code refactor
  • ✅ Build configuration change
  • 📝 Documentation
  • 🗑️ Chore

- Changed the appEnvironment constant from AppEnvironment.demo to AppEnvironment.development in the main.dart file.
- This modification allows the application to run in development mode, which typically includes more verbose logging and debugging features.
- Add a new section for upcoming releases in the CHANGELOG.md file
Creates the foundational `AppInitializer` service and its related `InitializationResult` sealed classes.

This establishes a new, isolated "locked box" for orchestrating the entire application startup sequence. The `AppInitializer` class will centralize all startup logic, eliminating race conditions and making the process more robust, testable, and maintainable.

This commit only creates the file structure and class shells; the implementation of the initialization logic will follow in subsequent steps.
Implements the main `initializeApp` method within the new `AppInitializer` service.

This method now contains the entire sequential startup flow:
1.  Fetches `RemoteConfig`.
2.  Checks for maintenance mode and forced updates.
3.  Fetches the initial user.
4.  If a user exists, fetches `UserAppSettings` and `UserContentPreferences` in parallel for improved performance.
5.  Handles demo-specific data initialization.
6.  Returns a single, definitive `InitializationResult` (`Success` or `Failure`).

This centralizes the critical startup logic, eliminates race conditions, and adds extensive logging for maintainability, creating a robust "glass box" for app initialization.
Implements the main initializeApp method within the new AppInitializer service.

This method now contains the entire sequential startup flow:

Fetches RemoteConfig.

Checks for maintenance mode and forced updates.

Fetches the initial user.

If a user exists, fetches UserAppSettings and UserContentPreferences in parallel for improved performance.

Handles demo-specific data initialization.
… initialization

- Introduce AppInitializer service to handle initializations in a centralized manner
- Remove individual initialization steps from bootstrap function
- Update logger statements for clarity and consistency
- Add packageInfoService and logger dependencies where necessary
Refactors the `App` widget to accept a single `InitializationResult` object in its constructor. This change significantly simplifies the widget's API and removes the long list of individual initial data parameters (`initialUser`, `initialRemoteConfig`, etc.).

The `App` widget now passes this result directly to the `AppBloc`, which has become the sole consumer of the pre-loaded initialization data. This aligns with the new "locked box" architecture, where `AppInitializer` handles all startup logic and `AppBloc` manages the resulting state.
This is a major refactoring of the `AppBloc`.

- The `AppBloc` constructor is completely rewritten. It no longer accepts a long list of repositories and initial data. Instead, it takes a single `InitializationResult` object from the `AppInitializer`.
- The initial state of the BLoC is now determined synchronously from this result, eliminating all asynchronous logic and potential race conditions from the constructor.
- The `_onAppStarted` event handler is now a no-op, as all its logic has been moved to the `AppInitializer`.
- The `_onAppUserChanged` handler is significantly improved. It now correctly detects role changes (anonymous to authenticated) and delegates the complex data migration and re-fetching logic to the `AppInitializer.handleUserTransition` method.
- All direct repository dependencies have been removed from the BLoC. It now accesses repositories when needed via `context.read<T>()` using the `navigatorKey`.

This change makes the `AppBloc` a much simpler and more robust pure state manager, solidifying the new "locked box" architecture.
Removes all now-unused repository and service parameters from the `App` and `_AppView` widgets.

The `App` widget's responsibility is now strictly limited to providing the repositories that are needed by the `GoRouter` and feature BLoCs. All initialization-related data is now passed exclusively through the `InitializationResult` object to the `AppBloc`.
Removes the redundant `environment` property from `AppState`. The environment is a constant determined at bootstrap and does not need to be part of the dynamic application state.
Removes dead code from `AppBloc` by deleting the now-obsolete `_onAppPeriodicConfigFetchRequested` and `_onAppVersionCheckRequested` event handlers.

Critically, this change also corrects the logic in `_onAppUserChanged` to properly handle user state transitions. It now correctly identifies a logout (`newUser == null`) versus a login or role change (`newUser != null`), ensuring the data migration and re-fetching flow is triggered only when a user is present.
This commit addresses several issues in `AppBloc` that arose from the recent refactoring:

- **Restores Event Handlers:** Re-registers the `AppUserFeedDecoratorShown` and `AppUserContentPreferencesChanged` event handlers, which were incorrectly removed.
- **Fixes Nullability Errors:** Corrects null-safety errors in `_onAppUserChanged` and `_onAppPeriodicConfigFetchRequested` to ensure the logic for user transitions and periodic updates is robust.
- **Removes Dead Code:** Deletes the unnecessary import of `package:bloc/bloc.dart`.

The `AppBloc` is now correctly wired to handle all its responsibilities within the new architecture.
This commit addresses several issues in `AppBloc` that arose from the recent refactoring:

- **Restores Event Handlers:** Re-registers the `AppUserFeedDecoratorShown` and `AppUserContentPreferencesChanged` event handlers, which were incorrectly removed.
- **Fixes Nullability Errors:** Corrects null-safety errors in `_onAppUserChanged` and `_onAppPeriodicConfigFetchRequested` to ensure the logic for user transitions and periodic updates is robust.
- **Removes Dead Code:** Deletes the unnecessary import of `package:bloc/bloc.dart`.

The `AppBloc` is now correctly wired to handle all its responsibilities within the new architecture.
Removes the now-obsolete `AppVersionCheckRequested` event, as this logic is handled by the `AppInitializer`.

Also simplifies the `AppStarted` event by removing the unused `initialUser` parameter, aligning it with the new initialization flow where the `AppBloc` receives all initial data via its constructor.
Removes the initial `HeadlinesFeedFetchRequested` event dispatch from the `initState` method of `HeadlinesFeedPage`.

This responsibility is being moved directly into the `HeadlinesFeedBloc`, which will now listen to the `AppBloc` to determine the correct moment to fetch data. This change eliminates a potential race condition and further decouples the UI from business logic orchestration.
This is a critical refactoring that moves the initial data fetch logic for the headlines feed from the UI layer (`HeadlinesFeedPage`) into the `HeadlinesFeedBloc` itself.

The BLoC now listens to the `AppBloc`'s state stream. It waits for the `AppBloc` to signal a stable, "running" status (`authenticated` or `anonymous`) before dispatching its own `HeadlinesFeedRefreshRequested` event.

This change fixes a major race condition where the feed could attempt to load before its dependencies (like remote config or user settings) were available, leading to indefinite loading spinners. The feed's lifecycle is now correctly orchestrated by the application's global state.
Adds a more detailed comment to the `_onAuthenticationVerifyCodeRequested` handler in `AuthenticationBloc`.

This comment explicitly states that upon successful code verification, the global `AppBloc` is responsible for handling the entire post-login sequence (data fetching, navigation, etc.) via its listener on the `authStateChanges` stream. This reinforces the new, centralized lifecycle management architecture.
Adds a more detailed comment to the `_onAuthenticationVerifyCodeRequested` handler in `AuthenticationBloc`.

This comment explicitly states that upon successful code verification, the global `AppBloc` is responsible for handling the entire post-login sequence (data fetching, navigation, etc.) via its listener on the `authStateChanges` stream. This reinforces the new, centralized lifecycle management architecture.
Fixes a bug where the "Create an Account" call-to-action button in the feed decorator was pointing to a malformed URL (`/authentication//account-linking`).

The `ctaUrl` is now correctly set to `Routes.accountLinking`, ensuring that tapping the button navigates the user directly to the correct account linking page.
- Robust and race-condition-free implementation
- Fixes indefinite loading issues
- Improves data migration processes
- Enhances authentication flows
Overhauls the "Clean & Modern Architecture" section of the README to more accurately and impressively describe the application's structure.

- Replaces low-level details with high-level architectural pillars.
- Highlights the Multi-Layered Architecture (Data, Repository, BLoC).
- Emphasizes the robust `AppInitializer` for race-condition-free startup.
- Better articulates the use of BLoC, GoRouter, and Dependency Injection.
- Add new entry for upcoming release
- Document major refactor of app startup and authentication lifecycle
- Highlight fixes for indefinite loading, data migration, and authentication flows
Updates the "Clean & Modern Architecture" section to describe the layers in terms of their function (e.g., "Data Layer (handling raw data retrieval)") rather than using project-specific internal class names like `DataClient`. This makes the description more professional and universally understandable.
Fixes a build-time error in `HeadlinesFeedBloc` where calls to `darkTheme` and `lightTheme` were missing the required `appTextScaleFactor` and `appFontWeight` arguments.

The bloc now correctly passes these values from the `AppBloc`'s state, ensuring that the theme used for generating the `AdThemeStyle` is correctly configured.
Fixes several build-time errors in `AppInitializer`:
- Removes the redundant `abstract` keyword from the `sealed class InitializationResult`.
- Explicitly sets the generic type for `Future.wait` to `<dynamic>` to resolve type inference issues when destructuring results.
- Removes the unused `_userRepository` field.
- Corrects a call to `AppLocalizationsX.of(context)` to correctly retrieve localization strings.
Fixes several build-time errors in `AppInitializer`:
- Removes the redundant `HttpExceptionX` extension, as this functionality is already provided by the `ui_kit` package.
- Removes the unused `_userRepository` field and its constructor initialization.
- Adds explicit type casts to the results of `Future.wait` to resolve `dynamic` type assignment errors when destructuring.
Removes the `DataRepository<User>` dependency from the `AppInitializer` service. This repository is only used for updating the user model, a responsibility that correctly resides within the `AppBloc`, not during the initial app startup sequence.
Fixes a build-time error in `app.dart` where the call to `createRouter` was missing several required repository arguments.

This change temporarily passes the required repositories from the `BuildContext` to the `createRouter` function. This is an intermediate step to resolve the immediate build error. A subsequent refactoring of the router and its nested BLoC providers will eliminate the need to pass these repositories as arguments, allowing them to be read from the context directly where needed.
Refactors `createRouter` to no longer accept repository instances as direct arguments. This is a crucial step in finalizing the new dependency injection architecture.

- The function signature for `createRouter` is now significantly simpler.
- BLoCs created within the router (e.g., `SettingsBloc`, `HeadlinesFeedBloc`, `EntityDetailsBloc`) now correctly fetch their dependencies from the `BuildContext` using `context.read<T>()`.

This change ensures that repositories are provided once at the top of the widget tree and consumed where needed, adhering to a clean DI pattern.
Modified the GoRouter configuration for the `feedFilter` route.

- The route builder now expects a `HeadlineFilter` object to be passed in `state.extra`.
- This object is extracted and passed to the `HeadlinesFilterPage` constructor.
- This change completes the decoupling of the filter page from the feed page's context, resolving the `BlocProvider.of()` error by providing dependencies explicitly.
Passes the `HeadlinesFeedBloc` instance from the `HeadlinesFeedPage` to the `HeadlinesFilterPage` via `GoRouter`'s `extra` parameter.

This is the first step in fixing the `ProviderNotFoundException` that occurs when the filter page tries to access the bloc, as it exists in a separate navigation route.
Updates the `feedFilter` route to accept a map in its `extra` parameter. This map contains both the `initialFilter` and the `HeadlinesFeedBloc` instance.

The route now wraps `HeadlinesFilterPage` in a `BlocProvider.value`, making the `HeadlinesFeedBloc` directly available to the filter page. This is a key step in resolving the `ProviderNotFoundException` by ensuring the filter page has access to the bloc it needs to update, regardless of its position in the navigation stack.
Refactors the `HeadlinesFilterPage` to correctly handle asynchronous dialog interactions and to properly access the `HeadlinesFeedBloc`.

- The `_saveAndApplyFilter` method now `await`s the result of `showDialog`, ensuring the page is only popped *after* the save dialog is dismissed. This fixes a bug where the page would close prematurely.
- The `_applyFilter` method now correctly reads the `HeadlinesFeedBloc` from the context provided by the router's `BlocProvider.value`, eliminating the `ProviderNotFoundException`.
- All related methods (`_applyAndExit`, `_showApplyOptionsDialog`) have been updated to use the new synchronous navigation flow, removing unnecessary `BuildContext` passing.
Updates the `HeadlinesFeedBloc` constructor to accept an optional `UserContentPreferences` object. This object is used to "seed" the bloc's initial state with the user's saved filters.

This change is the first step in fixing a state synchronization bug where saved filters would not appear on the feed page after navigating away and back. By providing the initial state directly, we eliminate the dependency on a reactive stream update for the initial data load.
Updates the router to read the `userContentPreferences` from the `AppBloc` at the time of `HeadlinesFeedBloc` creation. This initial state is then passed directly into the bloc's constructor.

This change ensures that the `HeadlinesFeedBloc` is immediately aware of the user's saved filters, fixing a bug where the filters would disappear upon navigating back to the feed page.
Introduces a new `ConfirmationDialog` widget in the shared widgets directory. This generic dialog can be used across the application to prompt users for confirmation before performing a destructive or important action.

It returns a boolean value (`true` for confirm, `false` for cancel) via `Navigator.pop`, allowing callers to `await` the user's choice.
Adds new localization strings in English and Arabic for the title, content, and confirm button of the new reusable confirmation dialog.
Implements a confirmation dialog on the Saved Filters page before deleting a filter, improving user experience.

This change also fixes a UI bug where a red screen would flicker upon deletion. By `await`ing the result of the new asynchronous dialog, we ensure the `PopupMenuButton`'s route is fully closed before the `AppBloc` state changes and the list rebuilds, thus preventing the "unsafe ancestor" error.
Refactors the `HeadlinesFeedPage` to render the empty state message within the `CustomScrollView` using a `SliverFillRemaining` widget.

This ensures that the `FeedSliverAppBar` and `SavedFiltersBar` remain visible and interactive even when a filter returns no results. This provides a more consistent user experience and allows users to easily modify or clear their filters without being forced into a UI dead end.
Refactors the `AppBloc` and `AppState` to correctly handle user logout.

Previously, on logout, the `AppBloc`'s state retained the old user object. This caused a bug where signing back in with the same user ID would be ignored by the `_onAppUserChanged` handler, as it appeared no state change had occurred. This left the user stuck on the authentication screen.

This fix introduces a `clearUser` flag to `AppState.copyWith`, ensuring that on logout, the `user`, `settings`, and `userContentPreferences` are explicitly set to `null`. This guarantees a clean state and allows the app to correctly process a subsequent login by the same user.
…rvice

Extends the `ContentLimitationService` to enforce limits on saving new filters.

This change adds a `saveFilter` case to the `ContentAction` enum and implements the corresponding logic within the `checkAction` method. It reads the user's role and the number of currently saved filters, comparing it against the role-specific limit defined in the remote configuration (`guestSavedFiltersLimit`, `authenticatedSavedFiltersLimit`, `premiumSavedFiltersLimit`).

This centralizes the business rule for filter limits, ensuring it can be applied consistently across the app.
Integrates the `ContentLimitationService` into the `HeadlinesFilterPage` to enforce limits on saving new filters.

When a user attempts to "Apply & Save" a filter, the app now checks if they have reached their limit. If the limit is met, a `ContentLimitationBottomSheet` is displayed to inform the user, and the save process is halted. This provides immediate, clear feedback and aligns the filter-saving feature with the app's overall content limitation strategy.
Fixes a critical bug in the demo environment where authenticating as a new user (anonymous or via email) would fail.

Previously, when a new user was created, the `AppInitializer` would immediately try to fetch their settings and preferences. In the demo environment, this data did not exist yet, causing a `NotFoundException` that would halt the login process and leave the UI unresponsive.

This fix modifies `AppInitializer.handleUserTransition` to explicitly call `DemoDataInitializerService` for new users in the demo environment. This ensures that the necessary user data is created on-the-fly *before* any attempt is made to read it, resolving the race condition and allowing the authentication flow to complete successfully.
- Change 'headlineSearchEnterQuery' to 'headlineSearchInitialHeadline'
- Change 'headlineSearchEnterQuerySubheadline' to 'headlineSearchInitialSubheadline'
- Add 'radius' parameter to UserAvatar constructor
- Update CircleAvatar to use customizable radius
- Set default radius value to AppSpacing.lg
- Add sign-in/sign-out buttons to AppBar for anonymous and authenticated users
- Replace custom avatar implementation with shared UserAvatar widget
- Style user role display as a "capsule" for better visual appeal
- Remove separate sign-in and sign-out button implementations
- Update localized strings for guest user messages
@fulleni
Copy link
Member Author

fulleni commented Oct 20, 2025

/gemini review

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This is an excellent and substantial refactoring of the application's startup and authentication lifecycle. The introduction of the AppInitializationPage and AppInitializer service as a "gatekeeper" is a fantastic architectural improvement that effectively eliminates race conditions and makes the startup process much more robust and predictable. The AppBloc is now significantly cleaner and adheres better to the Single Responsibility Principle. The improved environment configuration using --dart-define and the fixes to UI race conditions (like in the filter page) are also great additions. I've found a couple of minor issues, but overall, this is a very high-quality contribution that greatly improves the maintainability and stability of the app.

…FeedPageState

- Move super.build(context) to the beginning of the build method
- Remove redundant super.build calls elsewhere in the method
- Add comments explaining the importance of calling super.build first
@fulleni
Copy link
Member Author

fulleni commented Oct 20, 2025

/gemini summary

@fulleni fulleni merged commit b876d6c into main Oct 20, 2025
0 of 2 checks passed
@fulleni fulleni deleted the fix/auth-bugs-in-dev-and-prod-env branch October 20, 2025 11:52
@gemini-code-assist
Copy link

Summary of Changes

This pull request significantly refactors the application's core startup and authentication mechanisms to enhance robustness and predictability. By introducing a dedicated initialization service and streamlining the AppBloc architecture, the app's lifecycle, from launch to user login/logout and data loading, is now more stable and less prone to race conditions. This results in a more reliable and consistent user experience across all environments, with improved error handling and a cleaner separation of concerns within the codebase.

Highlights

  • Overhauled App Startup and Authentication Lifecycle: The application's startup and authentication processes have been completely refactored to be more robust and free of race conditions. This addresses multiple bugs related to indefinite loading states, data migration, and authentication flows, particularly in development and production environments.
  • New App Initialization Architecture: Introduced a dedicated AppInitializationBloc and AppInitializer service to centralize and orchestrate the entire application startup sequence. This ensures critical data like remote configuration, user settings, and preferences are fetched and validated in a sequential, race-condition-free manner before the main UI is rendered.
  • Refactored AppBloc and AppState: The AppBloc has been significantly simplified, delegating complex initialization and user transition logic to the new AppInitializer service. The AppState now uses a single error field and has removed redundant error states, making it cleaner and more predictable. The AppLifeCycleStatus enum has also been moved to its own dedicated file for better organization.
  • Improved Error Handling and Recovery: Implemented a new AppHotRestartWrapper at the root of the widget tree, allowing for a full application restart on critical errors. Dedicated full-screen status pages (MaintenancePage, UpdateRequiredPage, CriticalErrorPage) are now rendered outside the main app router, ensuring stability and proper theming even during critical states.
  • Enhanced GoRouter Configuration: The GoRouter setup has been streamlined, with its redirect logic now solely focused on authentication-based route protection, as global app status checks are handled earlier in the initialization flow. Dependencies for BlocProviders within routes are now resolved directly from the context, improving modularity and fixing ProviderNotFoundException issues.
  • Ad Manager and Feed Bloc Updates: The InterstitialAdManager now directly receives the GlobalKey<NavigatorState>, improving its ability to obtain BuildContext. The HeadlinesFeedBloc now intelligently waits for a stable 'running' state before initiating its first data fetch, preventing race conditions during initial UI rendering, and uses AutomaticKeepAliveClientMixin to preserve state across tab switches.
  • Dynamic Environment Configuration: The application environment (production, development, demo) is now determined at compile-time using dart-define flags, preventing hardcoding and ensuring correct configurations for different build targets.
  • UI/UX Refinements: The Account Page has been updated with a more consistent sign-in/out button placement in the AppBar and uses a standardized UserAvatar widget. A new ConfirmationDialog has been introduced for delete actions, and the Headlines Feed Page now displays empty filter states more gracefully within the scroll view.
Changelog
  • .vscode/launch.json
    • Added distinct launch configurations for demo, development, and production environments, utilizing dart-define arguments for environment-specific settings.
  • CHANGELOG.md
    • Added an entry for the upcoming release, detailing the overhaul of application startup and authentication lifecycle, including the introduction of AppInitializationPage and AppInitializationBloc.
  • README.md
    • Updated the 'Architecture & Technical Excellence' section to reflect the new multi-layered architecture, robust startup and lifecycle management, and advanced state management with BLoC.
  • android/.gitignore
    • Added .vscode/ to the gitignore file to prevent IDE-specific configuration files from being committed.
  • lib/account/view/account_page.dart
    • Refactored sign-in/out buttons to be conditionally displayed in the AppBar, removing them from the main content card.
    • Updated guest user display text to use new localization keys (accountGuestUserHeadline, accountGuestUserSubheadline).
    • Replaced custom avatar container with the new UserAvatar widget for consistency.
  • lib/account/view/followed_contents/countries/followed_countries_list_page.dart
    • Removed user from BlocBuilder context.
    • Updated error handling to use the generic appState.error instead of initialUserPreferencesError.
    • Modified retry logic to dispatch AppUserContentPreferencesRefreshed event.
  • lib/account/view/followed_contents/sources/followed_sources_list_page.dart
    • Removed user from BlocBuilder context.
    • Updated error handling to use the generic appState.error instead of initialUserPreferencesError.
    • Modified retry logic to dispatch AppUserContentPreferencesRefreshed event.
  • lib/account/view/followed_contents/topics/followed_topics_list_page.dart
    • Removed user from BlocBuilder context.
    • Updated error handling to use the generic appState.error instead of initialUserPreferencesError.
    • Modified retry logic to dispatch AppUserContentPreferencesRefreshed event.
  • lib/account/view/saved_filters_page.dart
    • Introduced ConfirmationDialog for deleting saved filters, ensuring user confirmation before deletion.
    • Made the onSelected callback for PopupMenuButton asynchronous to handle dialog results.
  • lib/account/view/saved_headlines_page.dart
    • Removed user from BlocBuilder context.
    • Updated error handling to use the generic appState.error instead of initialUserPreferencesError.
    • Modified retry logic to dispatch AppUserContentPreferencesRefreshed event.
  • lib/ads/interstitial_ad_manager.dart
    • Added GlobalKey<NavigatorState> to the constructor to directly access BuildContext for local and demo ads, decoupling it from AppBloc's navigator key.
  • lib/app/bloc/app_bloc.dart
    • Refactored constructor to receive pre-fetched data (user, remoteConfig, settings, userContentPreferences) and AppInitializer.
    • Removed internal _userSubscription and related logic, now managed by _AppView.
    • Simplified _onAppStarted to be a no-op, as initialization is handled by AppInitializationBloc.
    • Re-architected _onAppUserChanged to delegate user transition logic to AppInitializer.handleUserTransition and process InitializationResult.
    • Removed _fetchAndSetUserSettings and _fetchAndSetUserContentPreferences methods, as their logic is now in AppInitializer.
    • Removed AppVersionCheckRequested event handling.
    • Updated _onAppPeriodicConfigFetchRequested to focus solely on maintenance mode checks and remote config updates.
    • Implemented optimistic UI updates with rollback for AppSettingsChanged and AppUserContentPreferencesChanged events.
  • lib/app/bloc/app_event.dart
    • Removed initialUser parameter from AppStarted event.
    • Removed AppVersionCheckRequested event.
  • lib/app/bloc/app_initialization_bloc.dart
    • Added new BLoC to manage the application's startup and initialization process, orchestrating AppInitializer.
  • lib/app/bloc/app_initialization_event.dart
    • Added new events (AppInitializationStarted, AppInitializationRetried) for the AppInitializationBloc.
  • lib/app/bloc/app_initialization_state.dart
    • Added new states (AppInitializationInProgress, AppInitializationSucceeded, AppInitializationFailed) for the AppInitializationBloc.
  • lib/app/bloc/app_state.dart
    • Removed environment, initialRemoteConfigError, and initialUserPreferencesError fields.
    • Introduced a single error field for general error handling.
    • Added latestAppVersion field for update checks.
    • Modified copyWith method to include clearUser and clearError flags for explicit state clearing.
    • Moved AppLifeCycleStatus enum to its own file.
  • lib/app/config/app_config.dart
    • Updated factory constructors (production, development, demo) to use String.fromEnvironment for BASE_URL and APP_ENVIRONMENT.
    • Added a critical check in the production factory to ensure BASE_URL is provided at compile-time.
  • lib/app/models/app_life_cycle_status.dart
    • Moved the AppLifeCycleStatus enum to this new dedicated file for better organization.
  • lib/app/models/initialization_result.dart
    • Added new sealed class InitializationResult with InitializationSuccess and InitializationFailure subclasses to encapsulate app startup outcomes.
  • lib/app/services/app_initializer.dart
    • Added new service to centralize and orchestrate the entire application startup sequence, including remote config fetching, version checks, user data loading, and demo data initialization/migration.
  • lib/app/services/package_info_service.dart
    • Added // ignore_for_file: one_member_abstracts to suppress lint warning.
  • lib/app/view/app.dart
    • Refactored App widget constructor to receive pre-fetched data (user, remoteConfig, settings, userContentPreferences) from the initialization phase.
    • Moved _userSubscription, _interstitialAdManager, and _contentLimitationService lifecycle management into _AppViewState's initState and dispose methods.
    • The _AppView's build method now conditionally renders full-screen status pages (maintenance, update required) based on AppBloc state, and wraps the main MaterialApp.router with a MultiRepositoryProvider for InterstitialAdManager and ContentLimitationService.
  • lib/app/view/app_hot_restart_wrapper.dart
    • Added new widget to wrap the root of the application, providing a mechanism for a full 'hot restart' on critical errors.
  • lib/app/view/app_initialization_page.dart
    • Added new top-level widget responsible for orchestrating the AppInitializationBloc and displaying initial loading, maintenance, update, or critical error screens.
  • lib/authentication/bloc/authentication_bloc.dart
    • Updated comments in _onEmailCodeVerified and _onAnonymousSignInRequested to clarify that the global AppBloc listener handles post-login sequence, not the AuthenticationBloc itself.
  • lib/authentication/view/email_code_verification_page.dart
    • Changed how AppEnvironment is accessed, now using context.read<AppEnvironment>() instead of BlocSelector.
  • lib/bootstrap.dart
    • Removed direct fetching of initial remote config and user data.
    • Introduced and initialized AppInitializer service.
    • The bootstrap function now returns AppHotRestartWrapper wrapping AppInitializationPage, deferring the main app initialization to this new page.
  • lib/headline-details/view/headline_details_page.dart
    • Updated error handling in BlocListener to check for the generic appState.error.
  • lib/headlines-feed/bloc/headlines_feed_bloc.dart
    • Updated constructor comment to highlight the use of initialUserContentPreferences to prevent race conditions.
    • Modified _appBlocSubscription to specifically handle updates to user preferences.
    • Added HeadlinesFeedStarted event and handler to explicitly trigger the initial feed load.
  • lib/headlines-feed/bloc/headlines_feed_event.dart
    • Added new event HeadlinesFeedStarted to explicitly initiate the feed loading process.
  • lib/headlines-feed/view/headlines_feed_page.dart
    • Implemented AutomaticKeepAliveClientMixin and wantKeepAlive to preserve feed state across bottom navigation tab switches.
    • Moved initial data fetch from WidgetsBinding.instance.addPostFrameCallback to didChangeDependencies, dispatching HeadlinesFeedStarted.
    • Ensured super.build(context) is the first statement in the build method.
    • Refactored empty state message to be conditionally rendered within the CustomScrollView using SliverFillRemaining.
  • lib/headlines-feed/view/headlines_filter_page.dart
    • Modified HeadlinesFilterPage constructor to accept initialFilter as a required parameter.
    • Updated _buildFilterTile to accept AppLocalizations (l10n) as a parameter.
    • Refactored helper methods (_showApplyOptionsDialog, _saveAndApplyFilter, _applyAndExit, _applyFilter) to accept BuildContext as a parameter.
    • Integrated ContentLimitationService check into _saveAndApplyFilter to enforce limits on saving filters.
    • Updated _HeadlinesFilterView to accept initialFilter and use it in retry logic.
  • lib/headlines-feed/widgets/saved_filters_bar.dart
    • Modified the 'Open Filter' button's onPressed callback to pass the current initialFilter and HeadlinesFeedBloc instance via extra to the feedFilterName route, resolving ProviderNotFoundException.
  • lib/headlines-search/view/headline_search_delegate.dart
    • Updated localization keys for initial search headline and subheadline text.
  • lib/l10n/app_localizations.dart
    • Added new localization keys: deleteConfirmationDialogTitle, deleteConfirmationDialogContent, deleteConfirmationDialogConfirmButton, headlineSearchInitialHeadline, headlineSearchInitialSubheadline, accountGuestUserHeadline, accountGuestUserSubheadline.
  • lib/l10n/app_localizations_ar.dart
    • Added Arabic translations for new localization keys.
  • lib/l10n/app_localizations_en.dart
    • Added English translations for new localization keys.
  • lib/l10n/arb/app_ar.arb
    • Added new ARB entries for Arabic translations.
  • lib/l10n/arb/app_en.arb
    • Added new ARB entries for English translations.
  • lib/main.dart
    • Updated appEnvironment to dynamically determine the environment (production, demo, development) using String.fromEnvironment compile-time variables.
    • Wrapped the root appWidget with AppHotRestartWrapper to enable full application restarts.
  • lib/router/router.dart
    • Simplified createRouter function signature, removing direct repository dependencies and relying on context.read.
    • Streamlined redirect logic to focus purely on authentication status, as global app status checks are now handled by AppInitializationPage.
    • Instantiated FeedDecoratorService locally within EntityDetailsPage and HeadlinesFeedBloc create methods.
    • Moved BlocProvider for HeadlinesFeedBloc directly into the GoRoute for /feed, passing initialUserContentPreferences.
    • Modified feedFilterName route to receive initialFilter and headlinesFeedBloc via extra and provide headlinesFeedBloc using BlocProvider.value.
  • lib/shared/extensions/app_status_x.dart
    • Added new extension to AppLifeCycleStatus for checking if the app is in a 'running' state.
  • lib/shared/extensions/app_user_x.dart
    • Added new extension to User for checking if a user is a guest.
  • lib/shared/extensions/extensions.dart
    • Exported new extension files (app_status_x.dart, app_user_x.dart, user_feed_decorator_status_x.dart).
  • lib/shared/extensions/user_feed_decorator_status_x.dart
    • Added new extension to UserFeedDecoratorStatus for determining if a decorator can be shown based on completion and cooldown.
  • lib/shared/services/content_limitation_service.dart
    • Added saveFilter to the ContentAction enum and implemented logic to check limits for saving filters based on user role.
  • lib/shared/services/feed_decorator_service.dart
    • Updated the ctaUrl for FeedDecoratorType.getStarted to Routes.accountLinking.
  • lib/shared/widgets/confirmation_dialog.dart
    • Added new reusable ConfirmationDialog widget for generic action confirmations.
  • lib/shared/widgets/user_avatar.dart
    • Added an optional radius parameter to the UserAvatar widget for custom sizing.
  • lib/shared/widgets/widgets.dart
    • Exported new widgets: confirmation_dialog.dart and user_avatar.dart.
  • lib/status/view/critical_error_page.dart
    • Made the exception parameter nullable and provided a default UnknownException message if no specific exception is provided.
  • lib/status/view/update_required_page.dart
    • Made iosUpdateUrl and androidUpdateUrl nullable and added null-aware checks when parsing URIs and displaying error messages.
Activity
  • The pull request was opened by fulleni with the title "Fix/auth bugs in dev and prod env" and a description outlining a major refactoring of startup and authentication to eliminate race conditions and improve robustness.
  • A summary of changes was provided by gemini-code-assist[bot], highlighting the overhaul of app startup, new initialization architecture, refactored AppBloc, improved error handling, enhanced GoRouter configuration, and updates to the Ad Manager and Feed Bloc.
  • A critical review comment from gemini-code-assist[bot] identified a memory leak in _AppViewState due to MultiRepositoryProvider being recreated in the build method, suggesting to manage service lifecycles in initState and dispose.
  • A medium priority review comment from gemini-code-assist[bot] suggested moving extension methods from AppInitializer to their own dedicated files for better separation of concerns.
  • A medium priority review comment from gemini-code-assist[bot] recommended using compile-time variables (--dart-define) for appEnvironment instead of hardcoding it.
  • A high priority review comment from gemini-code-assist[bot] pointed out that the GoRouter instance was being created in the build method, leading to inefficiency, and advised instantiating it once in initState.
  • A medium priority review comment from gemini-code-assist[bot] noted that HeadlinesFeedBloc had a dependency on UI-specific ThemeData for AdThemeStyle and suggested deriving AdThemeStyle directly from AppState properties.
  • A high priority review comment from gemini-code-assist[bot] indicated incorrect placement of super.build(context) in _HeadlinesFeedPageState when using AutomaticKeepAliveClientMixin.
  • Another high priority review comment from gemini-code-assist[bot] reiterated the incorrect placement of super.build(context) in _HeadlinesFeedPageState.
  • A medium priority review comment from gemini-code-assist[bot] suggested adding .distinct() to the authStateChanges stream to prevent redundant AppUserChanged events.

@fulleni fulleni added this to the Foundation Edition milestone Nov 1, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: auth bugs in dev/prod env -- read description

2 participants