From ad994785757c3f467e81de9e6f33cd9d442ef14a Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 10:32:07 +0100 Subject: [PATCH 01/44] feat(l10n): integrate ht_ui_kit localizations Adds the HtUiKitLocalizations delegate to the MaterialApp configuration. This makes the shared, localized error strings from the UI kit package available throughout the dashboard application, preparing it for the refactoring of error handling logic. --- lib/app/view/app.dart | 9 ++++++--- pubspec.lock | 11 ++++++++++- pubspec.yaml | 3 +++ 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/lib/app/view/app.dart b/lib/app/view/app.dart index af2ec3f8..e2aa6582 100644 --- a/lib/app/view/app.dart +++ b/lib/app/view/app.dart @@ -13,6 +13,7 @@ import 'package:ht_dashboard/authentication/bloc/authentication_bloc.dart'; import 'package:ht_dashboard/content_management/bloc/content_management_bloc.dart'; import 'package:ht_dashboard/dashboard/bloc/dashboard_bloc.dart'; import 'package:ht_dashboard/l10n/app_localizations.dart'; +import 'package:ht_ui_kit/ht_ui_kit.dart'; import 'package:ht_dashboard/router/router.dart'; // Import for app_theme.dart import 'package:ht_dashboard/shared/theme/app_theme.dart'; @@ -209,9 +210,11 @@ class _AppViewState extends State<_AppView> { child: MaterialApp.router( debugShowCheckedModeBanner: false, routerConfig: _router, - localizationsDelegates: - AppLocalizations.localizationsDelegates, - supportedLocales: AppLocalizations.supportedLocales, + localizationsDelegates: const [ + HtUiKitLocalizations.delegate, + ...AppLocalizations.localizationsDelegates, + ], + supportedLocales: HtUiKitLocalizations.supportedLocales, theme: baseTheme == AppBaseTheme.dark ? darkThemeData : lightThemeData, diff --git a/pubspec.lock b/pubspec.lock index d8172532..e621731b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -277,7 +277,7 @@ packages: description: path: "." ref: HEAD - resolved-ref: "0b56d92624769ca3175d5ce2c7da27ab29514f8a" + resolved-ref: "6484a5641c3d633d286ea5848c5b7cf1e723ebc1" url: "https://github.com/headlines-toolkit/ht-http-client.git" source: git version: "0.0.0" @@ -308,6 +308,15 @@ packages: url: "https://github.com/headlines-toolkit/ht-shared.git" source: git version: "0.0.0" + ht_ui_kit: + dependency: "direct main" + description: + path: "." + ref: HEAD + resolved-ref: c33ec118041a9de02c96b177a88d646a08abd396 + url: "https://github.com/headlines-toolkit/ht-ui-kit.git" + source: git + version: "0.0.0" http: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index f47610d2..4699a585 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -57,6 +57,9 @@ dependencies: ht_shared: git: url: https://github.com/headlines-toolkit/ht-shared.git + ht_ui_kit: + git: + url: https://github.com/headlines-toolkit/ht-ui-kit.git intl: ^0.20.2 logging: ^1.3.0 timeago: ^3.7.1 From 7ce06ee6c5ee9bb04fe4fb3474a3d31205a00ada Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 10:33:58 +0100 Subject: [PATCH 02/44] refactor(auth): use localized exceptions in auth UI Updates the authentication pages (`authentication_page`, `request_code_page`, `email_code_verification_page`) to display localized error messages. - Modifies the `BlocConsumer` listeners to use the `exception` field from the `AuthenticationState`. - Uses the `toFriendlyMessage(context)` extension method from the `ht_ui_kit` package to render user-friendly error messages in SnackBars. --- lib/authentication/view/authentication_page.dart | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/authentication/view/authentication_page.dart b/lib/authentication/view/authentication_page.dart index a3727e12..475ac98b 100644 --- a/lib/authentication/view/authentication_page.dart +++ b/lib/authentication/view/authentication_page.dart @@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import 'package:ht_dashboard/authentication/bloc/authentication_bloc.dart'; import 'package:ht_dashboard/l10n/l10n.dart'; +import 'package:ht_ui_kit/ht_ui_kit.dart'; import 'package:ht_dashboard/router/routes.dart'; import 'package:ht_dashboard/shared/constants/app_spacing.dart'; @@ -31,15 +32,13 @@ class AuthenticationPage extends StatelessWidget { child: BlocConsumer( // Listener remains crucial for feedback (errors) listener: (context, state) { - if (state.status == AuthenticationStatus.failure) { + if (state.status == AuthenticationStatus.failure && + state.exception != null) { ScaffoldMessenger.of(context) ..hideCurrentSnackBar() ..showSnackBar( SnackBar( - content: Text( - // Provide a more user-friendly error message if possible - state.errorMessage!, - ), + content: Text(state.exception!.toFriendlyMessage(context)), backgroundColor: colorScheme.error, ), ); From c5552d8a667c27792132754f15b9b4bf351cb905 Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 10:48:47 +0100 Subject: [PATCH 03/44] feat(authentication): update state to use exception object Refactors `AuthenticationState` to replace the `errorMessage` string with a `HtHttpException` object named `exception`. This change is necessary to support the new `ht_ui_kit` package, which provides a `toFriendlyMessage` extension on `HtHttpException` for displaying localized error messages. Storing the full exception object in the state allows the UI layer to access it directly and use the extension, ensuring consistent and localized error handling. --- lib/authentication/bloc/authentication_state.dart | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/authentication/bloc/authentication_state.dart b/lib/authentication/bloc/authentication_state.dart index f4ba12bb..b12250e8 100644 --- a/lib/authentication/bloc/authentication_state.dart +++ b/lib/authentication/bloc/authentication_state.dart @@ -35,7 +35,7 @@ final class AuthenticationState extends Equatable { this.status = AuthenticationStatus.initial, this.user, this.email, - this.errorMessage, + this.exception, }); /// The current status of the authentication process. @@ -47,11 +47,11 @@ final class AuthenticationState extends Equatable { /// The email address involved in the current authentication flow. final String? email; - /// The error message describing an authentication failure, if any. - final String? errorMessage; + /// The error describing an authentication failure, if any. + final HtHttpException? exception; @override - List get props => [status, user, email, errorMessage]; + List get props => [status, user, email, exception]; /// Creates a copy of this [AuthenticationState] with the given fields /// replaced with the new values. @@ -59,13 +59,13 @@ final class AuthenticationState extends Equatable { AuthenticationStatus? status, User? user, String? email, - String? errorMessage, + HtHttpException? exception, }) { return AuthenticationState( status: status ?? this.status, user: user ?? this.user, email: email ?? this.email, - errorMessage: errorMessage ?? this.errorMessage, + exception: exception ?? this.exception, ); } } From c2935df559736cd31dd4698c5275edb6ae953e57 Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 10:48:56 +0100 Subject: [PATCH 04/44] feat(authentication): adapt bloc to handle HtHttpException Updates `AuthenticationBloc` to handle and emit `HtHttpException` objects instead of raw error strings. - All `catch` blocks now store the caught exception in the `exception` field of the `AuthenticationState`. - The manual email validation check is removed, as this logic is now handled by the `InvalidInputException` from the repository layer. - Generic `catch (e)` blocks now wrap the error in a `UnknownException` to ensure all failures are propagated as a standard exception type. --- .../bloc/authentication_bloc.dart | 73 +++++++------------ 1 file changed, 25 insertions(+), 48 deletions(-) diff --git a/lib/authentication/bloc/authentication_bloc.dart b/lib/authentication/bloc/authentication_bloc.dart index 586f1963..9e951855 100644 --- a/lib/authentication/bloc/authentication_bloc.dart +++ b/lib/authentication/bloc/authentication_bloc.dart @@ -3,17 +3,7 @@ import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:ht_auth_repository/ht_auth_repository.dart'; -import 'package:ht_shared/ht_shared.dart' show - AuthenticationException, - ForbiddenException, - HtHttpException, - InvalidInputException, - NetworkException, - NotFoundException, - OperationFailedException, - ServerException, - UnauthorizedException, - User; +import 'package:ht_shared/ht_shared.dart' show AuthenticationException, ForbiddenException, HtHttpException, InvalidInputException, NetworkException, NotFoundException, OperationFailedException, ServerException, UnauthorizedException, UnknownException, User; part 'authentication_event.dart'; part 'authentication_state.dart'; @@ -70,16 +60,6 @@ class AuthenticationBloc AuthenticationRequestSignInCodeRequested event, Emitter emit, ) async { - // Validate email format (basic check) - if (event.email.isEmpty || !event.email.contains('@')) { - emit( - state.copyWith( - status: AuthenticationStatus.failure, - errorMessage: 'Please enter a valid email address.', - ), - ); - return; - } emit(state.copyWith(status: AuthenticationStatus.requestCodeLoading)); try { await _authenticationRepository.requestSignInCode( @@ -96,53 +76,50 @@ class AuthenticationBloc emit( state.copyWith( status: AuthenticationStatus.failure, - errorMessage: 'Invalid input: ${e.message}', + exception: e, ), ); } on UnauthorizedException catch (e) { emit( state.copyWith( status: AuthenticationStatus.failure, - errorMessage: e.message, + exception: e, ), ); } on ForbiddenException catch (e) { emit( state.copyWith( status: AuthenticationStatus.failure, - errorMessage: e.message, + exception: e, ), ); - } on NetworkException catch (_) { + } on NetworkException catch (e) { emit( state.copyWith( status: AuthenticationStatus.failure, - errorMessage: 'Network error occurred.', + exception: e, ), ); } on ServerException catch (e) { emit( state.copyWith( status: AuthenticationStatus.failure, - errorMessage: 'Server error: ${e.message}', + exception: e, ), ); } on OperationFailedException catch (e) { emit( state.copyWith( status: AuthenticationStatus.failure, - errorMessage: 'Operation failed: ${e.message}', + exception: e, ), ); } on HtHttpException catch (e) { // Catch any other HtHttpException subtypes - final message = e.message.isNotEmpty - ? e.message - : 'An unspecified HTTP error occurred.'; emit( state.copyWith( status: AuthenticationStatus.failure, - errorMessage: 'HTTP error: $message', + exception: e, ), ); } catch (e) { @@ -150,7 +127,7 @@ class AuthenticationBloc emit( state.copyWith( status: AuthenticationStatus.failure, - errorMessage: 'An unexpected error occurred: $e', + exception: UnknownException('An unexpected error occurred: $e'), ), ); // Optionally log the stackTrace here @@ -175,42 +152,42 @@ class AuthenticationBloc emit( state.copyWith( status: AuthenticationStatus.failure, - errorMessage: e.message, + exception: e, ), ); } on AuthenticationException catch (e) { emit( state.copyWith( status: AuthenticationStatus.failure, - errorMessage: e.message, + exception: e, ), ); } on NotFoundException catch (e) { emit( state.copyWith( status: AuthenticationStatus.failure, - errorMessage: e.message, + exception: e, ), ); - } on NetworkException catch (_) { + } on NetworkException catch (e) { emit( state.copyWith( status: AuthenticationStatus.failure, - errorMessage: 'Network error occurred.', + exception: e, ), ); } on ServerException catch (e) { emit( state.copyWith( status: AuthenticationStatus.failure, - errorMessage: 'Server error: ${e.message}', + exception: e, ), ); } on OperationFailedException catch (e) { emit( state.copyWith( status: AuthenticationStatus.failure, - errorMessage: 'Operation failed: ${e.message}', + exception: e, ), ); } on HtHttpException catch (e) { @@ -218,7 +195,7 @@ class AuthenticationBloc emit( state.copyWith( status: AuthenticationStatus.failure, - errorMessage: 'HTTP error: ${e.message}', + exception: e, ), ); } catch (e) { @@ -226,7 +203,7 @@ class AuthenticationBloc emit( state.copyWith( status: AuthenticationStatus.failure, - errorMessage: 'An unexpected error occurred: $e', + exception: UnknownException('An unexpected error occurred: $e'), ), ); // Optionally log the stackTrace here @@ -243,25 +220,25 @@ class AuthenticationBloc await _authenticationRepository.signOut(); // On success, the _AuthenticationStatusChanged listener will handle // emitting AuthenticationUnauthenticated. - } on NetworkException catch (_) { + } on NetworkException catch (e) { emit( state.copyWith( status: AuthenticationStatus.failure, - errorMessage: 'Network error occurred.', + exception: e, ), ); } on ServerException catch (e) { emit( state.copyWith( status: AuthenticationStatus.failure, - errorMessage: 'Server error: ${e.message}', + exception: e, ), ); } on OperationFailedException catch (e) { emit( state.copyWith( status: AuthenticationStatus.failure, - errorMessage: 'Operation failed: ${e.message}', + exception: e, ), ); } on HtHttpException catch (e) { @@ -269,14 +246,14 @@ class AuthenticationBloc emit( state.copyWith( status: AuthenticationStatus.failure, - errorMessage: 'HTTP error: ${e.message}', + exception: e, ), ); } catch (e) { emit( state.copyWith( status: AuthenticationStatus.failure, - errorMessage: 'An unexpected error occurred: $e', + exception: UnknownException('An unexpected error occurred: $e'), ), ); } From 99ef00e5feb31435eb35846d34ac2203765f3da7 Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 10:49:05 +0100 Subject: [PATCH 05/44] feat(authentication): adapt bloc to handle HtHttpException Updates `AuthenticationBloc` to handle and emit `HtHttpException` objects instead of raw error strings. - All `catch` blocks now store the caught exception in the `exception` field of the `AuthenticationState`. - The manual email validation check is removed, as this logic is now handled by the `InvalidInputException` from the repository layer. - Generic `catch (e)` blocks now wrap the error in a `UnknownException` to ensure all failures are propagated as a standard exception type. --- lib/authentication/view/email_code_verification_page.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/authentication/view/email_code_verification_page.dart b/lib/authentication/view/email_code_verification_page.dart index 1635a597..67d81db8 100644 --- a/lib/authentication/view/email_code_verification_page.dart +++ b/lib/authentication/view/email_code_verification_page.dart @@ -5,6 +5,7 @@ import 'package:ht_dashboard/app/bloc/app_bloc.dart'; import 'package:ht_dashboard/app/config/config.dart'; import 'package:ht_dashboard/authentication/bloc/authentication_bloc.dart'; import 'package:ht_dashboard/l10n/l10n.dart'; +import 'package:ht_ui_kit/ht_ui_kit.dart'; import 'package:ht_dashboard/shared/constants/app_spacing.dart'; /// {@template email_code_verification_page} @@ -29,12 +30,13 @@ class EmailCodeVerificationPage extends StatelessWidget { body: SafeArea( child: BlocConsumer( listener: (context, state) { - if (state.status == AuthenticationStatus.failure) { + if (state.status == AuthenticationStatus.failure && + state.exception != null) { ScaffoldMessenger.of(context) ..hideCurrentSnackBar() ..showSnackBar( SnackBar( - content: Text(state.errorMessage!), + content: Text(state.exception!.toFriendlyMessage(context)), backgroundColor: colorScheme.error, ), ); From bd710d1fb0feb3909be47d6b82705befb7d39cbb Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 10:50:30 +0100 Subject: [PATCH 06/44] feat(authentication): refactor request code page to use localized errors Updates the `RequestCodePage` to use the `toFriendlyMessage` extension from `ht_ui_kit`. The `BlocConsumer` now accesses the `exception` object from the `AuthenticationState` and passes it to the extension method to display a user-friendly, localized error message in the `SnackBar`. The `buildWhen` condition is also updated to react to changes in the `exception` field. --- lib/authentication/view/request_code_page.dart | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/authentication/view/request_code_page.dart b/lib/authentication/view/request_code_page.dart index ca9116bd..25b67577 100644 --- a/lib/authentication/view/request_code_page.dart +++ b/lib/authentication/view/request_code_page.dart @@ -10,6 +10,7 @@ import 'package:ht_dashboard/authentication/bloc/authentication_bloc.dart'; import 'package:ht_dashboard/l10n/l10n.dart'; import 'package:ht_dashboard/router/routes.dart'; import 'package:ht_dashboard/shared/constants/app_spacing.dart'; +import 'package:ht_ui_kit/ht_ui_kit.dart'; /// {@template request_code_page} /// Page for initiating the email code sign-in process. @@ -70,12 +71,13 @@ class _RequestCodeView extends StatelessWidget { body: SafeArea( child: BlocConsumer( listener: (context, state) { - if (state.status == AuthenticationStatus.failure) { + if (state.status == AuthenticationStatus.failure && + state.exception != null) { ScaffoldMessenger.of(context) ..hideCurrentSnackBar() ..showSnackBar( SnackBar( - content: Text(state.errorMessage!), + content: Text(state.exception!.toFriendlyMessage(context)), backgroundColor: colorScheme.error, ), ); @@ -92,7 +94,7 @@ class _RequestCodeView extends StatelessWidget { // BuildWhen prevents unnecessary rebuilds if only listening buildWhen: (previous, current) => previous.status != current.status || - previous.errorMessage != current.errorMessage || + previous.exception != current.exception || previous.email != current.email, builder: (context, state) { final isLoading = From ae27301c2f2eb6aa3adab2898a5b0653baae5112 Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 11:28:31 +0100 Subject: [PATCH 07/44] feat(authentication): refactor auth page to use localized errors Updates the `AuthenticationPage` to use the `toFriendlyMessage` extension from `ht_ui_kit`. The `BlocConsumer` now accesses the `exception` object from the `AuthenticationState` and passes it to the extension method to display a user-friendly, localized error message in the `SnackBar`. A `debugPrint` statement was temporarily added to diagnose a context-related issue with the localization, which is now resolved. --- lib/authentication/view/authentication_page.dart | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/authentication/view/authentication_page.dart b/lib/authentication/view/authentication_page.dart index 475ac98b..0d3c9374 100644 --- a/lib/authentication/view/authentication_page.dart +++ b/lib/authentication/view/authentication_page.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; +import 'package:flutter/foundation.dart'; import 'package:ht_dashboard/authentication/bloc/authentication_bloc.dart'; import 'package:ht_dashboard/l10n/l10n.dart'; import 'package:ht_ui_kit/ht_ui_kit.dart'; @@ -34,11 +35,14 @@ class AuthenticationPage extends StatelessWidget { listener: (context, state) { if (state.status == AuthenticationStatus.failure && state.exception != null) { + final friendlyMessage = state.exception!.toFriendlyMessage( + context, + ); ScaffoldMessenger.of(context) ..hideCurrentSnackBar() ..showSnackBar( SnackBar( - content: Text(state.exception!.toFriendlyMessage(context)), + content: Text(friendlyMessage), backgroundColor: colorScheme.error, ), ); @@ -49,7 +53,8 @@ class AuthenticationPage extends StatelessWidget { // email flow pages. }, builder: (context, state) { - final isLoading = state.status == AuthenticationStatus.loading || + final isLoading = + state.status == AuthenticationStatus.loading || state.status == AuthenticationStatus.requestCodeLoading; return Padding( From 2094a7d6ff7128ec97a8f77798b5f8f8ba008092 Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 11:39:29 +0100 Subject: [PATCH 08/44] feat(dashboard): Update Dashboard State to handle HtHttpException --- lib/dashboard/bloc/dashboard_state.dart | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/dashboard/bloc/dashboard_state.dart b/lib/dashboard/bloc/dashboard_state.dart index 7f371997..7d0f19a7 100644 --- a/lib/dashboard/bloc/dashboard_state.dart +++ b/lib/dashboard/bloc/dashboard_state.dart @@ -22,37 +22,37 @@ final class DashboardState extends Equatable { this.summary, this.appConfig, this.recentHeadlines = const [], - this.errorMessage, + this.exception, }); final DashboardStatus status; final DashboardSummary? summary; final RemoteConfig? appConfig; final List recentHeadlines; - final String? errorMessage; + final HtHttpException? exception; DashboardState copyWith({ DashboardStatus? status, DashboardSummary? summary, RemoteConfig? appConfig, List? recentHeadlines, - String? errorMessage, + HtHttpException? exception, }) { return DashboardState( status: status ?? this.status, summary: summary ?? this.summary, appConfig: appConfig ?? this.appConfig, recentHeadlines: recentHeadlines ?? this.recentHeadlines, - errorMessage: errorMessage ?? this.errorMessage, + exception: exception ?? this.exception, ); } @override List get props => [ - status, - summary, - appConfig, - recentHeadlines, - errorMessage, - ]; + status, + summary, + appConfig, + recentHeadlines, + exception, + ]; } From e85579e91d16ea8ef006c26947452004d0e5f22e Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 11:39:32 +0100 Subject: [PATCH 09/44] feat(dashboard): Update DashboardBloc to emit HtHttpException --- lib/dashboard/bloc/dashboard_bloc.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/dashboard/bloc/dashboard_bloc.dart b/lib/dashboard/bloc/dashboard_bloc.dart index 7a3e6cfb..1857da4f 100644 --- a/lib/dashboard/bloc/dashboard_bloc.dart +++ b/lib/dashboard/bloc/dashboard_bloc.dart @@ -60,14 +60,14 @@ class DashboardBloc extends Bloc { emit( state.copyWith( status: DashboardStatus.failure, - errorMessage: e.message, + exception: e, ), ); } catch (e) { emit( state.copyWith( status: DashboardStatus.failure, - errorMessage: 'An unknown error occurred: $e', + exception: UnknownException('An unknown error occurred: $e'), ), ); } From 49f1f03a016c8772b74e6904b30013eace48a5b4 Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 11:40:10 +0100 Subject: [PATCH 10/44] feat(shared): Refactor FailureStateWidget to accept HtHttpException --- lib/main.dart | 2 +- lib/shared/widgets/failure_state_widget.dart | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 34b787b4..d42b6c68 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -6,7 +6,7 @@ import 'package:ht_dashboard/app/config/config.dart'; import 'package:ht_dashboard/bootstrap.dart'; // Define the current application environment (production/development/demo). -const AppEnvironment appEnvironment = AppEnvironment.demo; +const AppEnvironment appEnvironment = AppEnvironment.development; @JS('removeSplashFromWeb') external void removeSplashFromWeb(); diff --git a/lib/shared/widgets/failure_state_widget.dart b/lib/shared/widgets/failure_state_widget.dart index 4bea388a..30f8ad11 100644 --- a/lib/shared/widgets/failure_state_widget.dart +++ b/lib/shared/widgets/failure_state_widget.dart @@ -1,22 +1,24 @@ import 'package:flutter/material.dart'; +import 'package:ht_shared/ht_shared.dart'; +import 'package:ht_ui_kit/ht_ui_kit.dart'; /// A widget to display an error message and an optional retry button. class FailureStateWidget extends StatelessWidget { /// Creates a [FailureStateWidget]. /// - /// The [message] is the error message to display. + /// The [exception] is the error exception to display. /// /// The [onRetry] is an optional callback to be called /// when the retry button is pressed. const FailureStateWidget({ - required this.message, + required this.exception, super.key, this.onRetry, this.retryButtonText, }); - /// The error message to display. - final String message; + /// The error exception to display. + final HtHttpException exception; /// An optional callback to be called when the retry button is pressed. final VoidCallback? onRetry; @@ -26,12 +28,13 @@ class FailureStateWidget extends StatelessWidget { @override Widget build(BuildContext context) { + final friendlyMessage = exception.toFriendlyMessage(context); return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - message, + friendlyMessage, style: Theme.of(context).textTheme.bodyMedium, textAlign: TextAlign.center, ), From 1fa01badd28280289cd62692893c44e0742220d1 Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 11:40:41 +0100 Subject: [PATCH 11/44] feat(dashboard): Update DashboardPage to pass HtHttpException to FailureStateWidget --- lib/dashboard/view/dashboard_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dashboard/view/dashboard_page.dart b/lib/dashboard/view/dashboard_page.dart index 418b1f2d..83df1da4 100644 --- a/lib/dashboard/view/dashboard_page.dart +++ b/lib/dashboard/view/dashboard_page.dart @@ -43,7 +43,7 @@ class _DashboardPageState extends State { } if (state.status == DashboardStatus.failure) { return FailureStateWidget( - message: state.errorMessage ?? l10n.dashboardLoadFailure, + exception: state.exception!, onRetry: () { context.read().add(DashboardSummaryLoaded()); }, From e482de27eafc46b08190ed43619b18b5b5580800 Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 11:43:48 +0100 Subject: [PATCH 12/44] feat(app_config): Refactor AppConfigurationState to use HtHttpException (part 1) --- lib/app_configuration/bloc/app_configuration_state.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/app_configuration/bloc/app_configuration_state.dart b/lib/app_configuration/bloc/app_configuration_state.dart index 52f6dd6c..87393da5 100644 --- a/lib/app_configuration/bloc/app_configuration_state.dart +++ b/lib/app_configuration/bloc/app_configuration_state.dart @@ -38,8 +38,8 @@ class AppConfigurationState extends Equatable { /// The original application configuration loaded from the backend. final RemoteConfig? originalRemoteConfig; - /// An error message if an operation failed. - final String? errorMessage; + /// An error exception if an operation failed. + final HtHttpException? exception; /// Indicates if there are unsaved changes to the configuration. final bool isDirty; From 99bfb968a2df838bf35c8e7440f600f8b5b553c7 Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 11:46:46 +0100 Subject: [PATCH 13/44] refactor(app_config): Refactor AppConfigurationState to use HtHttpException --- .../bloc/app_configuration_state.dart | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/app_configuration/bloc/app_configuration_state.dart b/lib/app_configuration/bloc/app_configuration_state.dart index 87393da5..fe67cc57 100644 --- a/lib/app_configuration/bloc/app_configuration_state.dart +++ b/lib/app_configuration/bloc/app_configuration_state.dart @@ -24,7 +24,7 @@ class AppConfigurationState extends Equatable { this.status = AppConfigurationStatus.initial, this.remoteConfig, this.originalRemoteConfig, - this.errorMessage, + this.exception, this.isDirty = false, this.showSaveSuccess = false, }); @@ -52,7 +52,7 @@ class AppConfigurationState extends Equatable { AppConfigurationStatus? status, RemoteConfig? remoteConfig, RemoteConfig? originalRemoteConfig, - String? errorMessage, + HtHttpException? exception, bool? isDirty, bool clearErrorMessage = false, bool? showSaveSuccess, @@ -62,9 +62,9 @@ class AppConfigurationState extends Equatable { status: status ?? this.status, remoteConfig: remoteConfig ?? this.remoteConfig, originalRemoteConfig: originalRemoteConfig ?? this.originalRemoteConfig, - errorMessage: clearErrorMessage + exception: clearErrorMessage ? null - : errorMessage ?? this.errorMessage, + : exception ?? this.exception, isDirty: isDirty ?? this.isDirty, showSaveSuccess: clearShowSaveSuccess ? false @@ -74,11 +74,11 @@ class AppConfigurationState extends Equatable { @override List get props => [ - status, - remoteConfig, - originalRemoteConfig, - errorMessage, - isDirty, - showSaveSuccess, - ]; + status, + remoteConfig, + originalRemoteConfig, + exception, + isDirty, + showSaveSuccess, + ]; } From bfe689e9c2755266f97789f712d0abc97fc87ac8 Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 11:47:16 +0100 Subject: [PATCH 14/44] refactor(app_config): Update AppConfigurationBloc to emit HtHttpException --- lib/app_configuration/bloc/app_configuration_bloc.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/app_configuration/bloc/app_configuration_bloc.dart b/lib/app_configuration/bloc/app_configuration_bloc.dart index 7c443c2e..25bfd9ec 100644 --- a/lib/app_configuration/bloc/app_configuration_bloc.dart +++ b/lib/app_configuration/bloc/app_configuration_bloc.dart @@ -43,14 +43,14 @@ class AppConfigurationBloc emit( state.copyWith( status: AppConfigurationStatus.failure, - errorMessage: e.message, + exception: e, ), ); } catch (e) { emit( state.copyWith( status: AppConfigurationStatus.failure, - errorMessage: 'An unknown error occurred: $e', + exception: UnknownException('An unknown error occurred: $e'), ), ); } @@ -79,14 +79,14 @@ class AppConfigurationBloc emit( state.copyWith( status: AppConfigurationStatus.failure, - errorMessage: e.message, + exception: e, ), ); } catch (e) { emit( state.copyWith( status: AppConfigurationStatus.failure, - errorMessage: 'An unknown error occurred: $e', + exception: UnknownException('An unknown error occurred: $e'), ), ); } From 35bb9de2ba641a4bc7167e46a4f513bf60be9d8f Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 11:48:49 +0100 Subject: [PATCH 15/44] refactor(app_config): Update AppConfigurationPage to use HtHttpException and friendly messages --- .../view/app_configuration_page.dart | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/app_configuration/view/app_configuration_page.dart b/lib/app_configuration/view/app_configuration_page.dart index c036b3a1..db72e839 100644 --- a/lib/app_configuration/view/app_configuration_page.dart +++ b/lib/app_configuration/view/app_configuration_page.dart @@ -5,6 +5,7 @@ import 'package:ht_dashboard/l10n/l10n.dart'; import 'package:ht_dashboard/shared/constants/app_spacing.dart'; import 'package:ht_dashboard/shared/widgets/widgets.dart'; import 'package:ht_shared/ht_shared.dart'; +import 'package:ht_ui_kit/ht_ui_kit.dart'; // Import for toFriendlyMessage /// {@template app_configuration_page} /// A page for managing the application's remote configuration. @@ -80,15 +81,14 @@ class _AppConfigurationPageState extends State { context.read().add( const AppConfigurationFieldChanged(), ); - } else if (state.status == AppConfigurationStatus.failure) { + } else if (state.status == AppConfigurationStatus.failure && + state.exception != null) { ScaffoldMessenger.of(context) ..hideCurrentSnackBar() ..showSnackBar( SnackBar( content: Text( - l10n.appConfigSaveErrorMessage( - state.errorMessage ?? l10n.unknownError, - ), + state.exception!.toFriendlyMessage(context), style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: Theme.of(context).colorScheme.onError, ), @@ -108,8 +108,7 @@ class _AppConfigurationPageState extends State { ); } else if (state.status == AppConfigurationStatus.failure) { return FailureStateWidget( - message: - state.errorMessage ?? l10n.failedToLoadConfigurationMessage, + exception: state.exception!, onRetry: () { context.read().add( const AppConfigurationLoaded(), From d3a8c1efb61600a972dc1ad5302a9d078e6c5ab4 Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 11:51:39 +0100 Subject: [PATCH 16/44] refactor(settings): Refactor SettingsState to use HtHttpException --- lib/settings/bloc/settings_state.dart | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/settings/bloc/settings_state.dart b/lib/settings/bloc/settings_state.dart index aa16450f..ab9de65e 100644 --- a/lib/settings/bloc/settings_state.dart +++ b/lib/settings/bloc/settings_state.dart @@ -39,13 +39,13 @@ final class SettingsLoadSuccess extends SettingsState { /// {@endtemplate} final class SettingsLoadFailure extends SettingsState { /// {@macro settings_load_failure} - const SettingsLoadFailure(this.errorMessage, {super.userAppSettings}); + const SettingsLoadFailure(this.exception, {super.userAppSettings}); - /// The error message describing the failure. - final String errorMessage; + /// The error exception describing the failure. + final HtHttpException exception; @override - List get props => [errorMessage, userAppSettings]; + List get props => [exception, userAppSettings]; } /// {@template settings_update_in_progress} @@ -69,11 +69,11 @@ final class SettingsUpdateSuccess extends SettingsState { /// {@endtemplate} final class SettingsUpdateFailure extends SettingsState { /// {@macro settings_update_failure} - const SettingsUpdateFailure(this.errorMessage, {super.userAppSettings}); + const SettingsUpdateFailure(this.exception, {super.userAppSettings}); - /// The error message describing the failure. - final String errorMessage; + /// The error exception describing the failure. + final HtHttpException exception; @override - List get props => [errorMessage, userAppSettings]; + List get props => [exception, userAppSettings]; } From 0362b094236bf79166fc4501b03378276e0c239a Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 11:52:14 +0100 Subject: [PATCH 17/44] refactor(settings): Update SettingsBloc to emit HtHttpException --- lib/settings/bloc/settings_bloc.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/settings/bloc/settings_bloc.dart b/lib/settings/bloc/settings_bloc.dart index d6222a1e..29a2c48a 100644 --- a/lib/settings/bloc/settings_bloc.dart +++ b/lib/settings/bloc/settings_bloc.dart @@ -56,12 +56,12 @@ class SettingsBloc extends Bloc { emit(SettingsLoadSuccess(userAppSettings: defaultSettings)); } on HtHttpException catch (e) { emit( - SettingsLoadFailure(e.message, userAppSettings: state.userAppSettings), + SettingsLoadFailure(e, userAppSettings: state.userAppSettings), ); } catch (e) { emit( SettingsLoadFailure( - 'An unexpected error occurred: $e', + UnknownException('An unexpected error occurred: $e'), userAppSettings: state.userAppSettings, ), ); @@ -82,14 +82,14 @@ class SettingsBloc extends Bloc { } on HtHttpException catch (e) { emit( SettingsUpdateFailure( - e.message, + e, userAppSettings: state.userAppSettings, ), ); } catch (e) { emit( SettingsUpdateFailure( - 'An unexpected error occurred: $e', + UnknownException('An unexpected error occurred: $e'), userAppSettings: state.userAppSettings, ), ); From 1697e364af7f4241ea26e8e1bf960c5146ed86fe Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 11:53:06 +0100 Subject: [PATCH 18/44] refactor(settings): Update SettingsPage to use HtHttpException and friendly messages --- lib/settings/view/settings_page.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/settings/view/settings_page.dart b/lib/settings/view/settings_page.dart index 38ea659f..9a1ce91b 100644 --- a/lib/settings/view/settings_page.dart +++ b/lib/settings/view/settings_page.dart @@ -8,6 +8,7 @@ import 'package:ht_dashboard/shared/constants/app_spacing.dart'; import 'package:ht_dashboard/shared/widgets/widgets.dart'; import 'package:ht_data_repository/ht_data_repository.dart'; import 'package:ht_shared/ht_shared.dart'; +import 'package:ht_ui_kit/ht_ui_kit.dart'; // Import for toFriendlyMessage /// {@template settings_page} /// A page for user settings, allowing customization of theme and language. @@ -79,7 +80,7 @@ class _SettingsView extends StatelessWidget { ..showSnackBar( SnackBar( content: Text( - l10n.settingsSaveErrorMessage(state.errorMessage), + state.exception.toFriendlyMessage(context), ), ), ); @@ -104,7 +105,7 @@ class _SettingsView extends StatelessWidget { ); } else if (state is SettingsLoadFailure) { return FailureStateWidget( - message: l10n.failedToLoadSettingsMessage(state.errorMessage), + exception: state.exception, onRetry: () { context.read().add( SettingsLoaded( From a38263aaf184ffa5a293fbfc361d8a9f76e281de Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 11:58:26 +0100 Subject: [PATCH 19/44] docs: Add user-friendly error handling to features list --- README.md | 1 + lib/shared/widgets/failure_state_widget.dart | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1a4e236f..1dd4603e 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ Control the behavior and appearance of the `ht_main` mobile application remotely #### 📊 **Intuitive User Interface** Built with Flutter, the dashboard provides a responsive and user-friendly experience across various web browsers and screen sizes. +* **User-Friendly Error Handling:** Displays clear, localized error messages for a smooth and understandable user experience when issues arise. * **Benefit for you:** A modern, maintainable, and visually appealing interface for your administrative tasks. ✨ diff --git a/lib/shared/widgets/failure_state_widget.dart b/lib/shared/widgets/failure_state_widget.dart index 30f8ad11..7848c5a4 100644 --- a/lib/shared/widgets/failure_state_widget.dart +++ b/lib/shared/widgets/failure_state_widget.dart @@ -6,7 +6,9 @@ import 'package:ht_ui_kit/ht_ui_kit.dart'; class FailureStateWidget extends StatelessWidget { /// Creates a [FailureStateWidget]. /// - /// The [exception] is the error exception to display. + /// This widget accepts an [exception] of type [HtHttpException] + /// and uses the `toFriendlyMessage` extension from `ht_ui_kit` + /// to display a localized, user-friendly error message. /// /// The [onRetry] is an optional callback to be called /// when the retry button is pressed. @@ -18,6 +20,7 @@ class FailureStateWidget extends StatelessWidget { }); /// The error exception to display. + /// This exception will be converted to a friendly, localized message. final HtHttpException exception; /// An optional callback to be called when the retry button is pressed. From 7e5a9f5a65f257a152904330ec3a1859b31ec523 Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 12:21:07 +0100 Subject: [PATCH 20/44] refactor(content_management): Replace errorMessage with exception - Changed `errorMessage` to `exception` in `CreateHeadlineState`. - Updated `exception` type to `HtHttpException`. - Improved error handling and reporting. --- .../bloc/create_headline/create_headline_state.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/content_management/bloc/create_headline/create_headline_state.dart b/lib/content_management/bloc/create_headline/create_headline_state.dart index 26be5df2..c70890c3 100644 --- a/lib/content_management/bloc/create_headline/create_headline_state.dart +++ b/lib/content_management/bloc/create_headline/create_headline_state.dart @@ -33,7 +33,7 @@ final class CreateHeadlineState extends Equatable { this.topics = const [], this.countries = const [], this.contentStatus = ContentStatus.active, - this.errorMessage, + this.exception, this.createdHeadline, }); @@ -49,7 +49,7 @@ final class CreateHeadlineState extends Equatable { final List topics; final List countries; final ContentStatus contentStatus; - final String? errorMessage; + final HtHttpException? exception; final Headline? createdHeadline; /// Returns true if the form is valid and can be submitted. @@ -75,7 +75,7 @@ final class CreateHeadlineState extends Equatable { List? topics, List? countries, ContentStatus? contentStatus, - String? errorMessage, + HtHttpException? exception, Headline? createdHeadline, }) { return CreateHeadlineState( @@ -91,7 +91,7 @@ final class CreateHeadlineState extends Equatable { topics: topics ?? this.topics, countries: countries ?? this.countries, contentStatus: contentStatus ?? this.contentStatus, - errorMessage: errorMessage, + exception: exception, createdHeadline: createdHeadline ?? this.createdHeadline, ); } @@ -110,7 +110,7 @@ final class CreateHeadlineState extends Equatable { topics, countries, contentStatus, - errorMessage, + exception, createdHeadline, ]; } From 8d69bb35adf742b8ebd4e1ed88c28192a29c8a7c Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 12:21:14 +0100 Subject: [PATCH 21/44] refactor(content_management): replace errorMessage with exception - Changed `errorMessage` to `exception` in `CreateSourceState`. - Updated `exception` type to `HtHttpException`. - Improved error handling and information. --- .../bloc/create_source/create_source_state.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/content_management/bloc/create_source/create_source_state.dart b/lib/content_management/bloc/create_source/create_source_state.dart index 7f6bece8..0384e389 100644 --- a/lib/content_management/bloc/create_source/create_source_state.dart +++ b/lib/content_management/bloc/create_source/create_source_state.dart @@ -31,7 +31,7 @@ final class CreateSourceState extends Equatable { this.headquarters, this.countries = const [], this.contentStatus = ContentStatus.active, - this.errorMessage, + this.exception, this.createdSource, }); @@ -44,7 +44,7 @@ final class CreateSourceState extends Equatable { final Country? headquarters; final List countries; final ContentStatus contentStatus; - final String? errorMessage; + final HtHttpException? exception; final Source? createdSource; /// Returns true if the form is valid and can be submitted. @@ -66,7 +66,7 @@ final class CreateSourceState extends Equatable { ValueGetter? headquarters, List? countries, ContentStatus? contentStatus, - String? errorMessage, + HtHttpException? exception, Source? createdSource, }) { return CreateSourceState( @@ -79,7 +79,7 @@ final class CreateSourceState extends Equatable { headquarters: headquarters != null ? headquarters() : this.headquarters, countries: countries ?? this.countries, contentStatus: contentStatus ?? this.contentStatus, - errorMessage: errorMessage, + exception: exception, createdSource: createdSource ?? this.createdSource, ); } @@ -95,7 +95,7 @@ final class CreateSourceState extends Equatable { headquarters, countries, contentStatus, - errorMessage, + exception, createdSource, ]; } From 7d565eb595374236ffa9d3e432d610f4026ed0f7 Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 12:21:24 +0100 Subject: [PATCH 22/44] fix(create_topic): improve error handling - Replaced errorMessage with exception - Handle unexpected errors gracefully - Improved error message clarity --- .../bloc/create_topic/create_topic_bloc.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/content_management/bloc/create_topic/create_topic_bloc.dart b/lib/content_management/bloc/create_topic/create_topic_bloc.dart index f7f17465..6d6d51fa 100644 --- a/lib/content_management/bloc/create_topic/create_topic_bloc.dart +++ b/lib/content_management/bloc/create_topic/create_topic_bloc.dart @@ -102,14 +102,14 @@ class CreateTopicBloc extends Bloc { emit( state.copyWith( status: CreateTopicStatus.failure, - errorMessage: e.message, + exception: e, ), ); } catch (e) { emit( state.copyWith( status: CreateTopicStatus.failure, - errorMessage: e.toString(), + exception: UnknownException('An unexpected error occurred: $e'), ), ); } From 12ba1ad00da3e3d027ecb349bf970cee01f420f9 Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 12:21:29 +0100 Subject: [PATCH 23/44] refactor(content_management): replace errorMessage with exception - Changed `errorMessage` to `exception` in `CreateTopicState`. - Updated `exception` type to `HtHttpException`. - Improved error handling and reporting. --- .../bloc/create_topic/create_topic_state.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/content_management/bloc/create_topic/create_topic_state.dart b/lib/content_management/bloc/create_topic/create_topic_state.dart index 0fbc6137..120890ba 100644 --- a/lib/content_management/bloc/create_topic/create_topic_state.dart +++ b/lib/content_management/bloc/create_topic/create_topic_state.dart @@ -24,7 +24,7 @@ final class CreateTopicState extends Equatable { this.description = '', this.iconUrl = '', this.contentStatus = ContentStatus.active, - this.errorMessage, + this.exception, this.createdTopic, }); @@ -33,7 +33,7 @@ final class CreateTopicState extends Equatable { final String description; final String iconUrl; final ContentStatus contentStatus; - final String? errorMessage; + final HtHttpException? exception; final Topic? createdTopic; /// Returns true if the form is valid and can be submitted. @@ -47,7 +47,7 @@ final class CreateTopicState extends Equatable { String? description, String? iconUrl, ContentStatus? contentStatus, - String? errorMessage, + HtHttpException? exception, Topic? createdTopic, }) { return CreateTopicState( @@ -56,7 +56,7 @@ final class CreateTopicState extends Equatable { description: description ?? this.description, iconUrl: iconUrl ?? this.iconUrl, contentStatus: contentStatus ?? this.contentStatus, - errorMessage: errorMessage, + exception: exception, createdTopic: createdTopic ?? this.createdTopic, ); } @@ -68,7 +68,7 @@ final class CreateTopicState extends Equatable { description, iconUrl, contentStatus, - errorMessage, + exception, createdTopic, ]; } From 60654ef8df7d3c286e52ba013622bebcafb73375 Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 12:21:37 +0100 Subject: [PATCH 24/44] refactor(content_management): replace errorMessage with exception - Changed `errorMessage` to `exception` in `EditHeadlineState`. - Updated `exception` type to `HtHttpException`. - Updated `props` list in `EditHeadlineState`. --- .../edit_headline/edit_headline_state.dart | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/lib/content_management/bloc/edit_headline/edit_headline_state.dart b/lib/content_management/bloc/edit_headline/edit_headline_state.dart index 5cf98ea6..4bf778b0 100644 --- a/lib/content_management/bloc/edit_headline/edit_headline_state.dart +++ b/lib/content_management/bloc/edit_headline/edit_headline_state.dart @@ -34,7 +34,7 @@ final class EditHeadlineState extends Equatable { this.topics = const [], this.countries = const [], this.contentStatus = ContentStatus.active, - this.errorMessage, + this.exception, this.updatedHeadline, }); @@ -51,7 +51,7 @@ final class EditHeadlineState extends Equatable { final List topics; final List countries; final ContentStatus contentStatus; - final String? errorMessage; + final HtHttpException? exception; final Headline? updatedHeadline; /// Returns true if the form is valid and can be submitted. @@ -78,7 +78,7 @@ final class EditHeadlineState extends Equatable { List? topics, List? countries, ContentStatus? contentStatus, - String? errorMessage, + HtHttpException? exception, Headline? updatedHeadline, }) { return EditHeadlineState( @@ -95,27 +95,27 @@ final class EditHeadlineState extends Equatable { topics: topics ?? this.topics, countries: countries ?? this.countries, contentStatus: contentStatus ?? this.contentStatus, - errorMessage: errorMessage, + exception: exception, updatedHeadline: updatedHeadline ?? this.updatedHeadline, ); } @override List get props => [ - status, - initialHeadline, - title, - excerpt, - url, - imageUrl, - source, - topic, - eventCountry, - sources, - topics, - countries, - contentStatus, - errorMessage, - updatedHeadline, - ]; + status, + initialHeadline, + title, + excerpt, + url, + imageUrl, + source, + topic, + eventCountry, + sources, + topics, + countries, + contentStatus, + exception, + updatedHeadline, + ]; } From c2e2cb4ecffbed4ce704cc0d134d840f15313306 Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 12:21:47 +0100 Subject: [PATCH 25/44] refactor(content_management): replace errorMessage with exception - Changed `errorMessage` to `exception` in `EditSourceState`. - Updated `exception` type to `HtHttpException`. - Improved error handling and reporting. --- .../bloc/edit_source/edit_source_state.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/content_management/bloc/edit_source/edit_source_state.dart b/lib/content_management/bloc/edit_source/edit_source_state.dart index b0a2805b..1943a680 100644 --- a/lib/content_management/bloc/edit_source/edit_source_state.dart +++ b/lib/content_management/bloc/edit_source/edit_source_state.dart @@ -31,7 +31,7 @@ final class EditSourceState extends Equatable { this.headquarters, this.countries = const [], this.contentStatus = ContentStatus.active, - this.errorMessage, + this.exception, this.updatedSource, }); @@ -45,7 +45,7 @@ final class EditSourceState extends Equatable { final Country? headquarters; final List countries; final ContentStatus contentStatus; - final String? errorMessage; + final HtHttpException? exception; final Source? updatedSource; /// Returns true if the form is valid and can be submitted. @@ -68,7 +68,7 @@ final class EditSourceState extends Equatable { ValueGetter? headquarters, List? countries, ContentStatus? contentStatus, - String? errorMessage, + HtHttpException? exception, Source? updatedSource, }) { return EditSourceState( @@ -82,7 +82,7 @@ final class EditSourceState extends Equatable { headquarters: headquarters != null ? headquarters() : this.headquarters, countries: countries ?? this.countries, contentStatus: contentStatus ?? this.contentStatus, - errorMessage: errorMessage, + exception: exception, updatedSource: updatedSource ?? this.updatedSource, ); } @@ -99,7 +99,7 @@ final class EditSourceState extends Equatable { headquarters, countries, contentStatus, - errorMessage, + exception, updatedSource, ]; } From 4cca6a550d807ac024ef5c1b15f759bf4d5d4857 Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 12:21:53 +0100 Subject: [PATCH 26/44] fix(content-management): improve error handling in blocs - Replaced errorMessage with exception in states - Improved error handling in CreateSourceBloc - Improved error handling in EditTopicBloc - Updated exception handling for better clarity - Used HtHttpException for specific error types --- .../bloc/create_source/create_source_bloc.dart | 8 ++++---- .../bloc/edit_topic/edit_topic_bloc.dart | 10 +++++----- .../bloc/edit_topic/edit_topic_state.dart | 10 +++++----- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/content_management/bloc/create_source/create_source_bloc.dart b/lib/content_management/bloc/create_source/create_source_bloc.dart index 7bb78aaa..9cf6b1c7 100644 --- a/lib/content_management/bloc/create_source/create_source_bloc.dart +++ b/lib/content_management/bloc/create_source/create_source_bloc.dart @@ -51,14 +51,14 @@ class CreateSourceBloc extends Bloc { emit( state.copyWith( status: CreateSourceStatus.failure, - errorMessage: e.message, + exception: e, ), ); } catch (e) { emit( state.copyWith( status: CreateSourceStatus.failure, - errorMessage: e.toString(), + exception: UnknownException('An unexpected error occurred: $e'), ), ); } @@ -151,14 +151,14 @@ class CreateSourceBloc extends Bloc { emit( state.copyWith( status: CreateSourceStatus.failure, - errorMessage: e.message, + exception: e, ), ); } catch (e) { emit( state.copyWith( status: CreateSourceStatus.failure, - errorMessage: e.toString(), + exception: UnknownException('An unexpected error occurred: $e'), ), ); } diff --git a/lib/content_management/bloc/edit_topic/edit_topic_bloc.dart b/lib/content_management/bloc/edit_topic/edit_topic_bloc.dart index 32b1a5f2..e28f1eb4 100644 --- a/lib/content_management/bloc/edit_topic/edit_topic_bloc.dart +++ b/lib/content_management/bloc/edit_topic/edit_topic_bloc.dart @@ -47,14 +47,14 @@ class EditTopicBloc extends Bloc { emit( state.copyWith( status: EditTopicStatus.failure, - errorMessage: e.message, + exception: e, ), ); } catch (e) { emit( state.copyWith( status: EditTopicStatus.failure, - errorMessage: e.toString(), + exception: UnknownException('An unexpected error occurred: $e'), ), ); } @@ -121,7 +121,7 @@ class EditTopicBloc extends Bloc { emit( state.copyWith( status: EditTopicStatus.failure, - errorMessage: 'Cannot update: Original topic data not loaded.', + exception: UnknownException('Cannot update: Original topic data not loaded.'), ), ); return; @@ -152,14 +152,14 @@ class EditTopicBloc extends Bloc { emit( state.copyWith( status: EditTopicStatus.failure, - errorMessage: e.message, + exception: e, ), ); } catch (e) { emit( state.copyWith( status: EditTopicStatus.failure, - errorMessage: e.toString(), + exception: UnknownException('An unexpected error occurred: $e'), ), ); } diff --git a/lib/content_management/bloc/edit_topic/edit_topic_state.dart b/lib/content_management/bloc/edit_topic/edit_topic_state.dart index 7ee2b99d..ce821fb8 100644 --- a/lib/content_management/bloc/edit_topic/edit_topic_state.dart +++ b/lib/content_management/bloc/edit_topic/edit_topic_state.dart @@ -27,7 +27,7 @@ final class EditTopicState extends Equatable { this.description = '', this.iconUrl = '', this.contentStatus = ContentStatus.active, - this.errorMessage, + this.exception, this.updatedTopic, }); @@ -37,7 +37,7 @@ final class EditTopicState extends Equatable { final String description; final String iconUrl; final ContentStatus contentStatus; - final String? errorMessage; + final HtHttpException? exception; final Topic? updatedTopic; /// Returns true if the form is valid and can be submitted. @@ -52,7 +52,7 @@ final class EditTopicState extends Equatable { String? description, String? iconUrl, ContentStatus? contentStatus, - String? errorMessage, + HtHttpException? exception, Topic? updatedTopic, }) { return EditTopicState( @@ -62,7 +62,7 @@ final class EditTopicState extends Equatable { description: description ?? this.description, iconUrl: iconUrl ?? this.iconUrl, contentStatus: contentStatus ?? this.contentStatus, - errorMessage: errorMessage ?? this.errorMessage, + exception: exception, updatedTopic: updatedTopic ?? this.updatedTopic, ); } @@ -75,7 +75,7 @@ final class EditTopicState extends Equatable { description, iconUrl, contentStatus, - errorMessage, + exception, updatedTopic, ]; } From dbb05662a5be09b640673bf769914b95998d53a2 Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 12:24:43 +0100 Subject: [PATCH 27/44] fix(content_management): Improve error handling in EditSourceBloc - Replaced errorMessage with exception property. - Improved error reporting for unexpected errors. - Handle cases where original source data is missing. --- .../bloc/edit_source/edit_source_bloc.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/content_management/bloc/edit_source/edit_source_bloc.dart b/lib/content_management/bloc/edit_source/edit_source_bloc.dart index 45b65367..08652207 100644 --- a/lib/content_management/bloc/edit_source/edit_source_bloc.dart +++ b/lib/content_management/bloc/edit_source/edit_source_bloc.dart @@ -64,14 +64,14 @@ class EditSourceBloc extends Bloc { emit( state.copyWith( status: EditSourceStatus.failure, - errorMessage: e.message, + exception: e, ), ); } catch (e) { emit( state.copyWith( status: EditSourceStatus.failure, - errorMessage: e.toString(), + exception: UnknownException('An unexpected error occurred: $e'), ), ); } @@ -162,7 +162,7 @@ class EditSourceBloc extends Bloc { emit( state.copyWith( status: EditSourceStatus.failure, - errorMessage: 'Cannot update: Original source data not loaded.', + exception: UnknownException('Cannot update: Original source data not loaded.'), ), ); return; @@ -192,14 +192,14 @@ class EditSourceBloc extends Bloc { emit( state.copyWith( status: EditSourceStatus.failure, - errorMessage: e.message, + exception: e, ), ); } catch (e) { emit( state.copyWith( status: EditSourceStatus.failure, - errorMessage: e.toString(), + exception: UnknownException('An unexpected error occurred: $e'), ), ); } From 006d5b0b89cc68611f8f1f8b489a244ae3ce169d Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 12:41:24 +0100 Subject: [PATCH 28/44] fix(createHeadlineBloc): Handle exceptions more robustly - Replaced errorMessage with exception property. - Improved error handling for unexpected exceptions. - Added UnknownException for better error reporting. --- .../bloc/create_headline/create_headline_bloc.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/content_management/bloc/create_headline/create_headline_bloc.dart b/lib/content_management/bloc/create_headline/create_headline_bloc.dart index 5905b6c1..445dc789 100644 --- a/lib/content_management/bloc/create_headline/create_headline_bloc.dart +++ b/lib/content_management/bloc/create_headline/create_headline_bloc.dart @@ -178,14 +178,14 @@ class CreateHeadlineBloc emit( state.copyWith( status: CreateHeadlineStatus.failure, - errorMessage: e.message, + exception: e, ), ); } catch (e) { emit( state.copyWith( status: CreateHeadlineStatus.failure, - errorMessage: e.toString(), + exception: UnknownException('An unexpected error occurred: $e'), ), ); } From 89e12c4dccb48ef36ced4cccb92431c174cfd09a Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 12:41:36 +0100 Subject: [PATCH 29/44] fix(content_management): improve error handling in EditHeadlineBloc - Replaced errorMessage with exception in state - Improved error wrapping for better context - Handle unexpected errors more gracefully --- .../bloc/edit_headline/edit_headline_bloc.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/content_management/bloc/edit_headline/edit_headline_bloc.dart b/lib/content_management/bloc/edit_headline/edit_headline_bloc.dart index 3e4e5034..1936d964 100644 --- a/lib/content_management/bloc/edit_headline/edit_headline_bloc.dart +++ b/lib/content_management/bloc/edit_headline/edit_headline_bloc.dart @@ -84,14 +84,14 @@ class EditHeadlineBloc extends Bloc { emit( state.copyWith( status: EditHeadlineStatus.failure, - errorMessage: e.message, + exception: e, ), ); } catch (e) { emit( state.copyWith( status: EditHeadlineStatus.failure, - errorMessage: e.toString(), + exception: UnknownException('An unexpected error occurred: $e'), ), ); } @@ -196,7 +196,7 @@ class EditHeadlineBloc extends Bloc { emit( state.copyWith( status: EditHeadlineStatus.failure, - errorMessage: 'Cannot update: Original headline data not loaded.', + exception: UnknownException('Cannot update: Original headline data not loaded.'), ), ); return; @@ -227,14 +227,14 @@ class EditHeadlineBloc extends Bloc { emit( state.copyWith( status: EditHeadlineStatus.failure, - errorMessage: e.message, + exception: e, ), ); } catch (e) { emit( state.copyWith( status: EditHeadlineStatus.failure, - errorMessage: e.toString(), + exception: UnknownException('An unexpected error occurred: $e'), ), ); } From 876affb031769b13b148556d7560407a135f8b06 Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 12:42:01 +0100 Subject: [PATCH 30/44] fix(content_management): improve error handling - Show more informative error messages. - Use exception's friendly message. - Improve FailureStateWidget display. --- lib/content_management/view/create_headline_page.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/content_management/view/create_headline_page.dart b/lib/content_management/view/create_headline_page.dart index 339a1cc8..ac92d1a7 100644 --- a/lib/content_management/view/create_headline_page.dart +++ b/lib/content_management/view/create_headline_page.dart @@ -95,7 +95,7 @@ class _CreateHeadlineViewState extends State<_CreateHeadlineView> { ..hideCurrentSnackBar() ..showSnackBar( SnackBar( - content: Text(state.errorMessage ?? l10n.unknownError), + content: Text(state.exception!.toFriendlyMessage(context)), backgroundColor: Theme.of(context).colorScheme.error, ), ); @@ -115,7 +115,7 @@ class _CreateHeadlineViewState extends State<_CreateHeadlineView> { state.topics.isEmpty && state.countries.isEmpty) { return FailureStateWidget( - message: state.errorMessage ?? l10n.unknownError, + exception: state.exception!, onRetry: () => context.read().add( const CreateHeadlineDataLoaded(), ), From cf2142acdffd311d0eb6ce0cb953dcc56d7d0efc Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 12:42:06 +0100 Subject: [PATCH 31/44] fix(content_management): improve error handling in create source page - Use exception's friendly message for better user feedback. - Updated error display in FailureStateWidget. --- lib/content_management/view/create_source_page.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/content_management/view/create_source_page.dart b/lib/content_management/view/create_source_page.dart index 80596a6d..a11f2af7 100644 --- a/lib/content_management/view/create_source_page.dart +++ b/lib/content_management/view/create_source_page.dart @@ -9,6 +9,7 @@ import 'package:ht_dashboard/shared/constants/pagination_constants.dart'; import 'package:ht_dashboard/shared/shared.dart'; import 'package:ht_data_repository/ht_data_repository.dart'; import 'package:ht_shared/ht_shared.dart'; +import 'package:ht_ui_kit/ht_ui_kit.dart'; /// {@template create_source_page} /// A page for creating a new source. @@ -94,7 +95,7 @@ class _CreateSourceViewState extends State<_CreateSourceView> { ..hideCurrentSnackBar() ..showSnackBar( SnackBar( - content: Text(state.errorMessage ?? l10n.unknownError), + content: Text(state.exception!.toFriendlyMessage(context)), backgroundColor: Theme.of(context).colorScheme.error, ), ); @@ -112,7 +113,7 @@ class _CreateSourceViewState extends State<_CreateSourceView> { if (state.status == CreateSourceStatus.failure && state.countries.isEmpty) { return FailureStateWidget( - message: state.errorMessage ?? l10n.unknownError, + exception: state.exception!, onRetry: () => context.read().add( const CreateSourceDataLoaded(), ), From 37f75ed035c38844475596463db5ded027744e4e Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 12:42:11 +0100 Subject: [PATCH 32/44] fix(content_management): improve error handling in create topic page - Replaced generic error message with more informative one. - Used `toFriendlyMessage` for better user experience. --- lib/content_management/view/create_topic_page.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/content_management/view/create_topic_page.dart b/lib/content_management/view/create_topic_page.dart index adc2d0ca..4b617c0f 100644 --- a/lib/content_management/view/create_topic_page.dart +++ b/lib/content_management/view/create_topic_page.dart @@ -8,6 +8,7 @@ import 'package:ht_dashboard/shared/constants/pagination_constants.dart'; import 'package:ht_dashboard/shared/shared.dart'; import 'package:ht_data_repository/ht_data_repository.dart'; import 'package:ht_shared/ht_shared.dart'; +import 'package:ht_ui_kit/ht_ui_kit.dart'; /// {@template create_topic_page} /// A page for creating a new topic. @@ -92,7 +93,7 @@ class _CreateTopicViewState extends State<_CreateTopicView> { ..hideCurrentSnackBar() ..showSnackBar( SnackBar( - content: Text(state.errorMessage ?? l10n.unknownError), + content: Text(state.exception!.toFriendlyMessage(context)), backgroundColor: Theme.of(context).colorScheme.error, ), ); From e278f7b0c8d28f8d06afd3aaa7718f7539c1478b Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 12:42:16 +0100 Subject: [PATCH 33/44] fix(content_management): Improve error handling in EditSourcePage - Show more informative error messages. - Use exception's friendly message. - Improved FailureStateWidget usage. --- lib/content_management/view/edit_source_page.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/content_management/view/edit_source_page.dart b/lib/content_management/view/edit_source_page.dart index 469bbd59..a030bcee 100644 --- a/lib/content_management/view/edit_source_page.dart +++ b/lib/content_management/view/edit_source_page.dart @@ -120,7 +120,7 @@ class _EditSourceViewState extends State<_EditSourceView> { ..hideCurrentSnackBar() ..showSnackBar( SnackBar( - content: Text(state.errorMessage ?? l10n.unknownError), + content: Text(state.exception!.toFriendlyMessage(context)), backgroundColor: Theme.of(context).colorScheme.error, ), ); @@ -144,7 +144,7 @@ class _EditSourceViewState extends State<_EditSourceView> { if (state.status == EditSourceStatus.failure && state.initialSource == null) { return FailureStateWidget( - message: state.errorMessage ?? l10n.unknownError, + exception: state.exception!, onRetry: () => context.read().add(const EditSourceLoaded()), ); From 92e0bf0e0020d25a2f66a8f4e5515b43455c0b1e Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 12:42:20 +0100 Subject: [PATCH 34/44] fix(content_management): improve error handling - Use exception's friendly message. - Show more informative error messages. --- lib/content_management/view/edit_topic_page.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/content_management/view/edit_topic_page.dart b/lib/content_management/view/edit_topic_page.dart index 66c1320c..73b76165 100644 --- a/lib/content_management/view/edit_topic_page.dart +++ b/lib/content_management/view/edit_topic_page.dart @@ -7,6 +7,7 @@ import 'package:ht_dashboard/l10n/l10n.dart'; import 'package:ht_dashboard/shared/shared.dart'; import 'package:ht_data_repository/ht_data_repository.dart'; import 'package:ht_shared/ht_shared.dart'; +import 'package:ht_ui_kit/ht_ui_kit.dart'; /// {@template edit_topic_page} /// A page for editing an existing topic. @@ -116,7 +117,7 @@ class _EditTopicViewState extends State<_EditTopicView> { ..hideCurrentSnackBar() ..showSnackBar( SnackBar( - content: Text(state.errorMessage ?? l10n.unknownError), + content: Text(state.exception!.toFriendlyMessage(context)), backgroundColor: Theme.of(context).colorScheme.error, ), ); @@ -139,7 +140,7 @@ class _EditTopicViewState extends State<_EditTopicView> { if (state.status == EditTopicStatus.failure && state.initialTopic == null) { return FailureStateWidget( - message: state.errorMessage ?? l10n.unknownError, + exception: state.exception!, onRetry: () => context.read().add( const EditTopicLoaded(), ), From 53840cf7ac01e206dddc867e7c1092bb90bffee7 Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 15:08:06 +0100 Subject: [PATCH 35/44] fix(createHeadlineBloc): improve error handling - Replaced errorMessage with exception property. - Handle unexpected errors gracefully. - Improved error reporting clarity. --- .../bloc/create_headline/create_headline_bloc.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/content_management/bloc/create_headline/create_headline_bloc.dart b/lib/content_management/bloc/create_headline/create_headline_bloc.dart index 445dc789..c4c9341b 100644 --- a/lib/content_management/bloc/create_headline/create_headline_bloc.dart +++ b/lib/content_management/bloc/create_headline/create_headline_bloc.dart @@ -70,14 +70,14 @@ class CreateHeadlineBloc emit( state.copyWith( status: CreateHeadlineStatus.failure, - errorMessage: e.message, + exception: e, ), ); } catch (e) { emit( state.copyWith( status: CreateHeadlineStatus.failure, - errorMessage: e.toString(), + exception: UnknownException('An unexpected error occurred: $e'), ), ); } From f8e0d5ff318814475679edfaf6947eb6f337d39f Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 15:08:14 +0100 Subject: [PATCH 36/44] feat(content_management): Add HTUIKit dependency - Added HTUIKit dependency to create_headline_page.dart --- lib/content_management/view/create_headline_page.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/content_management/view/create_headline_page.dart b/lib/content_management/view/create_headline_page.dart index ac92d1a7..b751f80e 100644 --- a/lib/content_management/view/create_headline_page.dart +++ b/lib/content_management/view/create_headline_page.dart @@ -8,6 +8,7 @@ import 'package:ht_dashboard/shared/constants/pagination_constants.dart'; import 'package:ht_dashboard/shared/shared.dart'; import 'package:ht_data_repository/ht_data_repository.dart'; import 'package:ht_shared/ht_shared.dart'; +import 'package:ht_ui_kit/ht_ui_kit.dart'; /// {@template create_headline_page} /// A page for creating a new headline. From 63b132daa79208b385d02454a06398246b1e6a51 Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 15:08:20 +0100 Subject: [PATCH 37/44] feat(content_management): add ht_ui_kit dependency - Added ht_ui_kit import. --- lib/content_management/view/edit_source_page.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/content_management/view/edit_source_page.dart b/lib/content_management/view/edit_source_page.dart index a030bcee..73ee884e 100644 --- a/lib/content_management/view/edit_source_page.dart +++ b/lib/content_management/view/edit_source_page.dart @@ -7,6 +7,7 @@ import 'package:ht_dashboard/l10n/l10n.dart'; import 'package:ht_dashboard/shared/shared.dart'; import 'package:ht_data_repository/ht_data_repository.dart'; import 'package:ht_shared/ht_shared.dart'; +import 'package:ht_ui_kit/ht_ui_kit.dart'; /// {@template edit_source_page} /// A page for editing an existing source. From 769fcee45b2e1e4eb2825f69a7b34ea9f5f7a8bd Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 15:09:09 +0100 Subject: [PATCH 38/44] fix(content_management): Improve error handling in EditHeadlinePage - Use exception's friendly message in SnackBar. - Display exception in FailureStateWidget. --- lib/content_management/view/edit_headline_page.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/content_management/view/edit_headline_page.dart b/lib/content_management/view/edit_headline_page.dart index 6a57aec0..6505d56b 100644 --- a/lib/content_management/view/edit_headline_page.dart +++ b/lib/content_management/view/edit_headline_page.dart @@ -7,6 +7,7 @@ import 'package:ht_dashboard/l10n/l10n.dart'; import 'package:ht_dashboard/shared/shared.dart'; import 'package:ht_data_repository/ht_data_repository.dart'; import 'package:ht_shared/ht_shared.dart'; +import 'package:ht_ui_kit/ht_ui_kit.dart'; /// {@template edit_headline_page} /// A page for editing an existing headline. @@ -124,7 +125,7 @@ class _EditHeadlineViewState extends State<_EditHeadlineView> { ..hideCurrentSnackBar() ..showSnackBar( SnackBar( - content: Text(state.errorMessage ?? l10n.unknownError), + content: Text(state.exception!.toFriendlyMessage(context)), backgroundColor: Theme.of(context).colorScheme.error, ), ); @@ -148,7 +149,7 @@ class _EditHeadlineViewState extends State<_EditHeadlineView> { if (state.status == EditHeadlineStatus.failure && state.initialHeadline == null) { return FailureStateWidget( - message: state.errorMessage ?? l10n.unknownError, + exception: state.exception!, onRetry: () => context.read().add( const EditHeadlineLoaded(), ), From 35fc611f10bb675f36891bdf7996df818140f5a4 Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 15:10:11 +0100 Subject: [PATCH 39/44] refactor(content_management): replace errorMessage with exception - Changed `errorMessage` to `exception` - Improved error handling clarity - Uses HtHttpException for error type --- .../bloc/content_management_state.dart | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/content_management/bloc/content_management_state.dart b/lib/content_management/bloc/content_management_state.dart index 8728613e..b0cc0dd1 100644 --- a/lib/content_management/bloc/content_management_state.dart +++ b/lib/content_management/bloc/content_management_state.dart @@ -32,7 +32,7 @@ class ContentManagementState extends Equatable { this.sources = const [], this.sourcesCursor, this.sourcesHasMore = false, - this.errorMessage, + this.exception, }); /// The currently active tab in the content management section. @@ -74,8 +74,8 @@ class ContentManagementState extends Equatable { /// Indicates if there are more sources to load. final bool sourcesHasMore; - /// Error message if an operation fails. - final String? errorMessage; + /// The error describing an operation failure, if any. + final HtHttpException? exception; /// Creates a copy of this [ContentManagementState] with updated values. ContentManagementState copyWith({ @@ -92,7 +92,7 @@ class ContentManagementState extends Equatable { List? sources, String? sourcesCursor, bool? sourcesHasMore, - String? errorMessage, + HtHttpException? exception, }) { return ContentManagementState( activeTab: activeTab ?? this.activeTab, @@ -108,7 +108,7 @@ class ContentManagementState extends Equatable { sources: sources ?? this.sources, sourcesCursor: sourcesCursor ?? this.sourcesCursor, sourcesHasMore: sourcesHasMore ?? this.sourcesHasMore, - errorMessage: errorMessage, + exception: exception ?? this.exception, ); } @@ -127,6 +127,6 @@ class ContentManagementState extends Equatable { sources, sourcesCursor, sourcesHasMore, - errorMessage, + exception, ]; } From 33e0e21853ba752c1a42efb12b38f0c801c3c582 Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 15:10:17 +0100 Subject: [PATCH 40/44] fix(content_management): display specific exception in failure state - Replaced generic error message. - Now shows the specific exception. - Improves error handling clarity. --- lib/content_management/view/headlines_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/content_management/view/headlines_page.dart b/lib/content_management/view/headlines_page.dart index 97788f56..41f89c6c 100644 --- a/lib/content_management/view/headlines_page.dart +++ b/lib/content_management/view/headlines_page.dart @@ -52,7 +52,7 @@ class _HeadlinesPageState extends State { if (state.headlinesStatus == ContentManagementStatus.failure) { return FailureStateWidget( - message: state.errorMessage ?? l10n.unknownError, + exception: state.exception!, onRetry: () => context.read().add( const LoadHeadlinesRequested(limit: kDefaultRowsPerPage), ), From 1f9926754ff9c83f73c614b8e297e12da459211b Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 15:10:33 +0100 Subject: [PATCH 41/44] fix(content_management): Handle exceptions more robustly - Replaced errorMessage with exception property - Improved error handling for all bloc events - Used UnknownException for unexpected errors --- .../bloc/content_management_bloc.dart | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/content_management/bloc/content_management_bloc.dart b/lib/content_management/bloc/content_management_bloc.dart index 0d0a2585..12d5b431 100644 --- a/lib/content_management/bloc/content_management_bloc.dart +++ b/lib/content_management/bloc/content_management_bloc.dart @@ -78,14 +78,14 @@ class ContentManagementBloc emit( state.copyWith( headlinesStatus: ContentManagementStatus.failure, - errorMessage: e.message, + exception: e, ), ); } catch (e) { emit( state.copyWith( headlinesStatus: ContentManagementStatus.failure, - errorMessage: e.toString(), + exception: UnknownException('An unexpected error occurred: $e'), ), ); } @@ -105,14 +105,14 @@ class ContentManagementBloc emit( state.copyWith( headlinesStatus: ContentManagementStatus.failure, - errorMessage: e.message, + exception: e, ), ); } catch (e) { emit( state.copyWith( headlinesStatus: ContentManagementStatus.failure, - errorMessage: e.toString(), + exception: UnknownException('An unexpected error occurred: $e'), ), ); } @@ -157,14 +157,14 @@ class ContentManagementBloc emit( state.copyWith( topicsStatus: ContentManagementStatus.failure, - errorMessage: e.message, + exception: e, ), ); } catch (e) { emit( state.copyWith( topicsStatus: ContentManagementStatus.failure, - errorMessage: e.toString(), + exception: UnknownException('An unexpected error occurred: $e'), ), ); } @@ -184,14 +184,14 @@ class ContentManagementBloc emit( state.copyWith( topicsStatus: ContentManagementStatus.failure, - errorMessage: e.message, + exception: e, ), ); } catch (e) { emit( state.copyWith( topicsStatus: ContentManagementStatus.failure, - errorMessage: e.toString(), + exception: UnknownException('An unexpected error occurred: $e'), ), ); } @@ -236,14 +236,14 @@ class ContentManagementBloc emit( state.copyWith( sourcesStatus: ContentManagementStatus.failure, - errorMessage: e.message, + exception: e, ), ); } catch (e) { emit( state.copyWith( sourcesStatus: ContentManagementStatus.failure, - errorMessage: e.toString(), + exception: UnknownException('An unexpected error occurred: $e'), ), ); } @@ -263,14 +263,14 @@ class ContentManagementBloc emit( state.copyWith( sourcesStatus: ContentManagementStatus.failure, - errorMessage: e.message, + exception: e, ), ); } catch (e) { emit( state.copyWith( sourcesStatus: ContentManagementStatus.failure, - errorMessage: e.toString(), + exception: UnknownException('An unexpected error occurred: $e'), ), ); } From c162cff625b8fee640eda3e48e5006ccea61b46f Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 15:11:08 +0100 Subject: [PATCH 42/44] fix(content_management): display specific exception message - Replaced generic error message. - Now shows specific exception details. --- lib/content_management/view/sources_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/content_management/view/sources_page.dart b/lib/content_management/view/sources_page.dart index a353755d..d06451d8 100644 --- a/lib/content_management/view/sources_page.dart +++ b/lib/content_management/view/sources_page.dart @@ -53,7 +53,7 @@ class _SourcesPageState extends State { if (state.sourcesStatus == ContentManagementStatus.failure) { return FailureStateWidget( - message: state.errorMessage ?? l10n.unknownError, + exception: state.exception!, onRetry: () => context.read().add( const LoadSourcesRequested(limit: kDefaultRowsPerPage), ), From 7d22ab1d3093f9fe01e525cd2f90157eaf3dcd32 Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 15:12:01 +0100 Subject: [PATCH 43/44] fix(content_management): display exception details - Replaced generic error message. - Now shows specific exception. - Improves error handling clarity. --- lib/content_management/view/topics_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/content_management/view/topics_page.dart b/lib/content_management/view/topics_page.dart index 9f823753..f28fd315 100644 --- a/lib/content_management/view/topics_page.dart +++ b/lib/content_management/view/topics_page.dart @@ -49,7 +49,7 @@ class _TopicPageState extends State { if (state.topicsStatus == ContentManagementStatus.failure) { return FailureStateWidget( - message: state.errorMessage ?? l10n.unknownError, + exception: state.exception!, onRetry: () => context.read().add( const LoadTopicsRequested(limit: kDefaultRowsPerPage), ), From a6e5c1e873d48ca7e550adf27d8e24b83035f910 Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 15:14:27 +0100 Subject: [PATCH 44/44] style: format --- analysis_options.yaml | 1 + lib/app/view/app.dart | 28 +- .../bloc/app_configuration_event.dart | 2 +- .../bloc/app_configuration_state.dart | 18 +- .../view/app_configuration_page.dart | 366 ++++++++++-------- .../bloc/authentication_bloc.dart | 14 +- .../view/authentication_page.dart | 3 +- .../view/email_code_verification_page.dart | 2 +- .../create_headline/create_headline_bloc.dart | 20 +- .../create_headline_state.dart | 30 +- .../bloc/create_topic/create_topic_bloc.dart | 4 +- .../bloc/create_topic/create_topic_state.dart | 16 +- .../edit_headline/edit_headline_bloc.dart | 4 +- .../edit_headline/edit_headline_state.dart | 32 +- .../bloc/edit_source/edit_source_bloc.dart | 4 +- .../bloc/edit_topic/edit_topic_bloc.dart | 10 +- .../bloc/edit_topic/edit_topic_state.dart | 18 +- .../view/content_management_page.dart | 2 +- .../view/edit_topic_page.dart | 20 +- lib/content_management/view/topics_page.dart | 4 +- lib/dashboard/bloc/dashboard_state.dart | 12 +- lib/router/router.dart | 4 +- 22 files changed, 338 insertions(+), 276 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 2cc6fd2b..35fe6699 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,5 +1,6 @@ analyzer: errors: + avoid_bool_literals_in_conditional_expressions: ignore avoid_catches_without_on_clauses: ignore avoid_print: ignore avoid_redundant_argument_values: ignore diff --git a/lib/app/view/app.dart b/lib/app/view/app.dart index e2aa6582..765a49d8 100644 --- a/lib/app/view/app.dart +++ b/lib/app/view/app.dart @@ -13,13 +13,13 @@ import 'package:ht_dashboard/authentication/bloc/authentication_bloc.dart'; import 'package:ht_dashboard/content_management/bloc/content_management_bloc.dart'; import 'package:ht_dashboard/dashboard/bloc/dashboard_bloc.dart'; import 'package:ht_dashboard/l10n/app_localizations.dart'; -import 'package:ht_ui_kit/ht_ui_kit.dart'; import 'package:ht_dashboard/router/router.dart'; // Import for app_theme.dart import 'package:ht_dashboard/shared/theme/app_theme.dart'; import 'package:ht_data_repository/ht_data_repository.dart'; import 'package:ht_kv_storage_service/ht_kv_storage_service.dart'; import 'package:ht_shared/ht_shared.dart' hide AppStatus; +import 'package:ht_ui_kit/ht_ui_kit.dart'; import 'package:logging/logging.dart'; class App extends StatelessWidget { @@ -30,7 +30,8 @@ class App extends StatelessWidget { required HtDataRepository htCountriesRepository, required HtDataRepository htSourcesRepository, required HtDataRepository htUserAppSettingsRepository, - required HtDataRepository htUserContentPreferencesRepository, + required HtDataRepository + htUserContentPreferencesRepository, required HtDataRepository htRemoteConfigRepository, required HtDataRepository htDashboardSummaryRepository, required HtKVStorageService kvStorageService, @@ -54,7 +55,8 @@ class App extends StatelessWidget { final HtDataRepository _htCountriesRepository; final HtDataRepository _htSourcesRepository; final HtDataRepository _htUserAppSettingsRepository; - final HtDataRepository _htUserContentPreferencesRepository; + final HtDataRepository + _htUserContentPreferencesRepository; final HtDataRepository _htRemoteConfigRepository; final HtDataRepository _htDashboardSummaryRepository; final HtKVStorageService _kvStorageService; @@ -80,10 +82,10 @@ class App extends StatelessWidget { BlocProvider( create: (context) => AppBloc( authenticationRepository: context.read(), - userAppSettingsRepository: - context.read>(), - appConfigRepository: - context.read>(), + userAppSettingsRepository: context + .read>(), + appConfigRepository: context + .read>(), environment: _environment, logger: Logger('AppBloc'), ), @@ -95,8 +97,8 @@ class App extends StatelessWidget { ), BlocProvider( create: (context) => AppConfigurationBloc( - remoteConfigRepository: - context.read>(), + remoteConfigRepository: context + .read>(), ), ), BlocProvider( @@ -108,10 +110,10 @@ class App extends StatelessWidget { ), BlocProvider( create: (context) => DashboardBloc( - dashboardSummaryRepository: - context.read>(), - appConfigRepository: - context.read>(), + dashboardSummaryRepository: context + .read>(), + appConfigRepository: context + .read>(), headlinesRepository: context.read>(), ), ), diff --git a/lib/app_configuration/bloc/app_configuration_event.dart b/lib/app_configuration/bloc/app_configuration_event.dart index 0fc89bf0..0abacbe0 100644 --- a/lib/app_configuration/bloc/app_configuration_event.dart +++ b/lib/app_configuration/bloc/app_configuration_event.dart @@ -20,7 +20,7 @@ class AppConfigurationLoaded extends AppConfigurationEvent { /// {@template app_configuration_updated} /// Event to request the update of the application configuration. /// -/// Carries the new [appConfig] object to be saved. +/// Carries the new "appConfig" object to be saved. /// {@endtemplate} class AppConfigurationUpdated extends AppConfigurationEvent { /// {@macro app_configuration_updated} diff --git a/lib/app_configuration/bloc/app_configuration_state.dart b/lib/app_configuration/bloc/app_configuration_state.dart index fe67cc57..9ecc2205 100644 --- a/lib/app_configuration/bloc/app_configuration_state.dart +++ b/lib/app_configuration/bloc/app_configuration_state.dart @@ -62,9 +62,7 @@ class AppConfigurationState extends Equatable { status: status ?? this.status, remoteConfig: remoteConfig ?? this.remoteConfig, originalRemoteConfig: originalRemoteConfig ?? this.originalRemoteConfig, - exception: clearErrorMessage - ? null - : exception ?? this.exception, + exception: clearErrorMessage ? null : exception ?? this.exception, isDirty: isDirty ?? this.isDirty, showSaveSuccess: clearShowSaveSuccess ? false @@ -74,11 +72,11 @@ class AppConfigurationState extends Equatable { @override List get props => [ - status, - remoteConfig, - originalRemoteConfig, - exception, - isDirty, - showSaveSuccess, - ]; + status, + remoteConfig, + originalRemoteConfig, + exception, + isDirty, + showSaveSuccess, + ]; } diff --git a/lib/app_configuration/view/app_configuration_page.dart b/lib/app_configuration/view/app_configuration_page.dart index db72e839..bdbe45bf 100644 --- a/lib/app_configuration/view/app_configuration_page.dart +++ b/lib/app_configuration/view/app_configuration_page.dart @@ -54,8 +54,8 @@ class _AppConfigurationPageState extends State { child: Text( l10n.appConfigurationPageDescription, style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: Theme.of(context).colorScheme.onSurfaceVariant, - ), + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), ), ), ), @@ -71,16 +71,16 @@ class _AppConfigurationPageState extends State { content: Text( l10n.appConfigSaveSuccessMessage, style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: Theme.of(context).colorScheme.onPrimary, - ), + color: Theme.of(context).colorScheme.onPrimary, + ), ), backgroundColor: Theme.of(context).colorScheme.primary, ), ); // Clear the showSaveSuccess flag after showing the snackbar context.read().add( - const AppConfigurationFieldChanged(), - ); + const AppConfigurationFieldChanged(), + ); } else if (state.status == AppConfigurationStatus.failure && state.exception != null) { ScaffoldMessenger.of(context) @@ -90,8 +90,8 @@ class _AppConfigurationPageState extends State { content: Text( state.exception!.toFriendlyMessage(context), style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: Theme.of(context).colorScheme.onError, - ), + color: Theme.of(context).colorScheme.onError, + ), ), backgroundColor: Theme.of(context).colorScheme.error, ), @@ -111,8 +111,8 @@ class _AppConfigurationPageState extends State { exception: state.exception!, onRetry: () { context.read().add( - const AppConfigurationLoaded(), - ); + const AppConfigurationLoaded(), + ); }, ); } else if (state.status == AppConfigurationStatus.success && @@ -190,8 +190,8 @@ class _AppConfigurationPageState extends State { ? () { // Discard changes: revert to original config context.read().add( - const AppConfigurationDiscarded(), - ); + const AppConfigurationDiscarded(), + ); } : null, child: Text(context.l10n.discardChangesButton), @@ -201,10 +201,12 @@ class _AppConfigurationPageState extends State { onPressed: isDirty ? () async { final confirmed = await _showConfirmationDialog(context); - if (context.mounted && confirmed && remoteConfig != null) { + if (context.mounted && + confirmed && + remoteConfig != null) { context.read().add( - AppConfigurationUpdated(remoteConfig), - ); + AppConfigurationUpdated(remoteConfig), + ); } } : null, @@ -262,8 +264,8 @@ class _AppConfigurationPageState extends State { Text( l10n.userContentLimitsDescription, style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: Theme.of(context).colorScheme.onSurface.withOpacity(0.7), - ), + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.7), + ), ), const SizedBox(height: AppSpacing.lg), ExpansionTile( @@ -277,10 +279,10 @@ class _AppConfigurationPageState extends State { remoteConfig: remoteConfig, onConfigChanged: (newConfig) { context.read().add( - AppConfigurationFieldChanged( - remoteConfig: newConfig, - ), - ); + AppConfigurationFieldChanged( + remoteConfig: newConfig, + ), + ); }, buildIntField: _buildIntField, ), @@ -297,10 +299,10 @@ class _AppConfigurationPageState extends State { remoteConfig: remoteConfig, onConfigChanged: (newConfig) { context.read().add( - AppConfigurationFieldChanged( - remoteConfig: newConfig, - ), - ); + AppConfigurationFieldChanged( + remoteConfig: newConfig, + ), + ); }, buildIntField: _buildIntField, ), @@ -317,10 +319,10 @@ class _AppConfigurationPageState extends State { remoteConfig: remoteConfig, onConfigChanged: (newConfig) { context.read().add( - AppConfigurationFieldChanged( - remoteConfig: newConfig, - ), - ); + AppConfigurationFieldChanged( + remoteConfig: newConfig, + ), + ); }, buildIntField: _buildIntField, ), @@ -330,7 +332,10 @@ class _AppConfigurationPageState extends State { ); } - Widget _buildAdConfigSection(BuildContext context, RemoteConfig remoteConfig) { + Widget _buildAdConfigSection( + BuildContext context, + RemoteConfig remoteConfig, + ) { final l10n = context.l10n; return Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -338,8 +343,8 @@ class _AppConfigurationPageState extends State { Text( l10n.adSettingsDescription, style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: Theme.of(context).colorScheme.onSurface.withOpacity(0.7), - ), + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.7), + ), ), const SizedBox(height: AppSpacing.lg), ExpansionTile( @@ -353,10 +358,10 @@ class _AppConfigurationPageState extends State { remoteConfig: remoteConfig, onConfigChanged: (newConfig) { context.read().add( - AppConfigurationFieldChanged( - remoteConfig: newConfig, - ), - ); + AppConfigurationFieldChanged( + remoteConfig: newConfig, + ), + ); }, buildIntField: _buildIntField, ), @@ -373,10 +378,10 @@ class _AppConfigurationPageState extends State { remoteConfig: remoteConfig, onConfigChanged: (newConfig) { context.read().add( - AppConfigurationFieldChanged( - remoteConfig: newConfig, - ), - ); + AppConfigurationFieldChanged( + remoteConfig: newConfig, + ), + ); }, buildIntField: _buildIntField, ), @@ -393,10 +398,10 @@ class _AppConfigurationPageState extends State { remoteConfig: remoteConfig, onConfigChanged: (newConfig) { context.read().add( - AppConfigurationFieldChanged( - remoteConfig: newConfig, - ), - ); + AppConfigurationFieldChanged( + remoteConfig: newConfig, + ), + ); }, buildIntField: _buildIntField, ), @@ -417,8 +422,8 @@ class _AppConfigurationPageState extends State { Text( l10n.inAppPromptsDescription, style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: Theme.of(context).colorScheme.onSurface.withOpacity(0.7), - ), + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.7), + ), ), const SizedBox(height: AppSpacing.lg), ExpansionTile( @@ -432,10 +437,10 @@ class _AppConfigurationPageState extends State { remoteConfig: remoteConfig, onConfigChanged: (newConfig) { context.read().add( - AppConfigurationFieldChanged( - remoteConfig: newConfig, - ), - ); + AppConfigurationFieldChanged( + remoteConfig: newConfig, + ), + ); }, buildIntField: _buildIntField, ), @@ -452,10 +457,10 @@ class _AppConfigurationPageState extends State { remoteConfig: remoteConfig, onConfigChanged: (newConfig) { context.read().add( - AppConfigurationFieldChanged( - remoteConfig: newConfig, - ), - ); + AppConfigurationFieldChanged( + remoteConfig: newConfig, + ), + ); }, buildIntField: _buildIntField, ), @@ -466,7 +471,9 @@ class _AppConfigurationPageState extends State { } Widget _buildAppStatusSection( - BuildContext context, RemoteConfig remoteConfig) { + BuildContext context, + RemoteConfig remoteConfig, + ) { final l10n = context.l10n; return SingleChildScrollView( padding: const EdgeInsets.all(AppSpacing.lg), @@ -476,9 +483,9 @@ class _AppConfigurationPageState extends State { Text( l10n.appOperationalStatusWarning, style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: Theme.of(context).colorScheme.error, - fontWeight: FontWeight.bold, - ), + color: Theme.of(context).colorScheme.error, + fontWeight: FontWeight.bold, + ), ), const SizedBox(height: AppSpacing.lg), SwitchListTile( @@ -487,14 +494,14 @@ class _AppConfigurationPageState extends State { value: remoteConfig.appStatus.isUnderMaintenance, onChanged: (value) { context.read().add( - AppConfigurationFieldChanged( - remoteConfig: remoteConfig.copyWith( - appStatus: remoteConfig.appStatus.copyWith( - isUnderMaintenance: value, - ), - ), + AppConfigurationFieldChanged( + remoteConfig: remoteConfig.copyWith( + appStatus: remoteConfig.appStatus.copyWith( + isUnderMaintenance: value, ), - ); + ), + ), + ); }, ), _buildTextField( @@ -504,14 +511,14 @@ class _AppConfigurationPageState extends State { value: remoteConfig.appStatus.latestAppVersion, onChanged: (value) { context.read().add( - AppConfigurationFieldChanged( - remoteConfig: remoteConfig.copyWith( - appStatus: remoteConfig.appStatus.copyWith( - latestAppVersion: value, - ), - ), + AppConfigurationFieldChanged( + remoteConfig: remoteConfig.copyWith( + appStatus: remoteConfig.appStatus.copyWith( + latestAppVersion: value, ), - ); + ), + ), + ); }, ), SwitchListTile( @@ -520,14 +527,14 @@ class _AppConfigurationPageState extends State { value: remoteConfig.appStatus.isLatestVersionOnly, onChanged: (value) { context.read().add( - AppConfigurationFieldChanged( - remoteConfig: remoteConfig.copyWith( - appStatus: remoteConfig.appStatus.copyWith( - isLatestVersionOnly: value, - ), - ), + AppConfigurationFieldChanged( + remoteConfig: remoteConfig.copyWith( + appStatus: remoteConfig.appStatus.copyWith( + isLatestVersionOnly: value, ), - ); + ), + ), + ); }, ), _buildTextField( @@ -537,14 +544,14 @@ class _AppConfigurationPageState extends State { value: remoteConfig.appStatus.iosUpdateUrl, onChanged: (value) { context.read().add( - AppConfigurationFieldChanged( - remoteConfig: remoteConfig.copyWith( - appStatus: remoteConfig.appStatus.copyWith( - iosUpdateUrl: value, - ), - ), + AppConfigurationFieldChanged( + remoteConfig: remoteConfig.copyWith( + appStatus: remoteConfig.appStatus.copyWith( + iosUpdateUrl: value, ), - ); + ), + ), + ); }, ), _buildTextField( @@ -554,14 +561,14 @@ class _AppConfigurationPageState extends State { value: remoteConfig.appStatus.androidUpdateUrl, onChanged: (value) { context.read().add( - AppConfigurationFieldChanged( - remoteConfig: remoteConfig.copyWith( - appStatus: remoteConfig.appStatus.copyWith( - androidUpdateUrl: value, - ), - ), + AppConfigurationFieldChanged( + remoteConfig: remoteConfig.copyWith( + appStatus: remoteConfig.appStatus.copyWith( + androidUpdateUrl: value, ), - ); + ), + ), + ); }, ), ], @@ -590,9 +597,8 @@ class _AppConfigurationPageState extends State { Text( description, style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: - Theme.of(context).colorScheme.onSurface.withOpacity(0.7), - ), + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.7), + ), ), const SizedBox(height: AppSpacing.xs), TextFormField( @@ -636,9 +642,8 @@ class _AppConfigurationPageState extends State { Text( description, style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: - Theme.of(context).colorScheme.onSurface.withOpacity(0.7), - ), + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.7), + ), ), const SizedBox(height: AppSpacing.xs), TextFormField( @@ -674,7 +679,8 @@ class _UserPreferenceLimitsForm extends StatefulWidget { required int value, required ValueChanged onChanged, TextEditingController? controller, - }) buildIntField; + }) + buildIntField; @override State<_UserPreferenceLimitsForm> createState() => @@ -704,20 +710,26 @@ class _UserPreferenceLimitsFormState extends State<_UserPreferenceLimitsForm> { final config = widget.remoteConfig.userPreferenceConfig; switch (widget.userRole) { case AppUserRole.guestUser: - _followedItemsLimitController = - TextEditingController(text: config.guestFollowedItemsLimit.toString()); - _savedHeadlinesLimitController = - TextEditingController(text: config.guestSavedHeadlinesLimit.toString()); + _followedItemsLimitController = TextEditingController( + text: config.guestFollowedItemsLimit.toString(), + ); + _savedHeadlinesLimitController = TextEditingController( + text: config.guestSavedHeadlinesLimit.toString(), + ); case AppUserRole.standardUser: _followedItemsLimitController = TextEditingController( - text: config.authenticatedFollowedItemsLimit.toString()); + text: config.authenticatedFollowedItemsLimit.toString(), + ); _savedHeadlinesLimitController = TextEditingController( - text: config.authenticatedSavedHeadlinesLimit.toString()); + text: config.authenticatedSavedHeadlinesLimit.toString(), + ); case AppUserRole.premiumUser: _followedItemsLimitController = TextEditingController( - text: config.premiumFollowedItemsLimit.toString()); + text: config.premiumFollowedItemsLimit.toString(), + ); _savedHeadlinesLimitController = TextEditingController( - text: config.premiumSavedHeadlinesLimit.toString()); + text: config.premiumSavedHeadlinesLimit.toString(), + ); } } @@ -725,20 +737,22 @@ class _UserPreferenceLimitsFormState extends State<_UserPreferenceLimitsForm> { final config = widget.remoteConfig.userPreferenceConfig; switch (widget.userRole) { case AppUserRole.guestUser: - _followedItemsLimitController.text = - config.guestFollowedItemsLimit.toString(); - _savedHeadlinesLimitController.text = - config.guestSavedHeadlinesLimit.toString(); + _followedItemsLimitController.text = config.guestFollowedItemsLimit + .toString(); + _savedHeadlinesLimitController.text = config.guestSavedHeadlinesLimit + .toString(); case AppUserRole.standardUser: - _followedItemsLimitController.text = - config.authenticatedFollowedItemsLimit.toString(); - _savedHeadlinesLimitController.text = - config.authenticatedSavedHeadlinesLimit.toString(); + _followedItemsLimitController.text = config + .authenticatedFollowedItemsLimit + .toString(); + _savedHeadlinesLimitController.text = config + .authenticatedSavedHeadlinesLimit + .toString(); case AppUserRole.premiumUser: - _followedItemsLimitController.text = - config.premiumFollowedItemsLimit.toString(); - _savedHeadlinesLimitController.text = - config.premiumSavedHeadlinesLimit.toString(); + _followedItemsLimitController.text = config.premiumFollowedItemsLimit + .toString(); + _savedHeadlinesLimitController.text = config.premiumSavedHeadlinesLimit + .toString(); } } @@ -765,8 +779,10 @@ class _UserPreferenceLimitsFormState extends State<_UserPreferenceLimitsForm> { onChanged: (value) { widget.onConfigChanged( widget.remoteConfig.copyWith( - userPreferenceConfig: - _updateFollowedItemsLimit(userPreferenceConfig, value), + userPreferenceConfig: _updateFollowedItemsLimit( + userPreferenceConfig, + value, + ), ), ); }, @@ -775,14 +791,15 @@ class _UserPreferenceLimitsFormState extends State<_UserPreferenceLimitsForm> { widget.buildIntField( context, label: 'Saved Headlines Limit', - description: - 'Maximum number of headlines this user role can save.', + description: 'Maximum number of headlines this user role can save.', value: _getSavedHeadlinesLimit(userPreferenceConfig), onChanged: (value) { widget.onConfigChanged( widget.remoteConfig.copyWith( - userPreferenceConfig: - _updateSavedHeadlinesLimit(userPreferenceConfig, value), + userPreferenceConfig: _updateSavedHeadlinesLimit( + userPreferenceConfig, + value, + ), ), ); }, @@ -815,7 +832,9 @@ class _UserPreferenceLimitsFormState extends State<_UserPreferenceLimitsForm> { } UserPreferenceConfig _updateFollowedItemsLimit( - UserPreferenceConfig config, int value) { + UserPreferenceConfig config, + int value, + ) { switch (widget.userRole) { case AppUserRole.guestUser: return config.copyWith(guestFollowedItemsLimit: value); @@ -827,7 +846,9 @@ class _UserPreferenceLimitsFormState extends State<_UserPreferenceLimitsForm> { } UserPreferenceConfig _updateSavedHeadlinesLimit( - UserPreferenceConfig config, int value) { + UserPreferenceConfig config, + int value, + ) { switch (widget.userRole) { case AppUserRole.guestUser: return config.copyWith(guestSavedHeadlinesLimit: value); @@ -857,7 +878,8 @@ class _AdConfigForm extends StatefulWidget { required int value, required ValueChanged onChanged, TextEditingController? controller, - }) buildIntField; + }) + buildIntField; @override State<_AdConfigForm> createState() => _AdConfigFormState(); @@ -867,7 +889,7 @@ class _AdConfigFormState extends State<_AdConfigForm> { late final TextEditingController _adFrequencyController; late final TextEditingController _adPlacementIntervalController; late final TextEditingController - _articlesToReadBeforeShowingInterstitialAdsController; + _articlesToReadBeforeShowingInterstitialAdsController; @override void initState() { @@ -887,34 +909,43 @@ class _AdConfigFormState extends State<_AdConfigForm> { final adConfig = widget.remoteConfig.adConfig; switch (widget.userRole) { case AppUserRole.guestUser: - _adFrequencyController = - TextEditingController(text: adConfig.guestAdFrequency.toString()); + _adFrequencyController = TextEditingController( + text: adConfig.guestAdFrequency.toString(), + ); _adPlacementIntervalController = TextEditingController( - text: adConfig.guestAdPlacementInterval.toString()); + text: adConfig.guestAdPlacementInterval.toString(), + ); _articlesToReadBeforeShowingInterstitialAdsController = TextEditingController( - text: adConfig.guestArticlesToReadBeforeShowingInterstitialAds - .toString()); + text: adConfig.guestArticlesToReadBeforeShowingInterstitialAds + .toString(), + ); case AppUserRole.standardUser: _adFrequencyController = TextEditingController( - text: adConfig.authenticatedAdFrequency.toString()); + text: adConfig.authenticatedAdFrequency.toString(), + ); _adPlacementIntervalController = TextEditingController( - text: adConfig.authenticatedAdPlacementInterval.toString()); + text: adConfig.authenticatedAdPlacementInterval.toString(), + ); _articlesToReadBeforeShowingInterstitialAdsController = TextEditingController( - text: adConfig - .standardUserArticlesToReadBeforeShowingInterstitialAds - .toString()); + text: adConfig + .standardUserArticlesToReadBeforeShowingInterstitialAds + .toString(), + ); case AppUserRole.premiumUser: - _adFrequencyController = - TextEditingController(text: adConfig.premiumAdFrequency.toString()); + _adFrequencyController = TextEditingController( + text: adConfig.premiumAdFrequency.toString(), + ); _adPlacementIntervalController = TextEditingController( - text: adConfig.premiumAdPlacementInterval.toString()); + text: adConfig.premiumAdPlacementInterval.toString(), + ); _articlesToReadBeforeShowingInterstitialAdsController = TextEditingController( - text: adConfig - .premiumUserArticlesToReadBeforeShowingInterstitialAds - .toString()); + text: adConfig + .premiumUserArticlesToReadBeforeShowingInterstitialAds + .toString(), + ); } } @@ -923,23 +954,25 @@ class _AdConfigFormState extends State<_AdConfigForm> { switch (widget.userRole) { case AppUserRole.guestUser: _adFrequencyController.text = adConfig.guestAdFrequency.toString(); - _adPlacementIntervalController.text = - adConfig.guestAdPlacementInterval.toString(); + _adPlacementIntervalController.text = adConfig.guestAdPlacementInterval + .toString(); _articlesToReadBeforeShowingInterstitialAdsController.text = adConfig .guestArticlesToReadBeforeShowingInterstitialAds .toString(); case AppUserRole.standardUser: - _adFrequencyController.text = - adConfig.authenticatedAdFrequency.toString(); - _adPlacementIntervalController.text = - adConfig.authenticatedAdPlacementInterval.toString(); + _adFrequencyController.text = adConfig.authenticatedAdFrequency + .toString(); + _adPlacementIntervalController.text = adConfig + .authenticatedAdPlacementInterval + .toString(); _articlesToReadBeforeShowingInterstitialAdsController.text = adConfig .standardUserArticlesToReadBeforeShowingInterstitialAds .toString(); case AppUserRole.premiumUser: _adFrequencyController.text = adConfig.premiumAdFrequency.toString(); - _adPlacementIntervalController.text = - adConfig.premiumAdPlacementInterval.toString(); + _adPlacementIntervalController.text = adConfig + .premiumAdPlacementInterval + .toString(); _articlesToReadBeforeShowingInterstitialAdsController.text = adConfig .premiumUserArticlesToReadBeforeShowingInterstitialAds .toString(); @@ -1071,13 +1104,16 @@ class _AdConfigFormState extends State<_AdConfigForm> { switch (widget.userRole) { case AppUserRole.guestUser: return config.copyWith( - guestArticlesToReadBeforeShowingInterstitialAds: value); + guestArticlesToReadBeforeShowingInterstitialAds: value, + ); case AppUserRole.standardUser: return config.copyWith( - standardUserArticlesToReadBeforeShowingInterstitialAds: value); + standardUserArticlesToReadBeforeShowingInterstitialAds: value, + ); case AppUserRole.premiumUser: return config.copyWith( - premiumUserArticlesToReadBeforeShowingInterstitialAds: value); + premiumUserArticlesToReadBeforeShowingInterstitialAds: value, + ); } } } @@ -1100,7 +1136,8 @@ class _AccountActionConfigForm extends StatefulWidget { required int value, required ValueChanged onChanged, TextEditingController? controller, - }) buildIntField; + }) + buildIntField; @override State<_AccountActionConfigForm> createState() => @@ -1164,15 +1201,16 @@ class _AccountActionConfigFormState extends State<_AccountActionConfigForm> { String _formatLabel(String enumName) { // Converts camelCase to Title Case final spaced = enumName.replaceAllMapped( - RegExp(r'([A-Z])'), (match) => ' ${match.group(1)}'); + RegExp('([A-Z])'), + (match) => ' ${match.group(1)}', + ); return '${spaced[0].toUpperCase()}${spaced.substring(1)} Days'; } @override Widget build(BuildContext context) { final accountActionConfig = widget.remoteConfig.accountActionConfig; - final relevantActionTypes = - _getDaysMap(accountActionConfig).keys.toList(); + final relevantActionTypes = _getDaysMap(accountActionConfig).keys.toList(); return Column( children: relevantActionTypes.map((actionType) { @@ -1189,9 +1227,11 @@ class _AccountActionConfigFormState extends State<_AccountActionConfigForm> { final newConfig = widget.userRole == AppUserRole.guestUser ? accountActionConfig.copyWith( - guestDaysBetweenActions: updatedMap) + guestDaysBetweenActions: updatedMap, + ) : accountActionConfig.copyWith( - standardUserDaysBetweenActions: updatedMap); + standardUserDaysBetweenActions: updatedMap, + ); widget.onConfigChanged( widget.remoteConfig.copyWith(accountActionConfig: newConfig), diff --git a/lib/authentication/bloc/authentication_bloc.dart b/lib/authentication/bloc/authentication_bloc.dart index 9e951855..5a25421c 100644 --- a/lib/authentication/bloc/authentication_bloc.dart +++ b/lib/authentication/bloc/authentication_bloc.dart @@ -3,7 +3,19 @@ import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:ht_auth_repository/ht_auth_repository.dart'; -import 'package:ht_shared/ht_shared.dart' show AuthenticationException, ForbiddenException, HtHttpException, InvalidInputException, NetworkException, NotFoundException, OperationFailedException, ServerException, UnauthorizedException, UnknownException, User; +import 'package:ht_shared/ht_shared.dart' + show + AuthenticationException, + ForbiddenException, + HtHttpException, + InvalidInputException, + NetworkException, + NotFoundException, + OperationFailedException, + ServerException, + UnauthorizedException, + UnknownException, + User; part 'authentication_event.dart'; part 'authentication_state.dart'; diff --git a/lib/authentication/view/authentication_page.dart b/lib/authentication/view/authentication_page.dart index 0d3c9374..badc86e8 100644 --- a/lib/authentication/view/authentication_page.dart +++ b/lib/authentication/view/authentication_page.dart @@ -1,12 +1,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; -import 'package:flutter/foundation.dart'; import 'package:ht_dashboard/authentication/bloc/authentication_bloc.dart'; import 'package:ht_dashboard/l10n/l10n.dart'; -import 'package:ht_ui_kit/ht_ui_kit.dart'; import 'package:ht_dashboard/router/routes.dart'; import 'package:ht_dashboard/shared/constants/app_spacing.dart'; +import 'package:ht_ui_kit/ht_ui_kit.dart'; /// {@template authentication_page} /// Displays authentication options for the dashboard. diff --git a/lib/authentication/view/email_code_verification_page.dart b/lib/authentication/view/email_code_verification_page.dart index 67d81db8..33a3ec4c 100644 --- a/lib/authentication/view/email_code_verification_page.dart +++ b/lib/authentication/view/email_code_verification_page.dart @@ -5,8 +5,8 @@ import 'package:ht_dashboard/app/bloc/app_bloc.dart'; import 'package:ht_dashboard/app/config/config.dart'; import 'package:ht_dashboard/authentication/bloc/authentication_bloc.dart'; import 'package:ht_dashboard/l10n/l10n.dart'; -import 'package:ht_ui_kit/ht_ui_kit.dart'; import 'package:ht_dashboard/shared/constants/app_spacing.dart'; +import 'package:ht_ui_kit/ht_ui_kit.dart'; /// {@template email_code_verification_page} /// Page where the user enters the 6-digit code sent to their email diff --git a/lib/content_management/bloc/create_headline/create_headline_bloc.dart b/lib/content_management/bloc/create_headline/create_headline_bloc.dart index c4c9341b..d3b34885 100644 --- a/lib/content_management/bloc/create_headline/create_headline_bloc.dart +++ b/lib/content_management/bloc/create_headline/create_headline_bloc.dart @@ -17,11 +17,11 @@ class CreateHeadlineBloc required HtDataRepository sourcesRepository, required HtDataRepository topicsRepository, required HtDataRepository countriesRepository, - }) : _headlinesRepository = headlinesRepository, - _sourcesRepository = sourcesRepository, - _topicsRepository = topicsRepository, - _countriesRepository = countriesRepository, - super(const CreateHeadlineState()) { + }) : _headlinesRepository = headlinesRepository, + _sourcesRepository = sourcesRepository, + _topicsRepository = topicsRepository, + _countriesRepository = countriesRepository, + super(const CreateHeadlineState()) { on(_onDataLoaded); on(_onTitleChanged); on(_onExcerptChanged); @@ -46,8 +46,11 @@ class CreateHeadlineBloc ) async { emit(state.copyWith(status: CreateHeadlineStatus.loading)); try { - final [sourcesResponse, topicsResponse, countriesResponse] = - await Future.wait([ + final [ + sourcesResponse, + topicsResponse, + countriesResponse, + ] = await Future.wait([ _sourcesRepository.readAll(), _topicsRepository.readAll(), _countriesRepository.readAll(), @@ -55,8 +58,7 @@ class CreateHeadlineBloc final sources = (sourcesResponse as PaginatedResponse).items; final topics = (topicsResponse as PaginatedResponse).items; - final countries = - (countriesResponse as PaginatedResponse).items; + final countries = (countriesResponse as PaginatedResponse).items; emit( state.copyWith( diff --git a/lib/content_management/bloc/create_headline/create_headline_state.dart b/lib/content_management/bloc/create_headline/create_headline_state.dart index c70890c3..72934826 100644 --- a/lib/content_management/bloc/create_headline/create_headline_state.dart +++ b/lib/content_management/bloc/create_headline/create_headline_state.dart @@ -98,19 +98,19 @@ final class CreateHeadlineState extends Equatable { @override List get props => [ - status, - title, - excerpt, - url, - imageUrl, - source, - topic, - eventCountry, - sources, - topics, - countries, - contentStatus, - exception, - createdHeadline, - ]; + status, + title, + excerpt, + url, + imageUrl, + source, + topic, + eventCountry, + sources, + topics, + countries, + contentStatus, + exception, + createdHeadline, + ]; } diff --git a/lib/content_management/bloc/create_topic/create_topic_bloc.dart b/lib/content_management/bloc/create_topic/create_topic_bloc.dart index 6d6d51fa..92bc7b0b 100644 --- a/lib/content_management/bloc/create_topic/create_topic_bloc.dart +++ b/lib/content_management/bloc/create_topic/create_topic_bloc.dart @@ -12,8 +12,8 @@ class CreateTopicBloc extends Bloc { /// {@macro create_topic_bloc} CreateTopicBloc({ required HtDataRepository topicsRepository, - }) : _topicsRepository = topicsRepository, - super(const CreateTopicState()) { + }) : _topicsRepository = topicsRepository, + super(const CreateTopicState()) { on(_onNameChanged); on(_onDescriptionChanged); on(_onIconUrlChanged); diff --git a/lib/content_management/bloc/create_topic/create_topic_state.dart b/lib/content_management/bloc/create_topic/create_topic_state.dart index 120890ba..a25b92fb 100644 --- a/lib/content_management/bloc/create_topic/create_topic_state.dart +++ b/lib/content_management/bloc/create_topic/create_topic_state.dart @@ -63,12 +63,12 @@ final class CreateTopicState extends Equatable { @override List get props => [ - status, - name, - description, - iconUrl, - contentStatus, - exception, - createdTopic, - ]; + status, + name, + description, + iconUrl, + contentStatus, + exception, + createdTopic, + ]; } diff --git a/lib/content_management/bloc/edit_headline/edit_headline_bloc.dart b/lib/content_management/bloc/edit_headline/edit_headline_bloc.dart index 1936d964..6e8fe244 100644 --- a/lib/content_management/bloc/edit_headline/edit_headline_bloc.dart +++ b/lib/content_management/bloc/edit_headline/edit_headline_bloc.dart @@ -196,7 +196,9 @@ class EditHeadlineBloc extends Bloc { emit( state.copyWith( status: EditHeadlineStatus.failure, - exception: UnknownException('Cannot update: Original headline data not loaded.'), + exception: const UnknownException( + 'Cannot update: Original headline data not loaded.', + ), ), ); return; diff --git a/lib/content_management/bloc/edit_headline/edit_headline_state.dart b/lib/content_management/bloc/edit_headline/edit_headline_state.dart index 4bf778b0..ffbccc3c 100644 --- a/lib/content_management/bloc/edit_headline/edit_headline_state.dart +++ b/lib/content_management/bloc/edit_headline/edit_headline_state.dart @@ -102,20 +102,20 @@ final class EditHeadlineState extends Equatable { @override List get props => [ - status, - initialHeadline, - title, - excerpt, - url, - imageUrl, - source, - topic, - eventCountry, - sources, - topics, - countries, - contentStatus, - exception, - updatedHeadline, - ]; + status, + initialHeadline, + title, + excerpt, + url, + imageUrl, + source, + topic, + eventCountry, + sources, + topics, + countries, + contentStatus, + exception, + updatedHeadline, + ]; } diff --git a/lib/content_management/bloc/edit_source/edit_source_bloc.dart b/lib/content_management/bloc/edit_source/edit_source_bloc.dart index 08652207..1181a03c 100644 --- a/lib/content_management/bloc/edit_source/edit_source_bloc.dart +++ b/lib/content_management/bloc/edit_source/edit_source_bloc.dart @@ -162,7 +162,9 @@ class EditSourceBloc extends Bloc { emit( state.copyWith( status: EditSourceStatus.failure, - exception: UnknownException('Cannot update: Original source data not loaded.'), + exception: const UnknownException( + 'Cannot update: Original source data not loaded.', + ), ), ); return; diff --git a/lib/content_management/bloc/edit_topic/edit_topic_bloc.dart b/lib/content_management/bloc/edit_topic/edit_topic_bloc.dart index e28f1eb4..a15308f7 100644 --- a/lib/content_management/bloc/edit_topic/edit_topic_bloc.dart +++ b/lib/content_management/bloc/edit_topic/edit_topic_bloc.dart @@ -12,9 +12,9 @@ class EditTopicBloc extends Bloc { EditTopicBloc({ required HtDataRepository topicsRepository, required String topicId, - }) : _topicsRepository = topicsRepository, - _topicId = topicId, - super(const EditTopicState()) { + }) : _topicsRepository = topicsRepository, + _topicId = topicId, + super(const EditTopicState()) { on(_onLoaded); on(_onNameChanged); on(_onDescriptionChanged); @@ -121,7 +121,9 @@ class EditTopicBloc extends Bloc { emit( state.copyWith( status: EditTopicStatus.failure, - exception: UnknownException('Cannot update: Original topic data not loaded.'), + exception: const UnknownException( + 'Cannot update: Original topic data not loaded.', + ), ), ); return; diff --git a/lib/content_management/bloc/edit_topic/edit_topic_state.dart b/lib/content_management/bloc/edit_topic/edit_topic_state.dart index ce821fb8..e2a6ad38 100644 --- a/lib/content_management/bloc/edit_topic/edit_topic_state.dart +++ b/lib/content_management/bloc/edit_topic/edit_topic_state.dart @@ -69,13 +69,13 @@ final class EditTopicState extends Equatable { @override List get props => [ - status, - initialTopic, - name, - description, - iconUrl, - contentStatus, - exception, - updatedTopic, - ]; + status, + initialTopic, + name, + description, + iconUrl, + contentStatus, + exception, + updatedTopic, + ]; } diff --git a/lib/content_management/view/content_management_page.dart b/lib/content_management/view/content_management_page.dart index 1585bbe6..cfbc6a99 100644 --- a/lib/content_management/view/content_management_page.dart +++ b/lib/content_management/view/content_management_page.dart @@ -2,9 +2,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import 'package:ht_dashboard/content_management/bloc/content_management_bloc.dart'; -import 'package:ht_dashboard/content_management/view/topics_page.dart'; import 'package:ht_dashboard/content_management/view/headlines_page.dart'; import 'package:ht_dashboard/content_management/view/sources_page.dart'; +import 'package:ht_dashboard/content_management/view/topics_page.dart'; import 'package:ht_dashboard/l10n/l10n.dart'; import 'package:ht_dashboard/router/routes.dart'; import 'package:ht_dashboard/shared/constants/app_spacing.dart'; diff --git a/lib/content_management/view/edit_topic_page.dart b/lib/content_management/view/edit_topic_page.dart index 73b76165..e2df650f 100644 --- a/lib/content_management/view/edit_topic_page.dart +++ b/lib/content_management/view/edit_topic_page.dart @@ -108,7 +108,7 @@ class _EditTopicViewState extends State<_EditTopicView> { SnackBar(content: Text(l10n.topicUpdatedSuccessfully)), ); context.read().add( - TopicUpdated(state.updatedTopic!), + TopicUpdated(state.updatedTopic!), ); context.pop(); } @@ -161,9 +161,9 @@ class _EditTopicViewState extends State<_EditTopicView> { labelText: l10n.topicName, border: const OutlineInputBorder(), ), - onChanged: (value) => context - .read() - .add(EditTopicNameChanged(value)), + onChanged: (value) => context.read().add( + EditTopicNameChanged(value), + ), ), const SizedBox(height: AppSpacing.lg), TextFormField( @@ -173,9 +173,9 @@ class _EditTopicViewState extends State<_EditTopicView> { border: const OutlineInputBorder(), ), maxLines: 3, - onChanged: (value) => context - .read() - .add(EditTopicDescriptionChanged(value)), + onChanged: (value) => context.read().add( + EditTopicDescriptionChanged(value), + ), ), const SizedBox(height: AppSpacing.lg), TextFormField( @@ -184,9 +184,9 @@ class _EditTopicViewState extends State<_EditTopicView> { labelText: l10n.iconUrl, border: const OutlineInputBorder(), ), - onChanged: (value) => context - .read() - .add(EditTopicIconUrlChanged(value)), + onChanged: (value) => context.read().add( + EditTopicIconUrlChanged(value), + ), ), const SizedBox(height: AppSpacing.lg), DropdownButtonFormField( diff --git a/lib/content_management/view/topics_page.dart b/lib/content_management/view/topics_page.dart index f28fd315..a6540b1e 100644 --- a/lib/content_management/view/topics_page.dart +++ b/lib/content_management/view/topics_page.dart @@ -208,7 +208,9 @@ class _TopicsDataSource extends DataTableSource { if (hasMore) { // When loading, we show an extra row for the spinner. // Otherwise, we just indicate that there are more rows. - return isLoading ? topics.length + 1 : topics.length + kDefaultRowsPerPage; + return isLoading + ? topics.length + 1 + : topics.length + kDefaultRowsPerPage; } return topics.length; } diff --git a/lib/dashboard/bloc/dashboard_state.dart b/lib/dashboard/bloc/dashboard_state.dart index 7d0f19a7..bfbfe4d9 100644 --- a/lib/dashboard/bloc/dashboard_state.dart +++ b/lib/dashboard/bloc/dashboard_state.dart @@ -49,10 +49,10 @@ final class DashboardState extends Equatable { @override List get props => [ - status, - summary, - appConfig, - recentHeadlines, - exception, - ]; + status, + summary, + appConfig, + recentHeadlines, + exception, + ]; } diff --git a/lib/router/router.dart b/lib/router/router.dart index 67bc9d15..60de4878 100644 --- a/lib/router/router.dart +++ b/lib/router/router.dart @@ -11,12 +11,12 @@ import 'package:ht_dashboard/authentication/view/authentication_page.dart'; import 'package:ht_dashboard/authentication/view/email_code_verification_page.dart'; import 'package:ht_dashboard/authentication/view/request_code_page.dart'; import 'package:ht_dashboard/content_management/view/content_management_page.dart'; -import 'package:ht_dashboard/content_management/view/create_topic_page.dart'; import 'package:ht_dashboard/content_management/view/create_headline_page.dart'; import 'package:ht_dashboard/content_management/view/create_source_page.dart'; -import 'package:ht_dashboard/content_management/view/edit_topic_page.dart'; +import 'package:ht_dashboard/content_management/view/create_topic_page.dart'; import 'package:ht_dashboard/content_management/view/edit_headline_page.dart'; import 'package:ht_dashboard/content_management/view/edit_source_page.dart'; +import 'package:ht_dashboard/content_management/view/edit_topic_page.dart'; import 'package:ht_dashboard/dashboard/view/dashboard_page.dart'; import 'package:ht_dashboard/router/routes.dart'; import 'package:ht_dashboard/settings/view/settings_page.dart';