Skip to content

Feature/app-performance-retention-#52#60

Merged
RaulRinconX merged 42 commits intomainfrom
feature/app-performance-retention-#52
Nov 5, 2024
Merged

Feature/app-performance-retention-#52#60
RaulRinconX merged 42 commits intomainfrom
feature/app-performance-retention-#52

Conversation

@RaulRinconX
Copy link
Copy Markdown
Contributor

@RaulRinconX RaulRinconX commented Nov 5, 2024

This pull request includes significant updates to the EcoBites app, focusing on integrating Firebase Analytics, improving error handling, and modifying the address management system to use a single address instead of a list. Here are the most important changes:

Firebase Integration:

  • Added FirebaseAnalytics and FirebaseCrashlytics integration to log various events and errors (lib/app.dart, lib/core/utils/analytics_service.dart). [1] [2]

Error Handling:

  • Introduced a new ServerFailure class to handle server-related errors more effectively (lib/core/error/failures.dart).

UI and State Management:

  • Updated the EcoBitesApp widget to include FirebaseAnalytics and added a ProfileBloc provider (lib/app.dart).
  • Enhanced the AddressBloc to handle new error types and to use the current user's ID from Firebase Authentication (lib/features/address/presentation/bloc/address_bloc.dart). [1] [2]

Miscellaneous:

  • Added FirebaseFirestore to the AuthRepositoryImpl to support future enhancements (lib/features/auth/data/repositories/auth_repository_impl.dart). [1] [2]

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced user profile management with the addition of profile fetching and updating functionalities.
    • Improved user registration process, now capturing more detailed user information during sign-up.
    • Integrated Firebase Analytics for tracking user interactions and performance metrics.
    • Added a loading indicator during address confirmation and improved error handling for address-related actions.
  • Bug Fixes

    • Refined error handling for fetching user addresses and profiles, providing clearer feedback.
  • Documentation

    • Updated documentation to reflect new features and changes in method signatures.
  • Chores

    • Updated dependencies to include Firebase services for analytics and crash reporting.

…nd update saveAddress method for user profile integration
…ource, add statusCode and errorType to ServerFailure, and improve user ID retrieval in AddressScreen
…lity and equality checks, update event constructor for named parameters, and add connectivity_plus dependency
…andling, enhance UserProfile validation, and improve error messages in ProfileBloc
…d parameters for improved logging, enhancing clarity and maintainability
…actor logEvent method to accept structured parameters, and update app initialization to include analytics
…me logging, add logUserSession and logLoadingTime methods in HomeScreen and main.dart
@RaulRinconX RaulRinconX added the enhancement New feature or request label Nov 5, 2024
@RaulRinconX RaulRinconX added this to the Sprint 3 milestone Nov 5, 2024
@RaulRinconX RaulRinconX self-assigned this Nov 5, 2024
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Nov 5, 2024

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

📝 Walkthrough
📝 Walkthrough
📝 Walkthrough

Walkthrough

The pull request introduces significant updates across multiple files to enhance user profile management and analytics tracking in the EcoBites application. Key changes include the addition of a ProfileBloc for managing user profiles, modifications to the EcoBitesApp class to integrate Firebase Analytics, and the introduction of new methods for logging analytics events. The data handling for user addresses has been streamlined to focus on single address retrieval, and various repositories and use cases have been implemented to support these features.

Changes

File Change Summary
lib/app.dart Added ProfileBloc import, updated EcoBitesApp constructor to require FirebaseAnalytics, added BlocProvider for ProfileBloc, and integrated FirebaseAnalyticsObserver for navigation tracking.
lib/core/error/failures.dart Introduced ServerFailure class to enhance error handling with additional parameters.
lib/core/utils/analytics_service.dart Added asynchronous functions for logging various analytics events using Firebase.
lib/features/address/data/datasources/address_remote_data_source.dart Updated fetchUserAddresses to fetchUserAddress for single address retrieval and modified Firestore path for saving addresses.
lib/features/address/data/repositories/address_repository_impl.dart Renamed methods to focus on single address handling and updated error handling.
lib/features/address/domain/repositories/address_repository.dart Removed fetchUserAddresses and added fetchUserAddress to reflect single address retrieval.
lib/features/address/domain/usecases/fetch_user_addresses_usecase.dart Renamed to FetchUserAddressUseCase and updated method to handle a single address.
lib/features/address/presentation/bloc/address_bloc.dart Renamed use case and updated methods for handling single address logic and error handling.
lib/features/address/presentation/bloc/address_event.dart Updated SaveAddress to include a required userId parameter.
lib/features/address/presentation/screens/address_screen.dart Added user ID retrieval logic and loading indicators for address confirmation.
lib/features/auth/data/repositories/auth_repository_impl.dart Updated signUp method to include additional parameters for user profile creation.
lib/features/auth/domain/repositories/auth_repository.dart Enhanced signUp method to require more user information.
lib/features/auth/domain/usecases/sign_up_usecase.dart Updated SignUpParams to include new user fields.
lib/features/auth/presentation/bloc/auth_bloc.dart Enhanced _onSignUpRequested method to accept additional parameters.
lib/features/auth/presentation/bloc/auth_event.dart Expanded SignUpRequested class to include new parameters.
lib/features/auth/presentation/screens/login_screen.dart Integrated logging for login performance.
lib/features/auth/presentation/screens/register_screen.dart Enhanced registration form with new input fields and dropdowns.
lib/features/home/presentation/screens/home_screen.dart Updated logging strategy to use Firebase Analytics.
lib/features/profile/data/models/user_profile_model.dart Introduced UserProfile class for managing user profile data.
lib/features/profile/data/repositories/profile_repository_impl.dart Implemented methods for fetching and updating user profiles in Firestore.
lib/features/profile/domain/entities/user_profile.dart Added UserProfile class with factory methods for data handling.
lib/features/profile/domain/repositories/profile_repository.dart Created repository methods for managing user profiles and addresses.
lib/features/profile/domain/repositories/user_profile_repository.dart Introduced UserProfileRepository interface for profile management.
lib/features/profile/domain/usecases/fetch_user_profile_usecase.dart Added use case for fetching user profiles.
lib/features/profile/domain/usecases/update_user_profile_usecase.dart Added use case for updating user profiles.
lib/features/profile/presentation/bloc/profile_bloc.dart Implemented ProfileBloc for managing profile states and events.
lib/features/profile/presentation/bloc/profile_event.dart Defined events for loading and updating profiles.
lib/features/profile/presentation/bloc/profile_state.dart Established states for user profile management.
lib/features/profile/presentation/screens/profile_screen.dart Refactored to manage user profile state and input handling.
lib/features/splash/presentation/screens/splash_screen.dart Updated logging functionality for splash screen events.
lib/injection_container.dart Registered new profile-related components in service locator.
lib/main.dart Integrated Firebase services for analytics and error reporting.
pubspec.yaml Added dependencies for Firebase Analytics and Crashlytics.
widgetbook/macos/Flutter/GeneratedPluginRegistrant.swift Registered new plugins for connectivity and Firebase services.

Possibly related PRs

Suggested labels

bug

Suggested reviewers

  • abelarismendy

Poem

In the garden where data grows,
Profiles bloom, as analytics flows.
With each click and every tap,
We track the journey, map the map.
So hop along, dear user friends,
In EcoBites, the fun never ends! 🐰✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 40

🧹 Outside diff range and nitpick comments (52)
lib/features/address/domain/repositories/address_repository.dart (1)

1-8: Consider adding data validation and access control.

Since this repository handles user-specific address data and is part of Firebase integration:

  1. Consider adding input validation for the userId parameter to prevent unauthorized access
  2. Document any assumptions about the format of userId (e.g., if it should be a Firebase UID)
  3. Consider adding repository-level access control checks
lib/features/profile/domain/repositories/user_profile_repository.dart (1)

5-8: LGTM! Well-designed repository interface.

The interface follows clean architecture principles with:

  • Clear separation of concerns
  • Proper error handling using Either type
  • Appropriate use of nullable types
  • Consistent async operations

Consider documenting the following aspects to improve maintainability:

  1. Add documentation comments explaining the expected behavior of each method
  2. Document potential error cases that might result in ServerFailure
  3. Consider adding method-level annotations for tracking analytics events, given that this PR introduces Firebase Analytics

Example documentation structure:

/// Repository interface for managing user profiles
abstract class UserProfileRepository {
  /// Fetches a user profile by their unique identifier
  /// 
  /// Parameters:
  ///   - uid: The unique identifier of the user
  /// 
  /// Returns:
  ///   - Right(UserProfile?): The user profile if found, null if not exists
  ///   - Left(ServerFailure): If there's an error communicating with the server
  Future<Either<ServerFailure, UserProfile?>> fetchUserProfile(String uid);

  /// Updates an existing user profile
  /// 
  /// Parameters:
  ///   - profile: The updated user profile data
  /// 
  /// Returns:
  ///   - Right(Unit): If the update was successful
  ///   - Left(ServerFailure): If there's an error communicating with the server
  Future<Either<ServerFailure, Unit>> updateUserProfile(UserProfile profile);
}
lib/features/profile/domain/usecases/update_user_profile_usecase.dart (1)

4-7: Add class documentation.

Consider adding documentation to describe the purpose and usage of this use case. This helps maintain clean architecture principles by clearly stating the use case's role in the domain layer.

+/// Updates a user's profile through the user profile repository.
+///
+/// This use case follows clean architecture principles and serves as an intermediary
+/// between the presentation layer and the repository layer for profile updates.
 class UpdateUserProfileUseCase {
lib/features/address/presentation/bloc/address_event.dart (1)

Line range hint 1-20: Consider adding documentation for the event classes.

While the code structure is clean, adding documentation comments (///) for each event class would improve maintainability by clearly stating the purpose and expected payload of each event.

Example improvement:

 abstract class AddressEvent {}

+/// Event to save a user's address to the remote data source
+/// 
+/// Requires [address] containing the address details and [userId] for identification
 class SaveAddress extends AddressEvent {
   SaveAddress(this.address,  {required this.userId});
   final Address address;
   final String userId;
 }
lib/core/error/failures.dart (1)

14-23: Add documentation and consider additional improvements.

The ServerFailure implementation looks good and provides valuable error details. Consider these enhancements:

+/// Represents a failure that occurs during server communication.
+/// 
+/// Use this when the server returns an error response (e.g., 4xx, 5xx).
+/// For connectivity issues, use [NetworkFailure] instead.
+/// 
+/// Example:
+/// ```dart
+/// ServerFailure('Invalid request', statusCode: 400, errorType: 'BAD_REQUEST')
+/// ```
 class ServerFailure extends Failure {
+  /// Common error types for consistent error handling
+  static const String BAD_REQUEST = 'BAD_REQUEST';
+  static const String NOT_FOUND = 'NOT_FOUND';
+  static const String SERVER_ERROR = 'SERVER_ERROR';

   const ServerFailure(
     super.message, {
+    /// HTTP status code (e.g., 400, 404, 500)
     this.statusCode,
+    /// Type of error for categorization (e.g., BAD_REQUEST, NOT_FOUND)
     this.errorType,
   });

   final int? statusCode;
   final String? errorType;
+
+  /// Returns true if this is a client error (4xx status code)
+  bool get isClientError => statusCode != null && statusCode! >= 400 && statusCode! < 500;
+
+  /// Returns true if this is a server error (5xx status code)
+  bool get isServerError => statusCode != null && statusCode! >= 500 && statusCode! < 600;
 }
lib/features/profile/domain/usecases/fetch_user_profile_usecase.dart (1)

6-9: Add class and constructor documentation.

While the implementation is clean, adding documentation would improve code maintainability. Consider adding:

  • Class documentation explaining the purpose and responsibility of this use case
  • Constructor documentation describing the repository dependency
+/// Use case responsible for fetching a user's profile information.
 class FetchUserProfileUseCase {

+  /// Creates a new instance of [FetchUserProfileUseCase].
+  /// 
+  /// Requires a [UserProfileRepository] for accessing user profile data.
   FetchUserProfileUseCase(this.repository);
   final UserProfileRepository repository;
lib/features/address/domain/usecases/fetch_user_addresses_usecase.dart (1)

12-14: Enhance method documentation.

While the implementation is correct, consider improving the documentation to explain:

  • Why the address might be null
  • The business logic behind single address management
-  // Update to return a single Address, not a list.
+  /// Fetches the user's primary address.
+  /// Returns null if the user hasn't set an address yet.
+  /// This aligns with the single-address management approach where each user
+  /// maintains only one active address at a time.
lib/features/auth/domain/repositories/auth_repository.dart (1)

4-4: Consider domain boundary implications.

The import of CuisineType from the food domain creates a dependency between authentication and food domains. While this works for the current use case, consider if this coupling aligns with your domain boundaries. Alternative approaches could include:

  1. Moving CuisineType to a shared domain model
  2. Using a more generic type (e.g., string enum) in the auth domain
lib/features/profile/presentation/bloc/profile_event.dart (2)

1-12: LGTM! Base class implementation follows best practices.

The ProfileEvent class correctly implements Equatable and is marked as @immutable, which is essential for bloc event classes.

Consider removing the extra blank line at line 5 to maintain consistent spacing.


20-30: Enhance parameter validation and make the class const.

While the implementation is generally good, consider these improvements:

  1. Add null checks for parameters
  2. Validate updatedProfile
  3. Make the class const for better performance

Apply this diff to implement the suggestions:

 class UpdateProfileEvent extends ProfileEvent {
-  UpdateProfileEvent({
+  const UpdateProfileEvent({
     required this.userId,
     required this.updatedProfile,
-    }) : assert(userId.isNotEmpty, 'userId cannot be empty');
+    }) : assert(userId.isNotEmpty, 'UpdateProfileEvent requires a non-empty userId'),
+         assert(updatedProfile != null, 'UpdateProfileEvent requires a non-null updatedProfile');
   final String userId;
   final UserProfile updatedProfile;
lib/features/auth/presentation/bloc/auth_event.dart (1)

27-32: Consider implementing a builder pattern

The class has a large number of required parameters which could make it cumbersome to use and maintain.

Consider implementing a builder pattern or breaking this into smaller, more focused events:

class SignUpBuilder {
  String? email;
  String? password;
  String? name;
  String? surname;
  String? citizenId;
  String? phone;
  DateTime? birthDate;
  CuisineType? favoriteCuisine;

  SignUpBuilder setEmail(String email) {
    this.email = email;
    return this;
  }
  // ... similar methods for other fields

  SignUpRequested build() {
    assert(email != null && password != null /* ... */);
    return SignUpRequested(
      email: email!,
      password: password!,
      name: name!,
      surname: surname!,
      citizenId: citizenId!,
      phone: phone!,
      birthDate: birthDate!,
      favoriteCuisine: favoriteCuisine!,
    );
  }
}
lib/main.dart (1)

11-11: Consider moving analytics instance to dependency injection.

While the global analytics instance works, consider moving it to the dependency injection container (injection_container.dart) for better testability and maintainability. This would align with the existing DI pattern in the codebase.

-final FirebaseAnalytics analytics = FirebaseAnalytics.instance;

// In injection_container.dart:
+void setupAnalytics() {
+  sl.registerLazySingleton<FirebaseAnalytics>(() => FirebaseAnalytics.instance);
+}
lib/features/profile/presentation/bloc/profile_state.dart (4)

5-5: Translate comment to English for consistency.

The codebase should maintain consistent use of English in comments.

-// Definimos los diferentes estados que puede tener el perfil
+// Define the different states that a profile can have

6-6: Consider using sealed class or freezed package.

For better state handling and pattern matching, consider:

  1. Using Dart 3's sealed class feature:
sealed class ProfileState {}
  1. Or using the freezed package for immutable state classes with union types support.

This would provide compile-time exhaustiveness checking when handling states.


12-27: Add copyWith method and consider simplifying equality implementation.

While the current implementation is functional, consider these improvements:

  1. Add a copyWith method for easier state updates
  2. Consider using Dart's collection package for simpler equality implementation
 class ProfileLoaded extends ProfileState {
   ProfileLoaded(this.profile, {this.isUpdated = false});
   final UserProfile profile;
   final bool isUpdated;

+  ProfileLoaded copyWith({
+    UserProfile? profile,
+    bool? isUpdated,
+  }) {
+    return ProfileLoaded(
+      profile ?? this.profile,
+      isUpdated: isUpdated ?? this.isUpdated,
+    );
+  }

   @override
-  bool operator ==(Object other) =>
-      identical(this, other) ||
-      other is ProfileLoaded &&
-          runtimeType == other.runtimeType &&
-          profile == other.profile &&
-          isUpdated == other.isUpdated;
+  bool operator ==(Object other) =>
+      identical(this, other) ||
+      (other is ProfileLoaded &&
+          profile == other.profile &&
+          isUpdated == other.isUpdated);

   @override
   int get hashCode => profile.hashCode ^ isUpdated.hashCode;
 }

29-43: Enhance error handling and debugging support.

Consider these improvements to the error state:

  1. Add structured error information instead of just a message
  2. Include a toString override for better debugging
 class ProfileError extends ProfileState {
-  ProfileError(this.message);
+  ProfileError({
+    required this.message,
+    this.code,
+    this.stackTrace,
+  });

   final String message;
+  final String? code;
+  final StackTrace? stackTrace;

   @override
   bool operator ==(Object other) =>
       identical(this, other) ||
       (other is ProfileError &&
-          runtimeType == other.runtimeType &&
-          message == other.message);
+          message == other.message &&
+          code == other.code);

   @override
-  int get hashCode => message.hashCode;
+  int get hashCode => Object.hash(message, code);
+
+  @override
+  String toString() => 'ProfileError(message: $message, code: $code)';
 }
lib/features/profile/data/repositories/profile_repository_impl.dart (2)

1-8: Consider removing the always_specify_types ignore directive.

Explicit type specifications enhance code clarity and maintainability. Instead of ignoring the lint rule, consider adding explicit types throughout the file.

-// ignore_for_file: always_specify_types

9-13: Add class-level documentation.

Consider adding documentation to describe the purpose of this repository implementation and its role in the application architecture.

+/// Implementation of [UserProfileRepository] that manages user profiles in Firebase Firestore.
+/// 
+/// This repository handles the storage and retrieval of user profile data, including
+/// favorite cuisines and diet types.
 class ProfileRepositoryImpl implements UserProfileRepository {
lib/features/auth/domain/usecases/sign_up_usecase.dart (2)

29-47: Add documentation and consider splitting into smaller objects.

The SignUpParams class has grown to handle many responsibilities. Consider:

  1. Adding documentation for each parameter, especially format requirements for citizenId and phone
  2. Splitting into logical groups (e.g., BasicInfo, ContactInfo, Preferences) to improve maintainability

Example refactor:

+/// Represents user's basic information
+class UserBasicInfo extends Equatable {
+  const UserBasicInfo({
+    required this.name,
+    required this.surname,
+    required this.birthDate,
+  });
+
+  final String name;
+  final String surname;
+  final DateTime birthDate;
+
+  @override
+  List<Object?> get props => [name, surname, birthDate];
+}

 class SignUpParams extends Equatable {
   const SignUpParams({
     required this.email,
     required this.password,
-    required this.name,
-    required this.surname,
+    required this.basicInfo,
     required this.citizenId,
     required this.phone,
-    required this.birthDate,
     required this.favoriteCuisine,
   });

   final String email;
   final String password;
-  final String name;
-  final String surname;
+  final UserBasicInfo basicInfo;
   final String citizenId;
   final String phone;
-  final DateTime birthDate;
   final CuisineType favoriteCuisine;

Add validation for sensitive user data at presentation layer

After reviewing the codebase, I can see that validation is implemented in the presentation layer (login_screen.dart) but missing for the sign-up process. The repository directly saves the sensitive data without validation. Consider:

  • Add form validation in the sign-up screen similar to the login screen's pattern for:
    • citizenId (format/length check)
    • phone (proper phone number format)
    • birthDate (age restrictions)
    • name/surname (non-empty, proper character set)

The login screen already demonstrates a good validation pattern using TextFormField's validator property that can be reused.

🔗 Analysis chain

Line range hint 14-24: Consider adding input validation for sensitive data.

The use case is handling sensitive personal information (citizenId, phone) but doesn't perform any validation. Consider adding validation rules or moving them to a separate validation use case to ensure data integrity before reaching the repository.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if there's any validation logic in the repository or related files
rg -A 5 "validate|sanitize|check.*citizenId|check.*phone" lib/features/auth/

Length of output: 129


Script:

#!/bin/bash
# Let's check for any validation-related code in the auth feature
rg -l "validate|sanitize|check" lib/features/auth/

# Check the repository implementation to see how the data is handled
fd "auth.*repository.*impl" lib/features/auth/

# Look for any validator classes or utilities
fd "validator|validation" lib/features/auth/

# Check the SignUpParams class definition
ast-grep --pattern 'class SignUpParams {
  $$$
}'

Length of output: 319


Script:

#!/bin/bash
# Let's check the repository implementation to see how the sign-up data is handled
cat lib/features/auth/data/repositories/auth_repository_impl.dart

# Check the login screen for any validation patterns we might want to reuse
cat lib/features/auth/presentation/screens/login_screen.dart

# Look for any existing validation patterns in the entire codebase
rg "String\?.*validate" lib/

Length of output: 12292

lib/core/utils/analytics_service.dart (2)

1-3: Consider adding exports and error handling imports.

The analytics service functions are implicitly public. Consider:

  1. Adding an explicit export statement
  2. Importing error handling utilities for better error management
+export 'analytics_service.dart';
 import 'package:firebase_analytics/firebase_analytics.dart';
 import 'package:firebase_crashlytics/firebase_crashlytics.dart';
+import 'package:meta/meta.dart';
+import '../error/failures.dart';

33-40: Enhance login analytics with additional context.

Consider adding more context to login time tracking:

  1. Login method (email, social, etc.)
  2. Success/failure status
  3. Input validation
-Future<void> logLoginTime(int milliseconds) async {
+Future<void> logLoginTime({
+  required int milliseconds,
+  required String loginMethod,
+  required bool success,
+}) async {
+  if (milliseconds < 0) {
+    throw ArgumentError('Login time cannot be negative');
+  }
   await FirebaseAnalytics.instance.logEvent(
     name: 'login_time',
     parameters: <String, Object>{
       'milliseconds': milliseconds,
+      'login_method': loginMethod,
+      'success': success,
     },
   );
 }
lib/features/profile/data/models/user_profile_model.dart (3)

1-4: Consider adding Equatable and documentation.

The class would benefit from:

  1. Implementing Equatable for proper object comparison in Firebase operations
  2. Adding class-level documentation explaining its purpose and usage
+import 'package:equatable/equatable.dart';
 import 'package:eco_bites/features/address/data/models/address_model.dart';
 import 'package:eco_bites/features/food/domain/entities/cuisine_type.dart';
 
+/// Represents a user's profile in the EcoBites application.
+/// Contains personal information and preferences of the user.
-class UserProfile {
+class UserProfile extends Equatable {

34-42: Remove redundant comment and add Equatable implementation.

The field declarations look good, but:

  1. Remove the redundant comment on line 42
  2. Add Equatable props implementation for proper object comparison
   final CuisineType favoriteCuisine;
   final String diet;
-  final AddressModel? address; // Optional address field
+  final AddressModel? address;
+
+  @override
+  List<Object?> get props => [
+        name,
+        surname,
+        citizenId,
+        email,
+        phone,
+        birthDate,
+        favoriteCuisine,
+        diet,
+        address,
+      ];

44-56: Add toString method for better debugging.

The toMap implementation looks good. Consider adding a toString method for better debugging and logging capabilities.

     };
   }
+
+  @override
+  String toString() => 'UserProfile('
+      'name: $name, '
+      'email: $email, '
+      'phone: $phone, '
+      'birthDate: $birthDate, '
+      'favoriteCuisine: $favoriteCuisine, '
+      'address: $address)';
 }
lib/features/address/data/datasources/address_remote_data_source.dart (3)

19-19: Translate Spanish comments to English for consistency.

-      // Aquí aseguramos que el UID sea el mismo que el del usuario registrado
+      // Ensure the UID matches the registered user's UID

49-55: Add specific error codes for different failure scenarios.

Consider using specific error codes to differentiate between different types of failures (e.g., 'address-not-found', 'invalid-user', etc.) instead of reusing the original error code.

       if (e is FirebaseException) {
         throw FirebaseException(
           message: 'Failed to fetch address: ${e.message}',
           plugin: 'cloud_firestore',
-          code: e.code,
+          code: e.code == 'permission-denied' ? 'address-permission-denied' :
+                e.code == 'not-found' ? 'address-not-found' :
+                'address-fetch-error',
         );
       }

41-46: Optimize Firestore read with exists() check.

For cases where we only need to check address existence, consider using exists() first to avoid fetching the entire document.

-      final DocumentSnapshot<Map<String, dynamic>> snapshot =
-          await _firestore.collection('profiles').doc(userId).get();
-
-      if (snapshot.exists && snapshot.data()?['address'] != null) {
-        return AddressModel.fromMap(snapshot.data()!['address']);
-      }
+      final docRef = _firestore.collection('profiles').doc(userId);
+      final exists = await docRef.exists();
+      
+      if (!exists) return null;
+      
+      final snapshot = await docRef.get();
+      final address = snapshot.data()?['address'];
+      return address != null ? AddressModel.fromMap(address) : null;
lib/features/profile/domain/entities/user_profile.dart (1)

40-48: Improve field documentation and type safety.

Consider these improvements:

  1. Add documentation for each field
  2. Convert dietType to an enum for type safety

Add field documentation and create an enum:

/// Enumeration of supported diet types
enum DietType {
  unknown,
  vegan,
  vegetarian,
  pescatarian,
  other
}

class UserProfile extends Equatable {
  // ... constructor ...

  /// Unique identifier of the user
  final String userId;
  
  /// User's first name
  final String name;
  
  /// User's last name
  final String surname;
  
  /// Government-issued identification number
  final String citizenId;
  
  /// User's email address
  final String email;
  
  /// Contact phone number
  final String phone;
  
  /// User's date of birth
  final DateTime birthDate;
  
  /// Preferred cuisine type
  final CuisineType favoriteCuisine;
  
  /// Dietary preference/restriction
  final DietType dietType;
lib/app.dart (2)

59-61: LGTM: ProfileBloc provider is properly integrated

The ProfileBloc provider is correctly added to the MultiBlocProvider list and follows the established pattern of using the service locator.

Consider documenting the order of BlocProviders if there are dependencies between them, as the order can matter when blocks depend on each other.


64-66: LGTM: Firebase Analytics observer is properly configured

The FirebaseAnalyticsObserver is correctly integrated for automatic screen tracking. Consider adding custom analytics events for important user interactions beyond just navigation.

Example tracking points to consider:

  • User engagement metrics
  • Feature usage statistics
  • Error occurrences
  • Performance metrics
lib/features/auth/presentation/bloc/auth_bloc.dart (1)

71-76: Document the requirements for new signup parameters.

Please add documentation for the new parameters describing:

  • Required formats (e.g., phone number format, citizenId format)
  • Validation rules
  • Whether fields are optional or required

Example documentation:

/// SignUpParams(
///   email: Valid email address
///   password: Min 8 chars with 1 uppercase, 1 number
///   name: Required, 2-50 chars
///   surname: Required, 2-50 chars
///   citizenId: Required, national ID format XX-XXXX-XXXXX
///   phone: Required, format +XX-XXX-XXX-XXXX
///   birthDate: Required, must be 18+ years old
///   favoriteCuisine: Optional
/// )
lib/injection_container.dart (1)

Line range hint 181-185: Consider adding Firebase services initialization verification.

While the Firebase service registrations are correct, consider adding initialization checks:

  1. Verify Firebase initialization before registering services
  2. Consider wrapping Firebase instances in initialization-aware factories

Example improvement:

// Add this before registering Firebase services
Future<void> ensureFirebaseInitialized() async {
  if (!Firebase.apps.isNotEmpty) {
    await Firebase.initializeApp();
  }
}
lib/features/auth/presentation/screens/register_screen.dart (1)

132-149: Enhance form field components with validation and accessibility.

The helper methods could be improved with:

  1. Support for validation
  2. Error text display
  3. Better accessibility labels

Suggested improvements for _buildTextField:

 Widget _buildTextField(String label, TextEditingController controller,
-    {bool obscureText = false,}) {
+    {bool obscureText = false,
+     String? Function(String?)? validator,
+     String? errorText,
+     String? semanticsLabel,}) {
   return Padding(
     padding: const EdgeInsets.symmetric(vertical: 8.0),
     child: TextField(
       controller: controller,
       obscureText: obscureText,
+      semanticsLabel: semanticsLabel ?? label,
       decoration: InputDecoration(
         labelText: label,
         border: OutlineInputBorder(
           borderRadius: BorderRadius.circular(5),
         ),
         filled: true,
         fillColor: Colors.white,
+        errorText: errorText,
       ),
     ),
   );
 }

Also applies to: 180-206

lib/features/address/presentation/screens/address_screen.dart (1)

235-237: Enhance loading indicator UX in the confirm button.

While the loading indicator works, the button might change size when switching between text and loader, causing layout shifts.

Consider this improvement:

- child: isLoading
-   ? const CircularProgressIndicator()
-   : const Text('Confirm Address'),
+ child: SizedBox(
+   height: 20,  // Match your text height
+   child: isLoading
+     ? const CircularProgressIndicator(
+         strokeWidth: 2,
+         valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
+       )
+     : const Text('Confirm Address'),
+ ),
lib/features/home/presentation/screens/home_screen.dart (1)

Line range hint 1-300: Consider splitting HomeScreen into smaller components.

The HomeScreen has grown to handle multiple responsibilities (location tracking, address management, analytics, UI). Consider:

  1. Extract location tracking into a separate service
  2. Create dedicated widgets for address management and search
  3. Move analytics logic to a service layer

This will improve:

  • Testability
  • Maintainability
  • Code reusability
  • Separation of concerns

Would you like help creating a detailed refactoring plan?

lib/features/address/data/repositories/address_repository_impl.dart (1)

29-29: Consider extracting the error message 'No internet connection' to a constant.

The error message 'No internet connection' is repeated in multiple places. Extracting it to a constant will improve maintainability and make future changes easier.

Apply this diff:

+const String _noInternetConnectionMessage = 'No internet connection';

...

-      return const Left<Failure, void>(NetworkFailure('No internet connection'));
+      return const Left<Failure, void>(NetworkFailure(_noInternetConnectionMessage));

...

-      return const Left<Failure, Address?>(NetworkFailure('No internet connection'));
+      return const Left<Failure, Address?>(NetworkFailure(_noInternetConnectionMessage));

Also applies to: 46-46

lib/features/address/presentation/bloc/address_bloc.dart (2)

51-56: Use specific exception types in error handling

In the catch block, consider catching specific exceptions or failures rather than a generic Exception. This will improve error handling and allow for more precise messaging to the user.


49-56: Refactor duplicate error handling logic

The error handling logic in _onSaveAddress and _onLoadAddress is similar. Consider refactoring this code into a separate method to reduce duplication and improve maintainability.

Example refactored method:

void _handleFailure(Failure failure, Emitter<AddressState> emit, String action) {
  if (failure is ServerFailure) {
    emit(AddressError('Failed to $action address: Server Error'));
  } else if (failure is NetworkFailure) {
    emit(AddressError('Failed to $action address: Network Error'));
  } else {
    emit(AddressError('Failed to $action address: ${failure.toString()}'));
  }
}

Then update your error handling in both methods:

// In _onSaveAddress:
} catch (failure) {
  _handleFailure(failure as Failure, emit, 'save');
}

// In _onLoadAddress:
result.fold(
  (failure) {
    _handleFailure(failure, emit, 'load');
  },
  // ...
);

Also applies to: 75-83

lib/features/profile/presentation/bloc/profile_bloc.dart (2)

41-46: Use a distinct state instead of emitting an error when caching due to no internet.

When there is no internet connection and the updated profile is cached for later synchronization, the bloc currently emits a ProfileError state with a message indicating that the data will be saved when online. Emitting an error state in this expected scenario may not be appropriate. Consider creating a new state, such as ProfileCached, to represent this situation more accurately and inform the UI accordingly.

This change enhances state management clarity and allows the UI to provide appropriate feedback to the user.


55-57: Enhance exception handling for more informative error messages.

In the catch blocks, the emitted ProfileError states have generic messages. Providing more detailed error information can aid in debugging and improve user experience. Consider including exception details or specific error codes when appropriate.

For example, you could adjust the catch blocks to include exception information:

} catch (e) {
  emit(ProfileError('Failed to update profile: ${e.toString()}'));
}

Also applies to: 73-75, 100-101

lib/features/auth/data/repositories/auth_repository_impl.dart (1)

63-63: Ensure code comments are in English for consistency

Comments on lines 63 and 70 are in Spanish. For consistency across the codebase and to aid team collaboration, please use English for all code comments.

Consider translating the comments:

-// Crear el usuario en Firebase Authentication
+// Create the user in Firebase Authentication

...

-// Guardar datos adicionales en Firestore
+// Save additional user data in Firestore

Also applies to: 70-70

lib/features/profile/presentation/screens/profile_screen.dart (5)

1-1: Avoid suppressing lint rules; address 'use_build_context_synchronously' and 'unrelated_type_equality_checks'

Suppressing lint rules at the file level can hide potential issues and reduce code quality. It's better to resolve the underlying warnings to ensure code correctness and maintainability.

Consider fixing the code to comply with the lint rules and remove the // ignore_for_file directive.


74-87: Handle potential state changes appropriately in BlocConsumer

In the listener of BlocConsumer, consider handling other potential states, such as ProfileLoading, to provide feedback to the user during long operations.

You might add a loading indicator or disable inputs while the profile is being updated.


107-107: Correct greeting text: change '¡Hi' to 'Hi'

There appears to be an unintended inverted exclamation mark before 'Hi'. Changing it to 'Hi' will correct the greeting.

Apply this diff:

- '¡Hi, ${_nameController.text}!',
+ 'Hi, ${_nameController.text}!',

138-138: Handle potential parsing exceptions when parsing birth date

Parsing the birth date string could throw a FormatException if the text is not in the expected format. Consider adding error handling to prevent potential crashes.

Wrap the parsing in a try-catch block:

DateTime? birthDate;
try {
  birthDate = DateFormat('MM/dd/yyyy').parse(_birthDateController.text);
} catch (e) {
  // Handle the error or assign a default value
  birthDate = DateTime.now(); // Example default value
}

And update the updatedProfile accordingly.


45-45: Check for accurate type comparisons in conditional statements

The lint rule unrelated_type_equality_checks suggests there may be a type comparison issue. Ensure that internetConnectionBloc.state is being compared to a compatible type.

Verify that DisconnectedInternet is a subtype of the state and adjust the comparison if necessary.

lib/features/profile/domain/repositories/profile_repository.dart (5)

24-27: Consider rethrowing original exceptions for better debugging

Catching exceptions and throwing new generic Exceptions can obscure the original stack trace and make debugging more difficult. Consider rethrowing the original exceptions to preserve the stack trace and exception details.

Apply this diff to rethrow the original exceptions:

} on FirebaseException catch (e) {
-     throw Exception('Failed to fetch profile: $e');
+     rethrow;
} catch (e) {
-     throw Exception('Error processing profile data: $e');
+     rethrow;
}

31-31: Use consistent language in code comments

The comment on line 31 is in Spanish. To maintain consistency across the codebase, it's recommended to use English for all code comments unless the project standard specifies otherwise.

Apply this diff to translate the comment:

- // Solo actualizamos los campos `favoriteCuisine` y `dietType`
+ // Only updating the fields `favoriteCuisine` and `dietType`

40-45: Simplify exception handling and preserve stack trace

By catching all exceptions and throwing new ones, the original stack trace is lost, which can hinder debugging. Consider rethrowing the exception to maintain the original stack trace.

Apply this diff:

} catch (e) {
-     if (e is FirebaseException) {
-       throw Exception('Failed to save profile: ${e.message}');
-     }
-     throw Exception('Unexpected error while saving profile: $e');
+     rethrow;
}

67-73: Remove unnecessary async and await in synchronous code

The mapping over snapshot.docs doesn't perform any asynchronous operations, so marking the closure as async and using Future.wait is unnecessary. Simplify the code by removing the async modifier and Future.wait.

Apply this diff to simplify the code:

- return await Future.wait(
-   snapshot.docs.map((QueryDocumentSnapshot<Map<String, dynamic>> doc) async {
-     final Map<String, dynamic> data = doc.data();
-     return AddressModel.fromMap(data);
-   }),
- );
+ return snapshot.docs.map((doc) {
+   return AddressModel.fromMap(doc.data());
+ }).toList();

83-86: Specify concrete types for the update data map

Using <Object, Object?> is too general. For clarity and type safety, use <String, dynamic> when updating Firestore documents.

Apply this diff:

-     .update(<Object, Object?>{
+     .update(<String, dynamic>{
        'favoriteCuisine': profile.favoriteCuisine.name,
        'dietType': profile.dietType,
      });
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 35f712d and 0a39904.

⛔ Files ignored due to path filters (2)
  • pubspec.lock is excluded by !**/*.lock
  • widgetbook/pubspec.lock is excluded by !**/*.lock
📒 Files selected for processing (34)
  • lib/app.dart (2 hunks)
  • lib/core/error/failures.dart (1 hunks)
  • lib/core/utils/analytics_service.dart (1 hunks)
  • lib/features/address/data/datasources/address_remote_data_source.dart (3 hunks)
  • lib/features/address/data/repositories/address_repository_impl.dart (1 hunks)
  • lib/features/address/domain/repositories/address_repository.dart (1 hunks)
  • lib/features/address/domain/usecases/fetch_user_addresses_usecase.dart (1 hunks)
  • lib/features/address/presentation/bloc/address_bloc.dart (3 hunks)
  • lib/features/address/presentation/bloc/address_event.dart (1 hunks)
  • lib/features/address/presentation/screens/address_screen.dart (4 hunks)
  • lib/features/auth/data/repositories/auth_repository_impl.dart (3 hunks)
  • lib/features/auth/domain/repositories/auth_repository.dart (2 hunks)
  • lib/features/auth/domain/usecases/sign_up_usecase.dart (3 hunks)
  • lib/features/auth/presentation/bloc/auth_bloc.dart (3 hunks)
  • lib/features/auth/presentation/bloc/auth_event.dart (1 hunks)
  • lib/features/auth/presentation/screens/login_screen.dart (2 hunks)
  • lib/features/auth/presentation/screens/register_screen.dart (5 hunks)
  • lib/features/home/presentation/screens/home_screen.dart (3 hunks)
  • lib/features/profile/data/models/user_profile_model.dart (1 hunks)
  • lib/features/profile/data/repositories/profile_repository_impl.dart (1 hunks)
  • lib/features/profile/domain/entities/user_profile.dart (1 hunks)
  • lib/features/profile/domain/repositories/profile_repository.dart (1 hunks)
  • lib/features/profile/domain/repositories/user_profile_repository.dart (1 hunks)
  • lib/features/profile/domain/usecases/fetch_user_profile_usecase.dart (1 hunks)
  • lib/features/profile/domain/usecases/update_user_profile_usecase.dart (1 hunks)
  • lib/features/profile/presentation/bloc/profile_bloc.dart (1 hunks)
  • lib/features/profile/presentation/bloc/profile_event.dart (1 hunks)
  • lib/features/profile/presentation/bloc/profile_state.dart (1 hunks)
  • lib/features/profile/presentation/screens/profile_screen.dart (1 hunks)
  • lib/features/splash/presentation/screens/splash_screen.dart (3 hunks)
  • lib/injection_container.dart (5 hunks)
  • lib/main.dart (2 hunks)
  • pubspec.yaml (2 hunks)
  • widgetbook/macos/Flutter/GeneratedPluginRegistrant.swift (1 hunks)
🔇 Additional comments (40)
lib/features/address/domain/repositories/address_repository.dart (1)

1-7: LGTM! Clean architecture principles well applied.

The repository interface follows clean architecture principles with proper separation of concerns and dependency rules. The use of Either type for error handling is a solid choice for functional error handling.

lib/features/profile/domain/repositories/user_profile_repository.dart (1)

1-3: LGTM! Well-organized imports.

The imports are clean, necessary, and well-structured, properly separating the external package (dartz) from internal imports.

lib/features/profile/domain/usecases/update_user_profile_usecase.dart (1)

1-2: LGTM! Clean and well-scoped imports.

The imports are specific to the feature module and contain only what's needed.

lib/features/address/presentation/bloc/address_event.dart (1)

6-6: LGTM! The changes align with the new address management system.

The addition of the required userId parameter is a good improvement that:

  • Makes the user identification explicit and type-safe
  • Supports the transition to single address per user
  • Integrates well with Firebase Authentication

Let's verify that all call sites have been updated with the new parameter:

Also applies to: 8-8

✅ Verification successful

All call sites have been updated correctly with the new userId parameter

The verification shows that there is only one call site in lib/features/address/presentation/screens/address_screen.dart line 219, and it correctly uses the new constructor signature with both the address and userId parameters:

context.read<AddressBloc>().add(SaveAddress(address, userId: userId));
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find all instances where SaveAddress is instantiated to ensure they include userId

# Search for SaveAddress constructor calls
ast-grep --pattern 'SaveAddress($$$)'

# Also search for any remaining references to old constructor
rg 'SaveAddress\([^,]+\)' 

Length of output: 228

lib/features/profile/domain/usecases/fetch_user_profile_usecase.dart (2)

1-4: LGTM! Clean and well-organized imports.

The imports are properly organized and include all necessary dependencies for the use case implementation.


11-17: Verify analytics integration requirement.

The PR objectives mention Firebase Analytics integration, but this use case doesn't implement any analytics tracking. Consider tracking profile fetch events for better user behavior insights.

Let's verify if analytics tracking is implemented elsewhere:

Consider injecting an analytics service to track:

  • Profile fetch success/failure rates
  • Profile fetch latency
  • User engagement patterns
lib/features/address/domain/usecases/fetch_user_addresses_usecase.dart (3)

7-8: LGTM! Class renaming and structure are well-implemented.

The transition from plural to singular naming aligns with the PR objective of managing a single address, while maintaining clean architecture principles.


7-22: Verify analytics integration for address operations.

Since this PR includes Firebase Analytics integration, we should ensure address-related events are properly tracked:

#!/bin/bash
# Description: Verify analytics integration for address operations

# Check for analytics event logging in address-related files
rg -g "lib/features/address/**" "logEvent|AnalyticsService|FirebaseAnalytics" -A 3

# Check if the analytics service has address-related event constants defined
rg -g "lib/core/utils/analytics_service.dart" "address" -B 1 -A 1

18-22: Verify userId handling across the codebase.

The implementation looks good, but let's verify the consistency of userId handling:

✅ Verification successful

userId handling is properly implemented across the address feature

The verification shows consistent userId handling throughout the address feature:

  • Data source properly validates userId and uses it for Firestore operations
  • Repository implementation correctly passes userId between layers
  • Presentation layer safely retrieves userId from preferences and passes it to use cases
  • All components using FetchUserAddressParams properly provide the required userId parameter
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify consistent userId handling in address-related files

# Check for any direct Firebase Auth userId access that might bypass this use case
rg -g "lib/features/address/**" "currentUser.*uid" -A 3

# Verify all calls to this use case properly pass the userId
ast-grep --pattern 'FetchUserAddressParams.*userId'

Length of output: 148


Script:

#!/bin/bash
# Let's try a more comprehensive verification approach

# Check for userId usage in address-related files
rg -g "lib/features/address/**" "userId" -A 2

# Find all instances where FetchUserAddress is used
rg -g "lib/**" "FetchUserAddress" -A 2

# Look for the implementation of the use case to verify userId handling
fd "fetch_user_address" --exec cat {}

Length of output: 11503

lib/features/auth/presentation/bloc/auth_event.dart (1)

2-2: LGTM: Import statement is correctly placed

The import for CuisineType is properly added and necessary for the new favoriteCuisine field.

lib/main.dart (3)

4-6: LGTM: Firebase service imports are properly configured.

The necessary Firebase service imports are correctly added and follow Dart's import conventions.


29-29: LGTM: Crashlytics error handling is properly configured.

The global Flutter error handler is correctly set up to report errors to Firebase Crashlytics.


31-31: Verify Firebase services initialization order.

While the Firebase core is initialized, it's important to ensure that Analytics and Crashlytics are properly initialized before use.

✅ Verification successful

Let me check the initialization order more thoroughly by looking at the main.dart file content.


Firebase services initialization order is correct

The initialization order in main.dart is properly implemented:

  1. Firebase Core is initialized first with Firebase.initializeApp()
  2. Firebase Performance is enabled after Core initialization
  3. Firebase Crashlytics error handler is set up before app launch
  4. Firebase Analytics instance is available globally and used after Core initialization
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for proper Firebase services initialization
# Expected: Find initialization of Firebase services before their usage

# Search for Firebase initialization patterns
echo "Checking Firebase services initialization patterns..."
rg -n "Firebase.*\.instance" --type dart

# Search for await patterns on Firebase initialization
echo "\nChecking for async initialization..."
rg -n "await.*Firebase" --type dart

Length of output: 3424


Script:

#!/bin/bash
# Get the content of main.dart to verify initialization order
cat lib/main.dart

Length of output: 1097

lib/features/profile/presentation/bloc/profile_state.dart (1)

8-10: LGTM!

The initial and loading states are correctly implemented as simple classes.

widgetbook/macos/Flutter/GeneratedPluginRegistrant.swift (2)

21-25: LGTM: Plugin registrations are correctly implemented.

The new plugins are properly registered with the Flutter plugin registry:

  • ConnectivityPlusPlugin for network state management
  • FLTFirebaseAnalyticsPlugin for analytics tracking
  • FLTFirebaseCrashlyticsPlugin for error reporting

Let's verify these plugins are being used in the codebase:

#!/bin/bash
# Description: Check usage of the newly added plugins
echo "Checking Firebase Analytics usage..."
rg "FirebaseAnalytics|logEvent|analytics\." -t dart

echo -e "\nChecking Firebase Crashlytics usage..."
rg "FirebaseCrashlytics|recordError|crashlytics\." -t dart

echo -e "\nChecking Connectivity Plus usage..."
rg "ConnectivityResult|connectivity\." -t dart

9-13: LGTM: Required plugin imports are properly added.

The new imports align with the PR objectives for Firebase Analytics integration and error tracking. The addition of connectivity_plus will help with network state management.

Let's verify the plugin dependencies are properly declared in pubspec.yaml:

✅ Verification successful

Let me verify the pubspec.yaml directly since the previous search didn't show the dependencies:


All required plugins are properly declared in pubspec.yaml and actively used in the codebase

The verification confirms that:

  • connectivity_plus: ^6.1.0 is declared and used for network state management
  • firebase_analytics: ^11.3.3 is declared and integrated across multiple files
  • firebase_crashlytics: ^4.1.3 is declared and implemented for error tracking
  • The imports in GeneratedPluginRegistrant.swift are consistent with both the dependencies and their usage in the codebase
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that the imported plugins are declared in pubspec.yaml
rg -A 1 "connectivity_plus:|firebase_analytics:|firebase_crashlytics:" pubspec.yaml

Length of output: 3088


Script:

#!/bin/bash
# Check pubspec.yaml for the required dependencies
cat pubspec.yaml

Length of output: 4009

lib/features/profile/data/repositories/profile_repository_impl.dart (1)

16-17: Verify Firebase collection name consistency.

Ensure that the 'profiles' collection name is consistent with other parts of the codebase and follows the established naming conventions.

✅ Verification successful

Collection name 'profiles' is consistently used across the codebase

The verification confirms that the 'profiles' collection name is used consistently across all Firestore operations in the codebase, specifically in:

  • Profile management operations (get, set, update)
  • Address-related operations
  • Authentication flow
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for all Firestore collection references to ensure consistency
rg "collection\(['\"]profiles['\"]" 

Length of output: 1285

lib/core/utils/analytics_service.dart (1)

1-58: Verify Firebase configuration and initialization.

Ensure that Firebase is properly initialized before using these analytics services.

✅ Verification successful

Let me check the Firebase initialization code to ensure it's properly set up.


Firebase is properly configured and initialized

Firebase is correctly set up with:

  • Proper initialization in main.dart using platform-specific configurations
  • Configuration files present (google-services.json)
  • Required Firebase dependencies in pubspec.yaml
  • Platform-specific Firebase options configured in firebase_options.dart

The analytics services in analytics_service.dart are safe to use.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check Firebase initialization in the app
rg -l "Firebase.initializeApp" --type dart

# Check if Firebase configuration files are present
fd "google-services.json|GoogleService-Info.plist"

# Look for Firebase-related configuration in pubspec.yaml
rg "firebase" pubspec.yaml

Length of output: 338


Script:

#!/bin/bash
# Check Firebase initialization implementation
rg -A 10 "Firebase.initializeApp" lib/main.dart

# Check firebase_options.dart content
cat lib/firebase_options.dart

Length of output: 2702

lib/features/address/data/datasources/address_remote_data_source.dart (2)

6-6: LGTM! Interface change aligns with single address management.

The change from returning a list to a nullable single address model correctly implements the new address management approach.


21-26: Verify the collection structure change impact.

The change from users/{userId}/addresses subcollection to profiles/{userId} document with an address field is significant. Ensure data migration is handled appropriately.

Let's verify the collection structure and existing data:

lib/features/profile/domain/entities/user_profile.dart (2)

50-62: LGTM! Clean and correct implementation.

The toMap method properly handles all field serializations, including the DateTime to Timestamp conversion.


64-76: LGTM! Correct Equatable implementation.

All fields are properly included in the props list, ensuring correct value comparison.

lib/features/splash/presentation/screens/splash_screen.dart (1)

3-3: LGTM: Analytics service import added correctly.

The import aligns with the PR's objective of integrating Firebase Analytics.

lib/app.dart (2)

9-9: LGTM: Import statements are correctly added

The new imports for ProfileBloc and FirebaseAnalytics are properly placed and necessary for the new functionality.

Also applies to: 12-12


18-18: LGTM: Constructor properly integrates FirebaseAnalytics

The analytics integration is properly implemented with required parameter and final field. However, let's verify that all instantiations of EcoBitesApp have been updated with the new required parameter.

Also applies to: 21-21

✅ Verification successful

All EcoBitesApp instantiations properly include the analytics parameter

Verification confirms that the only instantiation of EcoBitesApp (in main.dart) correctly includes both required parameters - appLaunchTime and analytics. No further changes needed.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for EcoBitesApp instantiations to ensure they include the analytics parameter
rg "EcoBitesApp\(" --type dart

Length of output: 222

pubspec.yaml (1)

55-55: LGTM: Meta package addition.

The addition of the meta package with version ^1.15.0 is appropriate and commonly required by Firebase packages.

lib/features/auth/presentation/screens/login_screen.dart (1)

1-1: LGTM: Analytics import properly integrated.

The analytics service import is correctly placed and aligns with the PR's objective of integrating Firebase Analytics.

lib/injection_container.dart (3)

27-31: LGTM! Well-organized profile feature imports.

The new imports follow clean architecture patterns and align with the PR objectives for profile management.


Line range hint 104-126: Verify the impact of switching to single address management.

The change from fetchUserAddressesUseCase to fetchUserAddressUseCase reflects a shift from managing multiple addresses to a single address. This architectural change needs careful verification:

#!/bin/bash
# Check for any remaining plural references to addresses
echo "Checking for plural 'addresses' references in the codebase..."
rg -l "addresses" "lib/features/address"

# Check for potential UI/bloc references to address lists
echo "Checking for array/list handling of addresses..."
ast-grep --pattern 'List<Address' 

83-99: Consider adding error handling for profile operations.

While the dependency registration follows clean architecture patterns correctly, consider adding error handling:

  1. Register failure handlers for profile operations
  2. Ensure the repository implementation handles Firestore errors appropriately

Let's verify if error handling is implemented in the repository:

lib/features/auth/presentation/screens/register_screen.dart (1)

18-25: LGTM: Controllers are properly managed.

The TextEditingController instances are correctly initialized and properly disposed of, following Flutter best practices for resource management.

Also applies to: 29-35

lib/features/home/presentation/screens/home_screen.dart (1)

76-77: Verify loading time measurement implementation.

The loading time measurement needs verification:

  1. The logLoadingTime function is not defined in the visible code
  2. Consider error handling for the measurement logic
  3. Loading time measurement could be affected by other post-frame callbacks
✅ Verification successful

Let me gather more information about the logLoadingTime implementation.


Loading time measurement implementation is properly handled

The verification shows that:

  1. logLoadingTime is well-implemented in analytics_service.dart using Firebase Analytics
  2. The timing measurement is correctly isolated as the first PostFrameCallback in initState, ensuring accurate measurement
  3. Error handling is inherently managed by Firebase Analytics implementation

While there are multiple PostFrameCallback usages in home_screen.dart, they are properly sequenced with the timing measurement being the first callback, ensuring accurate load time capture.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for logLoadingTime implementation
echo "Searching for logLoadingTime implementation..."
rg "logLoadingTime" --type dart

# Search for other PostFrameCallback usage that might affect timing
echo "Checking other PostFrameCallback usage..."
ast-grep --pattern 'WidgetsBinding.instance.addPostFrameCallback' 

Length of output: 3216


Script:

#!/bin/bash
# Check the implementation of logLoadingTime in analytics_service.dart
echo "Checking logLoadingTime implementation..."
cat lib/core/utils/analytics_service.dart

# Check if there are any error handling patterns in home_screen.dart
echo "Checking error handling patterns in home_screen.dart..."
cat lib/features/home/presentation/screens/home_screen.dart

Length of output: 14616

lib/features/address/data/repositories/address_repository_impl.dart (2)

20-32: saveAddress method implementation looks good.

The saveAddress method correctly checks for network connectivity, handles exceptions, and returns the appropriate Either type.


34-47: fetchUserAddress method implementation is correct.

The fetchUserAddress method properly handles the retrieval of the user's address, including the case when no address is found, and handles exceptions appropriately.

lib/features/address/presentation/bloc/address_bloc.dart (4)

15-15: Ensure constructor parameter matches renamed use case

The constructor parameter fetchUserAddressUseCase should match the updated use case name. Confirm that this change aligns with the renamed class.


27-28: Handle potential null user when fetching user ID

While retrieving the current user from Firebase Authentication, ensure that the possibility of currentUser being null is adequately handled. The current implementation gracefully handles this by returning null for the userId.


85-89: Handle null address appropriately

When the fetched address is null, emitting AddressInitial is acceptable. Ensure that this state is correctly handled in the UI to inform the user that no address is available.


41-43: 🛠️ Refactor suggestion

Use specific failure type instead of generic exception

Throwing a generic Exception when the user is not authenticated can make error handling less precise. Consider using a specific AuthenticationFailure or emitting an appropriate error state to enhance error granularity.

Update the code as follows:

         if (userId == null) {
-          throw Exception('User not authenticated');
+          emit(AddressError('User not authenticated'));
+          return;
         }

Likely invalid or redundant comment.

lib/features/profile/presentation/screens/profile_screen.dart (2)

89-102: Ensure the UI updates if the profile changes during the session

Currently, the controllers are initialized only once. If the user's profile is updated elsewhere and a new ProfileLoaded state is emitted, the UI may not reflect the updated data.

Consider removing the _isInitialized check to update the controllers whenever a new ProfileLoaded state is received.


39-39: Ensure logUserRetention is properly implemented and imported

The logUserRetention function is called, but it's not clear if it's correctly implemented or imported. Ensure that this function is defined in analytics_service.dart and that it operates as expected.

Run the following script to verify that logUserRetention is defined:

✅ Verification successful

logUserRetention is properly implemented and imported

The function is correctly implemented in analytics_service.dart and properly imported in the profile screen:

  • Defined in core/utils/analytics_service.dart as an async function that logs to Firebase Analytics
  • Correctly imported in profile screen via import 'package:eco_bites/core/utils/analytics_service.dart'
  • Implementation matches the usage, accepting userId and days parameters
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the existence and implementation of `logUserRetention` function.

# Test: Search for the definition of `logUserRetention`. Expect: The function definition is found.
rg -A 5 'void\s+logUserRetention\('

Length of output: 37


Script:

#!/bin/bash
# Let's try different search patterns and locations to find the function

# Search for any occurrence of logUserRetention
rg "logUserRetention" -A 3

# Search specifically in analytics related files
fd "analytics.*\.dart$" --exec rg "logUserRetention" {} -A 3

# Search for the import statement in the profile screen
rg "import.*analytics" "lib/features/profile/presentation/screens/profile_screen.dart"

Length of output: 1106

abstract class AddressRepository {
Future<Either<Failure, void>> saveAddress(String userId, Address address);
Future<Either<Failure, List<Address>>> fetchUserAddresses(String userId);
Future<Either<Failure, Address?>> fetchUserAddress(String userId);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codebase verification

Inconsistency found in address handling implementation

There's a mismatch between the single address architecture and the actual implementation:

  • The AddressRepository correctly uses single address with Address?
  • However, ProfileRepository still implements multiple addresses:
    • Uses a subcollection 'addresses'
    • Has getUserAddresses() returning List<AddressModel>
    • Has saveUserAddress() that adds to addresses collection

This needs to be aligned with the single address approach by:

  1. Removing the addresses subcollection approach
  2. Updating ProfileRepository to handle single address
  3. Ensuring consistency with AddressRepository implementation
🔗 Analysis chain

Verify the impact of switching from multiple addresses to single address.

The change from List<Address> to Address? aligns with the PR objective of managing a single address. The nullable return type appropriately handles cases where a user has no address.

Let's verify this architectural change is consistently applied across the codebase:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that we're not mixing single/multiple address handling
# Test 1: Check for any remaining references to multiple addresses
rg -i "addresses" --type dart

# Test 2: Check for proper null handling in address-related files
rg "Address\?" --type dart

# Test 3: Look for any address-related UI files that might need updates
fd -e dart -e yaml . | rg -i "address.*screen|address.*page|address.*view"

Length of output: 2170


Script:

#!/bin/bash
# Let's check the implementation of fetch_user_addresses_usecase.dart since it appears in imports
cat lib/features/address/domain/usecases/fetch_user_addresses_usecase.dart

# Also check the address_bloc.dart to see how addresses are handled
cat lib/features/address/presentation/bloc/address_bloc.dart

# Check the profile repository implementation for addresses collection usage
cat lib/features/profile/domain/repositories/profile_repository.dart

Length of output: 7571

Comment on lines +9 to +11
Future<void> call(UserProfile userProfile) async {
await repository.updateUserProfile(userProfile);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling and input validation.

The current implementation lacks error handling and input validation. Given the PR's focus on Firebase integration and error handling improvements, consider:

  1. Adding error handling using the new ServerFailure class
  2. Validating the UserProfile input
  3. Integrating with FirebaseCrashlytics for error reporting
-  Future<void> call(UserProfile userProfile) async {
-    await repository.updateUserProfile(userProfile);
+  Future<Either<Failure, void>> call(UserProfile userProfile) async {
+    try {
+      // Validate input
+      if (userProfile.userId.isEmpty) {
+        return Left(ValidationFailure('User ID cannot be empty'));
+      }
+
+      await repository.updateUserProfile(userProfile);
+      return const Right(null);
+    } catch (e, stackTrace) {
+      // Log to Firebase Crashlytics
+      await FirebaseCrashlytics.instance.recordError(
+        e,
+        stackTrace,
+        reason: 'Error updating user profile',
+      );
+      return Left(ServerFailure('Failed to update profile'));
+    }
   }

Don't forget to add these imports:

import 'package:dartz/dartz.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:eco_bites/core/error/failures.dart';

Comment on lines +11 to +17
Future<UserProfile?> call(String userId) async {
final Either<ServerFailure, UserProfile?> result = await repository.fetchUserProfile(userId);
return result.fold(
(ServerFailure failure) => null,
(UserProfile? userProfile) => userProfile,
);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider improving error handling and adding input validation.

The current implementation has several areas for improvement:

  1. Silently returning null on failure loses valuable error context
  2. Missing input validation for userId
  3. No analytics tracking (mentioned in PR objectives)

Consider this improved implementation:

+  /// Fetches a user profile for the given user ID.
+  /// 
+  /// Returns null if the profile doesn't exist or if an error occurs.
+  /// Logs analytics events for successful and failed attempts.
   Future<UserProfile?> call(String userId) async {
+    if (userId.isEmpty) {
+      throw ArgumentError('userId cannot be empty');
+    }
+
     final Either<ServerFailure, UserProfile?> result = await repository.fetchUserProfile(userId);
     return result.fold(
-      (ServerFailure failure) => null,
+      (ServerFailure failure) {
+        // Log failure to analytics
+        return null;
+      },
       (UserProfile? userProfile) => userProfile,
     );
   }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +15 to +20
required String name,
required String surname,
required String citizenId,
required String phone,
required DateTime birthDate,
required CuisineType favoriteCuisine,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codebase verification

⚠️ Potential issue

Validation and data security measures needed for sensitive information

Based on the code analysis, there are several security and validation concerns that need to be addressed:

  1. No validation is implemented for any of the fields - the registration form uses basic TextField instead of TextFormField with validators
  2. Sensitive data (citizenId, phone) is being sent to the server without any encryption or secure handling
  3. Birth date validation only prevents future dates but lacks minimum age verification
  4. No format validation for phone numbers and citizen IDs

Required changes:

  • Replace TextField with TextFormField and implement proper validators
  • Add encryption for sensitive data before transmission
  • Implement proper format validation for phone and citizen ID
  • Add minimum age validation for birth date
  • Consider moving sensitive data handling to a secure storage solution
🔗 Analysis chain

Verify data handling and validation requirements.

The expanded user profile collection raises several important considerations:

  1. The citizenId parameter contains sensitive PII data. Ensure proper encryption and compliance with data protection regulations.
  2. Consider adding validation for:
    • Phone number format
    • Birth date (e.g., minimum age requirements)
    • Citizen ID format

Would you like me to help implement the validation logic for these fields?


Let me gather more information about validation and data handling in the codebase.


Let me check the user entity and look for any form validation in the signup screen.


Let me check the user utilities and request utilities for any validation or data handling.


Let me check the register screen implementation for any existing validation.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if there's encryption handling for sensitive data
rg -A 5 "citizenId|phone" lib/features/auth/data/

# Look for validation implementations
ast-grep --pattern 'validate$_($$$)' lib/features/auth/

Length of output: 1381


Script:

#!/bin/bash
# Check for validation utilities or helpers
rg -g '*.dart' -B 2 -A 2 'validate|Validator|encryption|encrypt' lib/

# Look for models/entities that might contain validation logic
fd -e dart . lib/features/auth/domain/entities
fd -e dart . lib/features/auth/domain/value_objects

# Check for any existing test cases that might reveal validation patterns
fd -e dart . lib/features/auth/test

Length of output: 952


Script:

#!/bin/bash
# Check user entity implementation
cat lib/features/auth/domain/entities/user.dart

# Look for signup form validation
rg -g '*.dart' -B 3 -A 3 'signup|register' lib/features/auth/presentation/

# Check for any form validators or utilities
fd -e dart . lib/core/

Length of output: 1870


Script:

#!/bin/bash
# Check user and request utilities
cat lib/core/utils/user_util.dart
cat lib/core/utils/request_util.dart

# Look for any validators in core
rg -g '*.dart' 'validator|validate|encryption|secure' lib/core/

# Check signup screen implementation
fd -g '*signup*.dart' . lib/features/auth/presentation/
fd -g '*register*.dart' . lib/features/auth/presentation/

Length of output: 1286


Script:

#!/bin/bash
# Check register screen implementation
cat lib/features/auth/presentation/screens/register_screen.dart

# Look for any validation patterns in the entire auth feature
rg -g '*.dart' 'TextFormField|validator:|String\?' lib/features/auth/

Length of output: 9701

Comment on lines +14 to +18
class LoadProfileEvent extends ProfileEvent {

const LoadProfileEvent(this.userId);
final String userId;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add props override to ensure proper equality comparison.

The LoadProfileEvent class should override the props getter to include userId in equality comparisons. Without this, two events with different userId values would be considered equal.

Apply this diff to fix the issue:

 class LoadProfileEvent extends ProfileEvent {
   const LoadProfileEvent(this.userId);
   final String userId;
+
+  @override
+  List<Object> get props => <Object>[userId];
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
class LoadProfileEvent extends ProfileEvent {
const LoadProfileEvent(this.userId);
final String userId;
}
class LoadProfileEvent extends ProfileEvent {
const LoadProfileEvent(this.userId);
final String userId;
@override
List<Object> get props => <Object>[userId];
}

Comment on lines +60 to +77
Future<void> _checkAndSaveCachedProfile() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
final String? cachedProfileString = prefs.getString('cachedProfile');
if (cachedProfileString != null) {
final Map<String, dynamic> cachedProfileMap = jsonDecode(cachedProfileString);
final UserProfile cachedProfile = UserProfile.fromMap(cachedProfileMap);
try {
await updateUserProfileUseCase(cachedProfile);
await prefs.remove('cachedProfile');
final UserProfile? updatedProfile = await fetchUserProfileUseCase(cachedProfile.userId);
if (updatedProfile != null) {
emit(ProfileLoaded(updatedProfile, isUpdated: true));
}
} catch (e) {
emit(ProfileError('Failed to update cached profile'));
}
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Ensure _checkAndSaveCachedProfile() is invoked when internet connectivity is restored.

The _checkAndSaveCachedProfile() method is designed to synchronize cached profile data when the device regains internet connectivity. However, it appears that this method is not currently being called. To ensure that cached profiles are properly updated, consider subscribing to the InternetConnectionBloc and invoking _checkAndSaveCachedProfile() when the connection state changes to connected.

You can add a listener in the constructor to handle connectivity changes:

// Add this in the constructor after initializing super(...)
_internetConnectionBloc.stream.listen((state) {
  if (state is ConnectedInternet) {
    _checkAndSaveCachedProfile();
  }
});

Comment on lines +54 to +59
required String name,
required String surname,
required String citizenId,
required String phone,
required DateTime birthDate,
required CuisineType favoriteCuisine,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider adding input validation for new sign-up parameters

The new parameters added to the signUp method (name, surname, citizenId, phone, birthDate, and favoriteCuisine) should be validated to ensure data integrity and prevent incorrect or malicious data from being stored in Firestore.

Consider adding validation checks before storing the data. For example:

if (name.isEmpty ||
    surname.isEmpty ||
    !isValidCitizenId(citizenId) ||
    !isValidPhoneNumber(phone) ||
    birthDate.isAfter(DateTime.now())) {
  return const Left<Failure, User>(ValidationFailure('Invalid user input'));
}

Comment on lines +71 to +79
await firestore.collection('profiles').doc(user.id).set(<String, dynamic>{
'name': name,
'surname': surname,
'citizenId': citizenId,
'phone': phone,
'birthDate': birthDate.toIso8601String(),
'email': email,
'favoriteCuisine': favoriteCuisine.name,
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure secure handling of sensitive user data

Storing sensitive information like citizenId, phone, and birthDate requires careful handling to comply with data protection regulations and to protect user privacy. Ensure that this data is stored securely, with appropriate encryption and access controls.

Consider reviewing your data storage policies and implement encryption or anonymization where necessary.

Comment on lines +87 to +88
} catch (e) {
return const Left<Failure, User>(ServerFailure('Failed to save user profile'));
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid catching broad exceptions

Using a general catch (e) statement can obscure specific exceptions and make debugging more difficult. It's better to catch specific exceptions related to Firestore operations to handle different failure scenarios appropriately.

Replace with specific exception handling:

} on AuthException catch (e) {
  return Left<Failure, User>(AuthFailure(e.message));
} on FirebaseException catch (e) {
  return const Left<Failure, User>(ServerFailure('Failed to save user profile'));
}

Comment on lines +46 to +47
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString('cachedProfile', updatedProfile.toMap().toString());
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use JSON encoding when saving profile data to SharedPreferences

When caching the profile data, serializing it using JSON ensures proper encoding and decoding, especially if the profile data contains nested structures or special characters.

Apply this diff to serialize and store the profile data as JSON:

+import 'dart:convert';

...

await prefs.setString('cachedProfile', jsonEncode(updatedProfile.toMap()));

Committable suggestion skipped: line range outside the PR's diff.

Copy link
Copy Markdown
Contributor

@abelarismendy abelarismendy left a comment

Choose a reason for hiding this comment

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

LGTM

@RaulRinconX RaulRinconX merged commit e999005 into main Nov 5, 2024
@RaulRinconX RaulRinconX linked an issue Nov 5, 2024 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

Status: DONE

Development

Successfully merging this pull request may close these issues.

feature/app-performance-retention

2 participants