From 27f0c4df1cc5a73033dbefa72c06576f7cb75a1d Mon Sep 17 00:00:00 2001 From: fulleni Date: Sat, 2 Aug 2025 21:45:29 +0100 Subject: [PATCH 01/14] feat(content_management): add CreateHeadlineDataUpdated event - Add new event to update BLoC with the latest shared data - Include countries list in the event properties - Update event class with necessary overrides --- .../bloc/create_headline/create_headline_event.dart | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/content_management/bloc/create_headline/create_headline_event.dart b/lib/content_management/bloc/create_headline/create_headline_event.dart index 655eae97..840d6792 100644 --- a/lib/content_management/bloc/create_headline/create_headline_event.dart +++ b/lib/content_management/bloc/create_headline/create_headline_event.dart @@ -83,3 +83,13 @@ final class CreateHeadlineStatusChanged extends CreateHeadlineEvent { final class CreateHeadlineSubmitted extends CreateHeadlineEvent { const CreateHeadlineSubmitted(); } + +/// Event to update the BLoC with the latest shared data. +final class CreateHeadlineDataUpdated extends CreateHeadlineEvent { + const CreateHeadlineDataUpdated({required this.countries}); + + final List countries; + + @override + List get props => [countries]; +} From c46fb76749010f3db647a1634b59e93a1a7a5633 Mon Sep 17 00:00:00 2001 From: fulleni Date: Sat, 2 Aug 2025 21:45:47 +0100 Subject: [PATCH 02/14] feat(content_management): add data update handler for headline creation - Implement CreateHeadlineDataUpdated event handling in CreateHeadlineBloc - Update state with new country data when CreateHeadlineDataUpdated event is triggered --- .../bloc/create_headline/create_headline_bloc.dart | 8 ++++++++ 1 file changed, 8 insertions(+) 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 91520e60..aec1ad5b 100644 --- a/lib/content_management/bloc/create_headline/create_headline_bloc.dart +++ b/lib/content_management/bloc/create_headline/create_headline_bloc.dart @@ -31,6 +31,7 @@ class CreateHeadlineBloc on(_onCountryChanged); on(_onStatusChanged); on(_onSubmitted); + on(_onDataUpdated); } final DataRepository _headlinesRepository; @@ -180,4 +181,11 @@ class CreateHeadlineBloc ); } } + + void _onDataUpdated( + CreateHeadlineDataUpdated event, + Emitter emit, + ) { + emit(state.copyWith(countries: event.countries)); + } } From 21441c43a37536909eaa46cd5392c35ca6a4e819 Mon Sep 17 00:00:00 2001 From: fulleni Date: Sat, 2 Aug 2025 21:49:08 +0100 Subject: [PATCH 03/14] feat(content_management): add EditHeadlineDataUpdated event - Add new event to update BLoC with latest shared data - Include countries list in the event properties --- .../bloc/edit_headline/edit_headline_event.dart | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/content_management/bloc/edit_headline/edit_headline_event.dart b/lib/content_management/bloc/edit_headline/edit_headline_event.dart index 6bad7bdd..50b93997 100644 --- a/lib/content_management/bloc/edit_headline/edit_headline_event.dart +++ b/lib/content_management/bloc/edit_headline/edit_headline_event.dart @@ -83,3 +83,13 @@ final class EditHeadlineStatusChanged extends EditHeadlineEvent { final class EditHeadlineSubmitted extends EditHeadlineEvent { const EditHeadlineSubmitted(); } + +/// Event to update the BLoC with the latest shared data. +final class EditHeadlineDataUpdated extends EditHeadlineEvent { + const EditHeadlineDataUpdated({required this.countries}); + + final List countries; + + @override + List get props => [countries]; +} From 341c97beefc4e24477e051a2d03fab03f4158602 Mon Sep 17 00:00:00 2001 From: fulleni Date: Sat, 2 Aug 2025 21:49:18 +0100 Subject: [PATCH 04/14] feat(content_management): add data update handler for headline editing - Implement EditHeadlineDataUpdated event handler in EditHeadlineBloc - Update constructor to listen for EditHeadlineDataUpdated events - Add logic to update state with new country data when received --- .../bloc/edit_headline/edit_headline_bloc.dart | 8 ++++++++ 1 file changed, 8 insertions(+) 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 6fc464f3..3d6a8873 100644 --- a/lib/content_management/bloc/edit_headline/edit_headline_bloc.dart +++ b/lib/content_management/bloc/edit_headline/edit_headline_bloc.dart @@ -31,6 +31,7 @@ class EditHeadlineBloc extends Bloc { on(_onCountryChanged); on(_onStatusChanged); on(_onSubmitted); + on(_onDataUpdated); } final DataRepository _headlinesRepository; @@ -228,4 +229,11 @@ class EditHeadlineBloc extends Bloc { ); } } + + void _onDataUpdated( + EditHeadlineDataUpdated event, + Emitter emit, + ) { + emit(state.copyWith(countries: event.countries)); + } } From 981fc2bcfe7a9cee83fcd7c404305e82d36fc5b8 Mon Sep 17 00:00:00 2001 From: fulleni Date: Sat, 2 Aug 2025 22:16:41 +0100 Subject: [PATCH 05/14] feat(content_management): add data update event for create source BLoC - Implement CreateSourceDataUpdated event to update BLoC with shared data - Add handler for CreateSourceDataUpdated event in CreateSourceBloc - Update countries and languages in CreateSourceState using new event data --- .../bloc/create_source/create_source_bloc.dart | 8 ++++++++ .../bloc/create_source/create_source_event.dart | 14 ++++++++++++++ 2 files changed, 22 insertions(+) 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 0f99f7fa..1af91673 100644 --- a/lib/content_management/bloc/create_source/create_source_bloc.dart +++ b/lib/content_management/bloc/create_source/create_source_bloc.dart @@ -30,6 +30,7 @@ class CreateSourceBloc extends Bloc { on(_onHeadquartersChanged); on(_onStatusChanged); on(_onSubmitted); + on(_onDataUpdated); } final DataRepository _sourcesRepository; @@ -130,4 +131,11 @@ class CreateSourceBloc extends Bloc { ); } } + + void _onDataUpdated( + CreateSourceDataUpdated event, + Emitter emit, + ) { + emit(state.copyWith(countries: event.countries, languages: event.languages)); + } } diff --git a/lib/content_management/bloc/create_source/create_source_event.dart b/lib/content_management/bloc/create_source/create_source_event.dart index c2351b04..be839c91 100644 --- a/lib/content_management/bloc/create_source/create_source_event.dart +++ b/lib/content_management/bloc/create_source/create_source_event.dart @@ -70,3 +70,17 @@ final class CreateSourceStatusChanged extends CreateSourceEvent { final class CreateSourceSubmitted extends CreateSourceEvent { const CreateSourceSubmitted(); } + +/// Event to update the BLoC with the latest shared data. +final class CreateSourceDataUpdated extends CreateSourceEvent { + const CreateSourceDataUpdated({ + required this.countries, + required this.languages, + }); + + final List countries; + final List languages; + + @override + List get props => [countries, languages]; +} From bfd5d8993e2bd29e8d05353cae2894e83d3ba030 Mon Sep 17 00:00:00 2001 From: fulleni Date: Sat, 2 Aug 2025 22:16:54 +0100 Subject: [PATCH 06/14] feat(content_management): add data update event to EditSourceBloc - Implement EditSourceDataUpdated event to update BLoC with latest shared data - Add handler for EditSourceDataUpdated event in EditSourceBloc - Update state with new countries and languages data when event is triggered --- .../bloc/edit_source/edit_source_bloc.dart | 8 ++++++++ .../bloc/edit_source/edit_source_event.dart | 14 ++++++++++++++ 2 files changed, 22 insertions(+) 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 3c5f78b1..961e0957 100644 --- a/lib/content_management/bloc/edit_source/edit_source_bloc.dart +++ b/lib/content_management/bloc/edit_source/edit_source_bloc.dart @@ -32,6 +32,7 @@ class EditSourceBloc extends Bloc { on(_onHeadquartersChanged); on(_onStatusChanged); on(_onSubmitted); + on(_onDataUpdated); } final DataRepository _sourcesRepository; @@ -195,4 +196,11 @@ class EditSourceBloc extends Bloc { ); } } + + void _onDataUpdated( + EditSourceDataUpdated event, + Emitter emit, + ) { + emit(state.copyWith(countries: event.countries, languages: event.languages)); + } } diff --git a/lib/content_management/bloc/edit_source/edit_source_event.dart b/lib/content_management/bloc/edit_source/edit_source_event.dart index 8620d9a5..b9128c3d 100644 --- a/lib/content_management/bloc/edit_source/edit_source_event.dart +++ b/lib/content_management/bloc/edit_source/edit_source_event.dart @@ -86,3 +86,17 @@ final class EditSourceStatusChanged extends EditSourceEvent { final class EditSourceSubmitted extends EditSourceEvent { const EditSourceSubmitted(); } + +/// Event to update the BLoC with the latest shared data. +final class EditSourceDataUpdated extends EditSourceEvent { + const EditSourceDataUpdated({ + required this.countries, + required this.languages, + }); + + final List countries; + final List languages; + + @override + List get props => [countries, languages]; +} From 3740573fc43ce837b8c9a87190217209c0967689 Mon Sep 17 00:00:00 2001 From: fulleni Date: Sat, 2 Aug 2025 22:17:09 +0100 Subject: [PATCH 07/14] refactor(content_management): improve country data handling in create headline page - Remove direct state access to allCountries in ContentManagementBloc - Use BlocListener to update CreateHeadlineBloc when allCountries change in ContentManagementBloc - Simplify CreateHeadlineBloc initialization by directly reading allCountries from context - Add loading indicator for country data in ContentManagementBloc --- .../view/create_headline_page.dart | 45 ++++++++++--------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/lib/content_management/view/create_headline_page.dart b/lib/content_management/view/create_headline_page.dart index 1dcbcd69..b986dae5 100644 --- a/lib/content_management/view/create_headline_page.dart +++ b/lib/content_management/view/create_headline_page.dart @@ -19,18 +19,12 @@ class CreateHeadlinePage extends StatelessWidget { @override Widget build(BuildContext context) { - // The list of all countries is fetched once and cached in the - // ContentManagementBloc. We read it here and provide it to the - // CreateHeadlineBloc. - final contentManagementState = context.watch().state; - final allCountries = contentManagementState.allCountries; - return BlocProvider( create: (context) => CreateHeadlineBloc( headlinesRepository: context.read>(), sourcesRepository: context.read>(), topicsRepository: context.read>(), - countries: allCountries, + countries: context.read().state.allCountries, )..add(const CreateHeadlineDataLoaded()), child: const _CreateHeadlineView(), ); @@ -79,12 +73,21 @@ class _CreateHeadlineViewState extends State<_CreateHeadlineView> { ), ], ), - body: BlocConsumer( - listenWhen: (previous, current) => previous.status != current.status, - listener: (context, state) { - if (state.status == CreateHeadlineStatus.success && - state.createdHeadline != null && - ModalRoute.of(context)!.isCurrent) { + body: BlocListener( + listenWhen: (previous, current) => + previous.allCountriesStatus != current.allCountriesStatus && + current.allCountriesStatus == ContentManagementStatus.success, + listener: (context, contentState) { + context.read().add( + CreateHeadlineDataUpdated(countries: contentState.allCountries), + ); + }, + child: BlocConsumer( + listenWhen: (previous, current) => previous.status != current.status, + listener: (context, state) { + if (state.status == CreateHeadlineStatus.success && + state.createdHeadline != null && + ModalRoute.of(context)!.isCurrent) { ScaffoldMessenger.of(context) ..hideCurrentSnackBar() ..showSnackBar( @@ -107,14 +110,14 @@ class _CreateHeadlineViewState extends State<_CreateHeadlineView> { ); } }, - builder: (context, state) { - if (state.status == CreateHeadlineStatus.loading) { - return LoadingStateWidget( - icon: Icons.newspaper, - headline: l10n.loadingData, - subheadline: l10n.pleaseWait, - ); - } + builder: (context, state) { + if (state.status == CreateHeadlineStatus.loading) { + return LoadingStateWidget( + icon: Icons.newspaper, + headline: l10n.loadingData, + subheadline: l10n.pleaseWait, + ); + } if (state.status == CreateHeadlineStatus.failure && state.sources.isEmpty && From ab20f6c575dad05c1220520709f861f9624732f7 Mon Sep 17 00:00:00 2001 From: fulleni Date: Sat, 2 Aug 2025 22:17:21 +0100 Subject: [PATCH 08/14] refactor(content_management): improve headline editing with dynamic country data - Remove direct state access for allCountries in EditHeadlinePage - Implement BlocListener for ContentManagementBloc to update countries data - Refactor widget tree to include new BlocListener --- .../view/edit_headline_page.dart | 47 ++++++++++--------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/lib/content_management/view/edit_headline_page.dart b/lib/content_management/view/edit_headline_page.dart index 5c9d10fe..9308d7b9 100644 --- a/lib/content_management/view/edit_headline_page.dart +++ b/lib/content_management/view/edit_headline_page.dart @@ -22,18 +22,12 @@ class EditHeadlinePage extends StatelessWidget { @override Widget build(BuildContext context) { - // The list of all countries is fetched once and cached in the - // ContentManagementBloc. We read it here and provide it to the - // EditHeadlineBloc. - final contentManagementState = context.watch().state; - final allCountries = contentManagementState.allCountries; - return BlocProvider( create: (context) => EditHeadlineBloc( headlinesRepository: context.read>(), sourcesRepository: context.read>(), topicsRepository: context.read>(), - countries: allCountries, + countries: context.read().state.allCountries, headlineId: headlineId, )..add(const EditHeadlineLoaded()), child: const _EditHeadlineView(), @@ -106,14 +100,23 @@ class _EditHeadlineViewState extends State<_EditHeadlineView> { ), ], ), - body: BlocConsumer( + body: BlocListener( listenWhen: (previous, current) => - previous.status != current.status || - previous.initialHeadline != current.initialHeadline, - listener: (context, state) { - if (state.status == EditHeadlineStatus.success && - state.updatedHeadline != null && - ModalRoute.of(context)!.isCurrent) { + previous.allCountriesStatus != current.allCountriesStatus && + current.allCountriesStatus == ContentManagementStatus.success, + listener: (context, contentState) { + context.read().add( + EditHeadlineDataUpdated(countries: contentState.allCountries), + ); + }, + child: BlocConsumer( + listenWhen: (previous, current) => + previous.status != current.status || + previous.initialHeadline != current.initialHeadline, + listener: (context, state) { + if (state.status == EditHeadlineStatus.success && + state.updatedHeadline != null && + ModalRoute.of(context)!.isCurrent) { ScaffoldMessenger.of(context) ..hideCurrentSnackBar() ..showSnackBar( @@ -141,14 +144,14 @@ class _EditHeadlineViewState extends State<_EditHeadlineView> { _imageUrlController.text = state.imageUrl; } }, - builder: (context, state) { - if (state.status == EditHeadlineStatus.loading) { - return LoadingStateWidget( - icon: Icons.newspaper, - headline: l10n.loadingHeadline, - subheadline: l10n.pleaseWait, - ); - } + builder: (context, state) { + if (state.status == EditHeadlineStatus.loading) { + return LoadingStateWidget( + icon: Icons.newspaper, + headline: l10n.loadingHeadline, + subheadline: l10n.pleaseWait, + ); + } if (state.status == EditHeadlineStatus.failure && state.initialHeadline == null) { From 45bda50c149a013087e684e0543ef72dcb5d1abf Mon Sep 17 00:00:00 2001 From: fulleni Date: Sat, 2 Aug 2025 22:20:55 +0100 Subject: [PATCH 09/14] refactor(content_management): improve source editing with dynamic data - Remove direct access to ContentManagementBloc state for country and language lists - Use BlocListener for ContentManagementBloc to update EditSourceBloc with new data - Add EditSourceDataUpdated event to handle updated country and language lists - Maintain existing functionality for source editing and status updates --- .../view/edit_source_page.dart | 55 +++++++++++-------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/lib/content_management/view/edit_source_page.dart b/lib/content_management/view/edit_source_page.dart index feca72a2..bcf14734 100644 --- a/lib/content_management/view/edit_source_page.dart +++ b/lib/content_management/view/edit_source_page.dart @@ -23,18 +23,11 @@ class EditSourcePage extends StatelessWidget { @override Widget build(BuildContext context) { - // The lists of all countries and languages are fetched once and cached in - // the ContentManagementBloc. We read them here and provide them to the - // EditSourceBloc. - final contentManagementState = context.read().state; - final allCountries = contentManagementState.allCountries; - final allLanguages = contentManagementState.allLanguages; - return BlocProvider( create: (context) => EditSourceBloc( sourcesRepository: context.read>(), - countries: allCountries, - languages: allLanguages, + countries: context.read().state.allCountries, + languages: context.read().state.allLanguages, sourceId: sourceId, )..add(const EditSourceLoaded()), child: const _EditSourceView(), @@ -104,14 +97,28 @@ class _EditSourceViewState extends State<_EditSourceView> { ), ], ), - body: BlocConsumer( + body: BlocListener( listenWhen: (previous, current) => - previous.status != current.status || - previous.initialSource != current.initialSource, - listener: (context, state) { - if (state.status == EditSourceStatus.success && - state.updatedSource != null && - ModalRoute.of(context)!.isCurrent) { + (previous.allCountriesStatus != current.allCountriesStatus && + current.allCountriesStatus == ContentManagementStatus.success) || + (previous.allLanguagesStatus != current.allLanguagesStatus && + current.allLanguagesStatus == ContentManagementStatus.success), + listener: (context, contentState) { + context.read().add( + EditSourceDataUpdated( + countries: contentState.allCountries, + languages: contentState.allLanguages, + ), + ); + }, + child: BlocConsumer( + listenWhen: (previous, current) => + previous.status != current.status || + previous.initialSource != current.initialSource, + listener: (context, state) { + if (state.status == EditSourceStatus.success && + state.updatedSource != null && + ModalRoute.of(context)!.isCurrent) { ScaffoldMessenger.of(context) ..hideCurrentSnackBar() ..showSnackBar( @@ -138,14 +145,14 @@ class _EditSourceViewState extends State<_EditSourceView> { _urlController.text = state.url; } }, - builder: (context, state) { - if (state.status == EditSourceStatus.loading) { - return LoadingStateWidget( - icon: Icons.source, - headline: l10n.loadingSource, - subheadline: l10n.pleaseWait, - ); - } + builder: (context, state) { + if (state.status == EditSourceStatus.loading) { + return LoadingStateWidget( + icon: Icons.source, + headline: l10n.loadingSource, + subheadline: l10n.pleaseWait, + ); + } if (state.status == EditSourceStatus.failure && state.initialSource == null) { From 6358a8ca3c48ac5e8c6811b86d555d5c467370ce Mon Sep 17 00:00:00 2001 From: fulleni Date: Sat, 2 Aug 2025 22:21:05 +0100 Subject: [PATCH 10/14] refactor(content_management): improve source creation page - Move country and language data fetching logic to BlocListener - Update CreateSourceBloc with latest data from ContentManagementBloc - Enhance code readability and maintainability --- .../view/create_source_page.dart | 53 +++++++++++-------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/lib/content_management/view/create_source_page.dart b/lib/content_management/view/create_source_page.dart index 2544750f..1be61268 100644 --- a/lib/content_management/view/create_source_page.dart +++ b/lib/content_management/view/create_source_page.dart @@ -20,18 +20,11 @@ class CreateSourcePage extends StatelessWidget { @override Widget build(BuildContext context) { - // The lists of all countries and languages are fetched once and cached in - // the ContentManagementBloc. We read them here and provide them to the - // CreateSourceBloc. - final contentManagementState = context.read().state; - final allCountries = contentManagementState.allCountries; - final allLanguages = contentManagementState.allLanguages; - return BlocProvider( create: (context) => CreateSourceBloc( sourcesRepository: context.read>(), - countries: allCountries, - languages: allLanguages, + countries: context.read().state.allCountries, + languages: context.read().state.allLanguages, ), child: const _CreateSourceView(), ); @@ -80,12 +73,26 @@ class _CreateSourceViewState extends State<_CreateSourceView> { ), ], ), - body: BlocConsumer( - listenWhen: (previous, current) => previous.status != current.status, - listener: (context, state) { - if (state.status == CreateSourceStatus.success && - state.createdSource != null && - ModalRoute.of(context)!.isCurrent) { + body: BlocListener( + listenWhen: (previous, current) => + (previous.allCountriesStatus != current.allCountriesStatus && + current.allCountriesStatus == ContentManagementStatus.success) || + (previous.allLanguagesStatus != current.allLanguagesStatus && + current.allLanguagesStatus == ContentManagementStatus.success), + listener: (context, contentState) { + context.read().add( + CreateSourceDataUpdated( + countries: contentState.allCountries, + languages: contentState.allLanguages, + ), + ); + }, + child: BlocConsumer( + listenWhen: (previous, current) => previous.status != current.status, + listener: (context, state) { + if (state.status == CreateSourceStatus.success && + state.createdSource != null && + ModalRoute.of(context)!.isCurrent) { ScaffoldMessenger.of(context) ..hideCurrentSnackBar() ..showSnackBar( @@ -108,14 +115,14 @@ class _CreateSourceViewState extends State<_CreateSourceView> { ); } }, - builder: (context, state) { - if (state.status == CreateSourceStatus.loading) { - return LoadingStateWidget( - icon: Icons.source, - headline: l10n.loadingData, - subheadline: l10n.pleaseWait, - ); - } + builder: (context, state) { + if (state.status == CreateSourceStatus.loading) { + return LoadingStateWidget( + icon: Icons.source, + headline: l10n.loadingData, + subheadline: l10n.pleaseWait, + ); + } if (state.status == CreateSourceStatus.failure) { return FailureStateWidget( From 54b4d5c61118b3927ea4ee719ed6d91319d520c5 Mon Sep 17 00:00:00 2001 From: fulleni Date: Sat, 2 Aug 2025 22:21:15 +0100 Subject: [PATCH 11/14] style(content_management): remove unnecessary lines of code - Reduced code complexity in _CreateHeadlineViewState class - Removed redundant closing parentheses to improve readability --- .../view/create_headline_page.dart | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/content_management/view/create_headline_page.dart b/lib/content_management/view/create_headline_page.dart index b986dae5..67d1c0d5 100644 --- a/lib/content_management/view/create_headline_page.dart +++ b/lib/content_management/view/create_headline_page.dart @@ -107,17 +107,17 @@ class _CreateHeadlineViewState extends State<_CreateHeadlineView> { content: Text(state.exception!.toFriendlyMessage(context)), backgroundColor: Theme.of(context).colorScheme.error, ), - ); + ); } }, - builder: (context, state) { - if (state.status == CreateHeadlineStatus.loading) { - return LoadingStateWidget( - icon: Icons.newspaper, - headline: l10n.loadingData, - subheadline: l10n.pleaseWait, - ); - } + builder: (context, state) { + if (state.status == CreateHeadlineStatus.loading) { + return LoadingStateWidget( + icon: Icons.newspaper, + headline: l10n.loadingData, + subheadline: l10n.pleaseWait, + ); + } if (state.status == CreateHeadlineStatus.failure && state.sources.isEmpty && From bab7f4ae65db589aeb4639d8de7fbf2af0d4daa4 Mon Sep 17 00:00:00 2001 From: fulleni Date: Sat, 2 Aug 2025 22:21:27 +0100 Subject: [PATCH 12/14] style: fix indentation in edit headline page - Adjust indentation in the buildBlocListener method - Ensure consistent formatting for better code readability --- .../view/edit_headline_page.dart | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/content_management/view/edit_headline_page.dart b/lib/content_management/view/edit_headline_page.dart index 9308d7b9..2f28e79c 100644 --- a/lib/content_management/view/edit_headline_page.dart +++ b/lib/content_management/view/edit_headline_page.dart @@ -144,14 +144,14 @@ class _EditHeadlineViewState extends State<_EditHeadlineView> { _imageUrlController.text = state.imageUrl; } }, - builder: (context, state) { - if (state.status == EditHeadlineStatus.loading) { - return LoadingStateWidget( - icon: Icons.newspaper, - headline: l10n.loadingHeadline, - subheadline: l10n.pleaseWait, - ); - } + builder: (context, state) { + if (state.status == EditHeadlineStatus.loading) { + return LoadingStateWidget( + icon: Icons.newspaper, + headline: l10n.loadingHeadline, + subheadline: l10n.pleaseWait, + ); + } if (state.status == EditHeadlineStatus.failure && state.initialHeadline == null) { From 362ddaab2a7b407972afa992ef9d93524ab92cec Mon Sep 17 00:00:00 2001 From: fulleni Date: Sat, 2 Aug 2025 22:28:02 +0100 Subject: [PATCH 13/14] lint: misc --- lib/content_management/view/create_source_page.dart | 1 - lib/content_management/view/edit_source_page.dart | 1 - 2 files changed, 2 deletions(-) diff --git a/lib/content_management/view/create_source_page.dart b/lib/content_management/view/create_source_page.dart index 1be61268..0cc9ca15 100644 --- a/lib/content_management/view/create_source_page.dart +++ b/lib/content_management/view/create_source_page.dart @@ -5,7 +5,6 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/content_management/bloc/content_management_bloc.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/content_management/bloc/create_source/create_source_bloc.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/l10n/l10n.dart'; -import 'package:flutter_news_app_web_dashboard_full_source_code/shared/extensions/source_type_l10n.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/shared/shared.dart'; import 'package:go_router/go_router.dart'; import 'package:ui_kit/ui_kit.dart'; diff --git a/lib/content_management/view/edit_source_page.dart b/lib/content_management/view/edit_source_page.dart index bcf14734..89ae1819 100644 --- a/lib/content_management/view/edit_source_page.dart +++ b/lib/content_management/view/edit_source_page.dart @@ -5,7 +5,6 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/content_management/bloc/content_management_bloc.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/content_management/bloc/edit_source/edit_source_bloc.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/l10n/l10n.dart'; -import 'package:flutter_news_app_web_dashboard_full_source_code/shared/extensions/source_type_l10n.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/shared/shared.dart'; import 'package:go_router/go_router.dart'; import 'package:ui_kit/ui_kit.dart'; From 923c9b4e4cecd8a72cc0a81a2ce0db0d68d1f212 Mon Sep 17 00:00:00 2001 From: fulleni Date: Sat, 2 Aug 2025 22:30:28 +0100 Subject: [PATCH 14/14] refactor(content_management): improve code structure and readability - Reorganize import statements for better clarity - Consolidate repeated code into functions to reduce duplication - Adjust indentation and spacing for consistent formatting - Refactor some widget builds for better readability --- .../view/create_headline_page.dart | 392 +++++++-------- .../view/create_source_page.dart | 390 ++++++++------- .../view/edit_headline_page.dart | 456 +++++++++--------- .../view/edit_source_page.dart | 397 +++++++-------- 4 files changed, 842 insertions(+), 793 deletions(-) diff --git a/lib/content_management/view/create_headline_page.dart b/lib/content_management/view/create_headline_page.dart index 67d1c0d5..e87e17c6 100644 --- a/lib/content_management/view/create_headline_page.dart +++ b/lib/content_management/view/create_headline_page.dart @@ -79,8 +79,8 @@ class _CreateHeadlineViewState extends State<_CreateHeadlineView> { current.allCountriesStatus == ContentManagementStatus.success, listener: (context, contentState) { context.read().add( - CreateHeadlineDataUpdated(countries: contentState.allCountries), - ); + CreateHeadlineDataUpdated(countries: contentState.allCountries), + ); }, child: BlocConsumer( listenWhen: (previous, current) => previous.status != current.status, @@ -88,215 +88,221 @@ class _CreateHeadlineViewState extends State<_CreateHeadlineView> { if (state.status == CreateHeadlineStatus.success && state.createdHeadline != null && ModalRoute.of(context)!.isCurrent) { - ScaffoldMessenger.of(context) - ..hideCurrentSnackBar() - ..showSnackBar( - SnackBar(content: Text(l10n.headlineCreatedSuccessfully)), + ScaffoldMessenger.of(context) + ..hideCurrentSnackBar() + ..showSnackBar( + SnackBar(content: Text(l10n.headlineCreatedSuccessfully)), + ); + context.read().add( + // Refresh the list to show the new headline + const LoadHeadlinesRequested(limit: kDefaultRowsPerPage), ); - context.read().add( - // Refresh the list to show the new headline - const LoadHeadlinesRequested(limit: kDefaultRowsPerPage), - ); - context.pop(); - } - if (state.status == CreateHeadlineStatus.failure) { - ScaffoldMessenger.of(context) - ..hideCurrentSnackBar() - ..showSnackBar( - SnackBar( - content: Text(state.exception!.toFriendlyMessage(context)), - backgroundColor: Theme.of(context).colorScheme.error, - ), - ); - } - }, - builder: (context, state) { - if (state.status == CreateHeadlineStatus.loading) { - return LoadingStateWidget( - icon: Icons.newspaper, - headline: l10n.loadingData, - subheadline: l10n.pleaseWait, - ); - } + context.pop(); + } + if (state.status == CreateHeadlineStatus.failure) { + ScaffoldMessenger.of(context) + ..hideCurrentSnackBar() + ..showSnackBar( + SnackBar( + content: Text(state.exception!.toFriendlyMessage(context)), + backgroundColor: Theme.of(context).colorScheme.error, + ), + ); + } + }, + builder: (context, state) { + if (state.status == CreateHeadlineStatus.loading) { + return LoadingStateWidget( + icon: Icons.newspaper, + headline: l10n.loadingData, + subheadline: l10n.pleaseWait, + ); + } - if (state.status == CreateHeadlineStatus.failure && - state.sources.isEmpty && - state.topics.isEmpty) { - return FailureStateWidget( - exception: state.exception!, - onRetry: () => context.read().add( - const CreateHeadlineDataLoaded(), - ), - ); - } + if (state.status == CreateHeadlineStatus.failure && + state.sources.isEmpty && + state.topics.isEmpty) { + return FailureStateWidget( + exception: state.exception!, + onRetry: () => context.read().add( + const CreateHeadlineDataLoaded(), + ), + ); + } - return SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.all(AppSpacing.lg), - child: Form( - key: _formKey, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - TextFormField( - initialValue: state.title, - decoration: InputDecoration( - labelText: l10n.headlineTitle, - border: const OutlineInputBorder(), - ), - onChanged: (value) => context - .read() - .add(CreateHeadlineTitleChanged(value)), - ), - const SizedBox(height: AppSpacing.lg), - TextFormField( - initialValue: state.excerpt, - decoration: InputDecoration( - labelText: l10n.excerpt, - border: const OutlineInputBorder(), - ), - maxLines: 3, - onChanged: (value) => context - .read() - .add(CreateHeadlineExcerptChanged(value)), - ), - const SizedBox(height: AppSpacing.lg), - TextFormField( - initialValue: state.url, - decoration: InputDecoration( - labelText: l10n.sourceUrl, - border: const OutlineInputBorder(), + return SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(AppSpacing.lg), + child: Form( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextFormField( + initialValue: state.title, + decoration: InputDecoration( + labelText: l10n.headlineTitle, + border: const OutlineInputBorder(), + ), + onChanged: (value) => context + .read() + .add(CreateHeadlineTitleChanged(value)), ), - onChanged: (value) => context - .read() - .add(CreateHeadlineUrlChanged(value)), - ), - const SizedBox(height: AppSpacing.lg), - TextFormField( - initialValue: state.imageUrl, - decoration: InputDecoration( - labelText: l10n.imageUrl, - border: const OutlineInputBorder(), + const SizedBox(height: AppSpacing.lg), + TextFormField( + initialValue: state.excerpt, + decoration: InputDecoration( + labelText: l10n.excerpt, + border: const OutlineInputBorder(), + ), + maxLines: 3, + onChanged: (value) => context + .read() + .add(CreateHeadlineExcerptChanged(value)), ), - onChanged: (value) => context - .read() - .add(CreateHeadlineImageUrlChanged(value)), - ), - const SizedBox(height: AppSpacing.lg), - DropdownButtonFormField( - value: state.source, - decoration: InputDecoration( - labelText: l10n.sourceName, - border: const OutlineInputBorder(), + const SizedBox(height: AppSpacing.lg), + TextFormField( + initialValue: state.url, + decoration: InputDecoration( + labelText: l10n.sourceUrl, + border: const OutlineInputBorder(), + ), + onChanged: (value) => context + .read() + .add(CreateHeadlineUrlChanged(value)), ), - items: [ - DropdownMenuItem(value: null, child: Text(l10n.none)), - ...state.sources.map( - (source) => DropdownMenuItem( - value: source, - child: Text(source.name), - ), + const SizedBox(height: AppSpacing.lg), + TextFormField( + initialValue: state.imageUrl, + decoration: InputDecoration( + labelText: l10n.imageUrl, + border: const OutlineInputBorder(), ), - ], - onChanged: (value) => context - .read() - .add(CreateHeadlineSourceChanged(value)), - ), - const SizedBox(height: AppSpacing.lg), - DropdownButtonFormField( - value: state.topic, - decoration: InputDecoration( - labelText: l10n.topicName, - border: const OutlineInputBorder(), + onChanged: (value) => context + .read() + .add(CreateHeadlineImageUrlChanged(value)), ), - items: [ - DropdownMenuItem(value: null, child: Text(l10n.none)), - ...state.topics.map( - (topic) => DropdownMenuItem( - value: topic, - child: Text(topic.name), + const SizedBox(height: AppSpacing.lg), + DropdownButtonFormField( + value: state.source, + decoration: InputDecoration( + labelText: l10n.sourceName, + border: const OutlineInputBorder(), + ), + items: [ + DropdownMenuItem(value: null, child: Text(l10n.none)), + ...state.sources.map( + (source) => DropdownMenuItem( + value: source, + child: Text(source.name), + ), ), + ], + onChanged: (value) => context + .read() + .add(CreateHeadlineSourceChanged(value)), + ), + const SizedBox(height: AppSpacing.lg), + DropdownButtonFormField( + value: state.topic, + decoration: InputDecoration( + labelText: l10n.topicName, + border: const OutlineInputBorder(), ), - ], - onChanged: (value) => context - .read() - .add(CreateHeadlineTopicChanged(value)), - ), - const SizedBox(height: AppSpacing.lg), - BlocBuilder( - builder: (context, contentState) { - final isLoading = contentState.allCountriesStatus == - ContentManagementStatus.loading; - return DropdownButtonFormField( - value: state.eventCountry, - decoration: InputDecoration( - labelText: l10n.countryName, - border: const OutlineInputBorder(), - helperText: - isLoading ? l10n.loadingFullList : null, + items: [ + DropdownMenuItem(value: null, child: Text(l10n.none)), + ...state.topics.map( + (topic) => DropdownMenuItem( + value: topic, + child: Text(topic.name), + ), ), - items: [ - DropdownMenuItem( - value: null, - child: Text(l10n.none), + ], + onChanged: (value) => context + .read() + .add(CreateHeadlineTopicChanged(value)), + ), + const SizedBox(height: AppSpacing.lg), + BlocBuilder< + ContentManagementBloc, + ContentManagementState + >( + builder: (context, contentState) { + final isLoading = + contentState.allCountriesStatus == + ContentManagementStatus.loading; + return DropdownButtonFormField( + value: state.eventCountry, + decoration: InputDecoration( + labelText: l10n.countryName, + border: const OutlineInputBorder(), + helperText: isLoading + ? l10n.loadingFullList + : null, ), - ...state.countries.map( - (country) => DropdownMenuItem( - value: country, - child: Row( - children: [ - SizedBox( - width: 32, - height: 20, - child: Image.network( - country.flagUrl, - fit: BoxFit.cover, - errorBuilder: - (context, error, stackTrace) => - const Icon(Icons.flag), + items: [ + DropdownMenuItem( + value: null, + child: Text(l10n.none), + ), + ...state.countries.map( + (country) => DropdownMenuItem( + value: country, + child: Row( + children: [ + SizedBox( + width: 32, + height: 20, + child: Image.network( + country.flagUrl, + fit: BoxFit.cover, + errorBuilder: + (context, error, stackTrace) => + const Icon(Icons.flag), + ), ), - ), - const SizedBox(width: AppSpacing.md), - Text(country.name), - ], + const SizedBox(width: AppSpacing.md), + Text(country.name), + ], + ), ), ), - ), - ], - onChanged: isLoading - ? null - : (value) => context - .read() - .add(CreateHeadlineCountryChanged(value)), - ); - }, - ), - const SizedBox(height: AppSpacing.lg), - DropdownButtonFormField( - value: state.contentStatus, - decoration: InputDecoration( - labelText: l10n.status, - border: const OutlineInputBorder(), + ], + onChanged: isLoading + ? null + : (value) => context + .read() + .add(CreateHeadlineCountryChanged(value)), + ); + }, + ), + const SizedBox(height: AppSpacing.lg), + DropdownButtonFormField( + value: state.contentStatus, + decoration: InputDecoration( + labelText: l10n.status, + border: const OutlineInputBorder(), + ), + items: ContentStatus.values.map((status) { + return DropdownMenuItem( + value: status, + child: Text(status.l10n(context)), + ); + }).toList(), + onChanged: (value) { + if (value == null) return; + context.read().add( + CreateHeadlineStatusChanged(value), + ); + }, ), - items: ContentStatus.values.map((status) { - return DropdownMenuItem( - value: status, - child: Text(status.l10n(context)), - ); - }).toList(), - onChanged: (value) { - if (value == null) return; - context.read().add( - CreateHeadlineStatusChanged(value), - ); - }, - ), - ], + ], + ), ), ), - ), - ); - }, + ); + }, + ), ), ); } diff --git a/lib/content_management/view/create_source_page.dart b/lib/content_management/view/create_source_page.dart index 0cc9ca15..4cfab528 100644 --- a/lib/content_management/view/create_source_page.dart +++ b/lib/content_management/view/create_source_page.dart @@ -75,16 +75,17 @@ class _CreateSourceViewState extends State<_CreateSourceView> { body: BlocListener( listenWhen: (previous, current) => (previous.allCountriesStatus != current.allCountriesStatus && - current.allCountriesStatus == ContentManagementStatus.success) || + current.allCountriesStatus == + ContentManagementStatus.success) || (previous.allLanguagesStatus != current.allLanguagesStatus && current.allLanguagesStatus == ContentManagementStatus.success), listener: (context, contentState) { context.read().add( - CreateSourceDataUpdated( - countries: contentState.allCountries, - languages: contentState.allLanguages, - ), - ); + CreateSourceDataUpdated( + countries: contentState.allCountries, + languages: contentState.allLanguages, + ), + ); }, child: BlocConsumer( listenWhen: (previous, current) => previous.status != current.status, @@ -92,28 +93,28 @@ class _CreateSourceViewState extends State<_CreateSourceView> { if (state.status == CreateSourceStatus.success && state.createdSource != null && ModalRoute.of(context)!.isCurrent) { - ScaffoldMessenger.of(context) - ..hideCurrentSnackBar() - ..showSnackBar( - SnackBar(content: Text(l10n.sourceCreatedSuccessfully)), - ); - context.read().add( - // Refresh the list to show the new source - const LoadSourcesRequested(limit: kDefaultRowsPerPage), - ); - context.pop(); - } - if (state.status == CreateSourceStatus.failure) { - ScaffoldMessenger.of(context) - ..hideCurrentSnackBar() - ..showSnackBar( - SnackBar( - content: Text(state.exception!.toFriendlyMessage(context)), - backgroundColor: Theme.of(context).colorScheme.error, - ), + ScaffoldMessenger.of(context) + ..hideCurrentSnackBar() + ..showSnackBar( + SnackBar(content: Text(l10n.sourceCreatedSuccessfully)), + ); + context.read().add( + // Refresh the list to show the new source + const LoadSourcesRequested(limit: kDefaultRowsPerPage), ); - } - }, + context.pop(); + } + if (state.status == CreateSourceStatus.failure) { + ScaffoldMessenger.of(context) + ..hideCurrentSnackBar() + ..showSnackBar( + SnackBar( + content: Text(state.exception!.toFriendlyMessage(context)), + backgroundColor: Theme.of(context).colorScheme.error, + ), + ); + } + }, builder: (context, state) { if (state.status == CreateSourceStatus.loading) { return LoadingStateWidget( @@ -123,179 +124,196 @@ class _CreateSourceViewState extends State<_CreateSourceView> { ); } - if (state.status == CreateSourceStatus.failure) { - return FailureStateWidget( - exception: state.exception!, - onRetry: () => context.read().add( - const SharedDataRequested(), - ), - ); - } + if (state.status == CreateSourceStatus.failure) { + return FailureStateWidget( + exception: state.exception!, + onRetry: () => context.read().add( + const SharedDataRequested(), + ), + ); + } - return SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.all(AppSpacing.lg), - child: Form( - key: _formKey, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - TextFormField( - initialValue: state.name, - decoration: InputDecoration( - labelText: l10n.sourceName, - border: const OutlineInputBorder(), + return SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(AppSpacing.lg), + child: Form( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextFormField( + initialValue: state.name, + decoration: InputDecoration( + labelText: l10n.sourceName, + border: const OutlineInputBorder(), + ), + onChanged: (value) => context + .read() + .add(CreateSourceNameChanged(value)), ), - onChanged: (value) => context - .read() - .add(CreateSourceNameChanged(value)), - ), - const SizedBox(height: AppSpacing.lg), - TextFormField( - initialValue: state.description, - decoration: InputDecoration( - labelText: l10n.description, - border: const OutlineInputBorder(), + const SizedBox(height: AppSpacing.lg), + TextFormField( + initialValue: state.description, + decoration: InputDecoration( + labelText: l10n.description, + border: const OutlineInputBorder(), + ), + maxLines: 3, + onChanged: (value) => context + .read() + .add(CreateSourceDescriptionChanged(value)), ), - maxLines: 3, - onChanged: (value) => context - .read() - .add(CreateSourceDescriptionChanged(value)), - ), - const SizedBox(height: AppSpacing.lg), - TextFormField( - initialValue: state.url, - decoration: InputDecoration( - labelText: l10n.sourceUrl, - border: const OutlineInputBorder(), + const SizedBox(height: AppSpacing.lg), + TextFormField( + initialValue: state.url, + decoration: InputDecoration( + labelText: l10n.sourceUrl, + border: const OutlineInputBorder(), + ), + onChanged: (value) => context + .read() + .add(CreateSourceUrlChanged(value)), ), - onChanged: (value) => context - .read() - .add(CreateSourceUrlChanged(value)), - ), - const SizedBox(height: AppSpacing.lg), - BlocBuilder( - builder: (context, contentState) { - final isLoading = contentState.allLanguagesStatus == - ContentManagementStatus.loading; - return DropdownButtonFormField( - value: state.language, - decoration: InputDecoration( - labelText: l10n.language, - border: const OutlineInputBorder(), - helperText: - isLoading ? l10n.loadingFullList : null, - ), - items: [ - DropdownMenuItem( - value: null, child: Text(l10n.none)), - ...state.languages.map( - (language) => DropdownMenuItem( - value: language, - child: Text(language.name), - ), + const SizedBox(height: AppSpacing.lg), + BlocBuilder< + ContentManagementBloc, + ContentManagementState + >( + builder: (context, contentState) { + final isLoading = + contentState.allLanguagesStatus == + ContentManagementStatus.loading; + return DropdownButtonFormField( + value: state.language, + decoration: InputDecoration( + labelText: l10n.language, + border: const OutlineInputBorder(), + helperText: isLoading + ? l10n.loadingFullList + : null, ), - ], - onChanged: isLoading - ? null - : (value) => context - .read() - .add(CreateSourceLanguageChanged(value)), - ); - }, - ), - const SizedBox(height: AppSpacing.lg), - DropdownButtonFormField( - value: state.sourceType, - decoration: InputDecoration( - labelText: l10n.sourceType, - border: const OutlineInputBorder(), + items: [ + DropdownMenuItem( + value: null, + child: Text(l10n.none), + ), + ...state.languages.map( + (language) => DropdownMenuItem( + value: language, + child: Text(language.name), + ), + ), + ], + onChanged: isLoading + ? null + : (value) => context + .read() + .add(CreateSourceLanguageChanged(value)), + ); + }, ), - items: [ - DropdownMenuItem(value: null, child: Text(l10n.none)), - ...SourceType.values.map( - (type) => DropdownMenuItem( - value: type, - child: Text(type.localizedName(l10n)), - ), + const SizedBox(height: AppSpacing.lg), + DropdownButtonFormField( + value: state.sourceType, + decoration: InputDecoration( + labelText: l10n.sourceType, + border: const OutlineInputBorder(), ), - ], - onChanged: (value) => context - .read() - .add(CreateSourceTypeChanged(value)), - ), - const SizedBox(height: AppSpacing.lg), - BlocBuilder( - builder: (context, contentState) { - final isLoading = contentState.allCountriesStatus == - ContentManagementStatus.loading; - return DropdownButtonFormField( - value: state.headquarters, - decoration: InputDecoration( - labelText: l10n.headquarters, - border: const OutlineInputBorder(), - helperText: - isLoading ? l10n.loadingFullList : null, + items: [ + DropdownMenuItem(value: null, child: Text(l10n.none)), + ...SourceType.values.map( + (type) => DropdownMenuItem( + value: type, + child: Text(type.localizedName(l10n)), + ), ), - items: [ - DropdownMenuItem( - value: null, child: Text(l10n.none)), - ...state.countries.map( - (country) => DropdownMenuItem( - value: country, - child: Row( - children: [ - SizedBox( - width: 32, - height: 20, - child: Image.network( - country.flagUrl, - fit: BoxFit.cover, - errorBuilder: - (context, error, stackTrace) => - const Icon(Icons.flag), + ], + onChanged: (value) => context + .read() + .add(CreateSourceTypeChanged(value)), + ), + const SizedBox(height: AppSpacing.lg), + BlocBuilder< + ContentManagementBloc, + ContentManagementState + >( + builder: (context, contentState) { + final isLoading = + contentState.allCountriesStatus == + ContentManagementStatus.loading; + return DropdownButtonFormField( + value: state.headquarters, + decoration: InputDecoration( + labelText: l10n.headquarters, + border: const OutlineInputBorder(), + helperText: isLoading + ? l10n.loadingFullList + : null, + ), + items: [ + DropdownMenuItem( + value: null, + child: Text(l10n.none), + ), + ...state.countries.map( + (country) => DropdownMenuItem( + value: country, + child: Row( + children: [ + SizedBox( + width: 32, + height: 20, + child: Image.network( + country.flagUrl, + fit: BoxFit.cover, + errorBuilder: + (context, error, stackTrace) => + const Icon(Icons.flag), + ), ), - ), - const SizedBox(width: AppSpacing.md), - Text(country.name), - ], + const SizedBox(width: AppSpacing.md), + Text(country.name), + ], + ), ), ), - ), - ], - onChanged: isLoading - ? null - : (value) => context.read().add( - CreateSourceHeadquartersChanged(value)), - ); - }, - ), - const SizedBox(height: AppSpacing.lg), - DropdownButtonFormField( - value: state.contentStatus, - decoration: InputDecoration( - labelText: l10n.status, - border: const OutlineInputBorder(), + ], + onChanged: isLoading + ? null + : (value) => + context.read().add( + CreateSourceHeadquartersChanged(value), + ), + ); + }, + ), + const SizedBox(height: AppSpacing.lg), + DropdownButtonFormField( + value: state.contentStatus, + decoration: InputDecoration( + labelText: l10n.status, + border: const OutlineInputBorder(), + ), + items: ContentStatus.values.map((status) { + return DropdownMenuItem( + value: status, + child: Text(status.l10n(context)), + ); + }).toList(), + onChanged: (value) { + if (value == null) return; + context.read().add( + CreateSourceStatusChanged(value), + ); + }, ), - items: ContentStatus.values.map((status) { - return DropdownMenuItem( - value: status, - child: Text(status.l10n(context)), - ); - }).toList(), - onChanged: (value) { - if (value == null) return; - context.read().add( - CreateSourceStatusChanged(value), - ); - }, - ), - ], + ], + ), ), ), - ), - ); - }, + ); + }, + ), ), ); } diff --git a/lib/content_management/view/edit_headline_page.dart b/lib/content_management/view/edit_headline_page.dart index 2f28e79c..558bc0de 100644 --- a/lib/content_management/view/edit_headline_page.dart +++ b/lib/content_management/view/edit_headline_page.dart @@ -106,8 +106,8 @@ class _EditHeadlineViewState extends State<_EditHeadlineView> { current.allCountriesStatus == ContentManagementStatus.success, listener: (context, contentState) { context.read().add( - EditHeadlineDataUpdated(countries: contentState.allCountries), - ); + EditHeadlineDataUpdated(countries: contentState.allCountries), + ); }, child: BlocConsumer( listenWhen: (previous, current) => @@ -117,254 +117,260 @@ class _EditHeadlineViewState extends State<_EditHeadlineView> { if (state.status == EditHeadlineStatus.success && state.updatedHeadline != null && ModalRoute.of(context)!.isCurrent) { - ScaffoldMessenger.of(context) - ..hideCurrentSnackBar() - ..showSnackBar( - SnackBar(content: Text(l10n.headlineUpdatedSuccessfully)), + ScaffoldMessenger.of(context) + ..hideCurrentSnackBar() + ..showSnackBar( + SnackBar(content: Text(l10n.headlineUpdatedSuccessfully)), + ); + context.read().add( + const LoadHeadlinesRequested(limit: kDefaultRowsPerPage), ); - context.read().add( - const LoadHeadlinesRequested(limit: kDefaultRowsPerPage), - ); - context.pop(); - } - if (state.status == EditHeadlineStatus.failure) { - ScaffoldMessenger.of(context) - ..hideCurrentSnackBar() - ..showSnackBar( - SnackBar( - content: Text(state.exception!.toFriendlyMessage(context)), - backgroundColor: Theme.of(context).colorScheme.error, - ), + context.pop(); + } + if (state.status == EditHeadlineStatus.failure) { + ScaffoldMessenger.of(context) + ..hideCurrentSnackBar() + ..showSnackBar( + SnackBar( + content: Text(state.exception!.toFriendlyMessage(context)), + backgroundColor: Theme.of(context).colorScheme.error, + ), + ); + } + if (state.initialHeadline != null) { + _titleController.text = state.title; + _excerptController.text = state.excerpt; + _urlController.text = state.url; + _imageUrlController.text = state.imageUrl; + } + }, + builder: (context, state) { + if (state.status == EditHeadlineStatus.loading) { + return LoadingStateWidget( + icon: Icons.newspaper, + headline: l10n.loadingHeadline, + subheadline: l10n.pleaseWait, ); - } - if (state.initialHeadline != null) { - _titleController.text = state.title; - _excerptController.text = state.excerpt; - _urlController.text = state.url; - _imageUrlController.text = state.imageUrl; - } - }, - builder: (context, state) { - if (state.status == EditHeadlineStatus.loading) { - return LoadingStateWidget( - icon: Icons.newspaper, - headline: l10n.loadingHeadline, - subheadline: l10n.pleaseWait, - ); - } - - if (state.status == EditHeadlineStatus.failure && - state.initialHeadline == null) { - return FailureStateWidget( - exception: state.exception!, - onRetry: () => context.read().add( - const EditHeadlineLoaded(), - ), - ); - } + } - // Find the correct instances from the lists to ensure - // the Dropdowns can display the selections correctly. - Source? selectedSource; - if (state.source != null) { - try { - selectedSource = state.sources.firstWhere( - (s) => s.id == state.source!.id, + if (state.status == EditHeadlineStatus.failure && + state.initialHeadline == null) { + return FailureStateWidget( + exception: state.exception!, + onRetry: () => context.read().add( + const EditHeadlineLoaded(), + ), ); - } catch (_) { - selectedSource = null; } - } - Topic? selectedTopic; - if (state.topic != null) { - try { - selectedTopic = state.topics.firstWhere( - (t) => t.id == state.topic!.id, - ); - } catch (_) { - selectedTopic = null; + // Find the correct instances from the lists to ensure + // the Dropdowns can display the selections correctly. + Source? selectedSource; + if (state.source != null) { + try { + selectedSource = state.sources.firstWhere( + (s) => s.id == state.source!.id, + ); + } catch (_) { + selectedSource = null; + } } - } - Country? selectedCountry; - if (state.eventCountry != null) { - try { - selectedCountry = state.countries.firstWhere( - (c) => c.id == state.eventCountry!.id, - ); - } catch (_) { - selectedCountry = null; + Topic? selectedTopic; + if (state.topic != null) { + try { + selectedTopic = state.topics.firstWhere( + (t) => t.id == state.topic!.id, + ); + } catch (_) { + selectedTopic = null; + } } - } - return SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.all(AppSpacing.lg), - child: Form( - key: _formKey, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - TextFormField( - controller: _titleController, - decoration: InputDecoration( - labelText: l10n.headlineTitle, - border: const OutlineInputBorder(), - ), - onChanged: (value) => context - .read() - .add(EditHeadlineTitleChanged(value)), - ), - const SizedBox(height: AppSpacing.lg), - TextFormField( - controller: _excerptController, - decoration: InputDecoration( - labelText: l10n.excerpt, - border: const OutlineInputBorder(), - ), - maxLines: 3, - onChanged: (value) => context - .read() - .add(EditHeadlineExcerptChanged(value)), - ), - const SizedBox(height: AppSpacing.lg), - TextFormField( - controller: _urlController, - decoration: InputDecoration( - labelText: l10n.sourceUrl, - border: const OutlineInputBorder(), + Country? selectedCountry; + if (state.eventCountry != null) { + try { + selectedCountry = state.countries.firstWhere( + (c) => c.id == state.eventCountry!.id, + ); + } catch (_) { + selectedCountry = null; + } + } + + return SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(AppSpacing.lg), + child: Form( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextFormField( + controller: _titleController, + decoration: InputDecoration( + labelText: l10n.headlineTitle, + border: const OutlineInputBorder(), + ), + onChanged: (value) => context + .read() + .add(EditHeadlineTitleChanged(value)), ), - onChanged: (value) => context - .read() - .add(EditHeadlineUrlChanged(value)), - ), - const SizedBox(height: AppSpacing.lg), - TextFormField( - controller: _imageUrlController, - decoration: InputDecoration( - labelText: l10n.imageUrl, - border: const OutlineInputBorder(), + const SizedBox(height: AppSpacing.lg), + TextFormField( + controller: _excerptController, + decoration: InputDecoration( + labelText: l10n.excerpt, + border: const OutlineInputBorder(), + ), + maxLines: 3, + onChanged: (value) => context + .read() + .add(EditHeadlineExcerptChanged(value)), ), - onChanged: (value) => context - .read() - .add(EditHeadlineImageUrlChanged(value)), - ), - const SizedBox(height: AppSpacing.lg), - DropdownButtonFormField( - value: selectedSource, - decoration: InputDecoration( - labelText: l10n.sourceName, - border: const OutlineInputBorder(), + const SizedBox(height: AppSpacing.lg), + TextFormField( + controller: _urlController, + decoration: InputDecoration( + labelText: l10n.sourceUrl, + border: const OutlineInputBorder(), + ), + onChanged: (value) => context + .read() + .add(EditHeadlineUrlChanged(value)), ), - items: [ - DropdownMenuItem(value: null, child: Text(l10n.none)), - ...state.sources.map( - (source) => DropdownMenuItem( - value: source, - child: Text(source.name), - ), + const SizedBox(height: AppSpacing.lg), + TextFormField( + controller: _imageUrlController, + decoration: InputDecoration( + labelText: l10n.imageUrl, + border: const OutlineInputBorder(), ), - ], - onChanged: (value) => context - .read() - .add(EditHeadlineSourceChanged(value)), - ), - const SizedBox(height: AppSpacing.lg), - DropdownButtonFormField( - value: selectedTopic, - decoration: InputDecoration( - labelText: l10n.topicName, - border: const OutlineInputBorder(), + onChanged: (value) => context + .read() + .add(EditHeadlineImageUrlChanged(value)), ), - items: [ - DropdownMenuItem(value: null, child: Text(l10n.none)), - ...state.topics.map( - (topic) => DropdownMenuItem( - value: topic, - child: Text(topic.name), + const SizedBox(height: AppSpacing.lg), + DropdownButtonFormField( + value: selectedSource, + decoration: InputDecoration( + labelText: l10n.sourceName, + border: const OutlineInputBorder(), + ), + items: [ + DropdownMenuItem(value: null, child: Text(l10n.none)), + ...state.sources.map( + (source) => DropdownMenuItem( + value: source, + child: Text(source.name), + ), ), + ], + onChanged: (value) => context + .read() + .add(EditHeadlineSourceChanged(value)), + ), + const SizedBox(height: AppSpacing.lg), + DropdownButtonFormField( + value: selectedTopic, + decoration: InputDecoration( + labelText: l10n.topicName, + border: const OutlineInputBorder(), ), - ], - onChanged: (value) => context - .read() - .add(EditHeadlineTopicChanged(value)), - ), - const SizedBox(height: AppSpacing.lg), - BlocBuilder( - builder: (context, contentState) { - final isLoading = contentState.allCountriesStatus == - ContentManagementStatus.loading; - return DropdownButtonFormField( - value: selectedCountry, - decoration: InputDecoration( - labelText: l10n.countryName, - border: const OutlineInputBorder(), - helperText: - isLoading ? l10n.loadingFullList : null, + items: [ + DropdownMenuItem(value: null, child: Text(l10n.none)), + ...state.topics.map( + (topic) => DropdownMenuItem( + value: topic, + child: Text(topic.name), + ), ), - items: [ - DropdownMenuItem( - value: null, - child: Text(l10n.none), + ], + onChanged: (value) => context + .read() + .add(EditHeadlineTopicChanged(value)), + ), + const SizedBox(height: AppSpacing.lg), + BlocBuilder< + ContentManagementBloc, + ContentManagementState + >( + builder: (context, contentState) { + final isLoading = + contentState.allCountriesStatus == + ContentManagementStatus.loading; + return DropdownButtonFormField( + value: selectedCountry, + decoration: InputDecoration( + labelText: l10n.countryName, + border: const OutlineInputBorder(), + helperText: isLoading + ? l10n.loadingFullList + : null, ), - ...state.countries.map( - (country) => DropdownMenuItem( - value: country, - child: Row( - children: [ - SizedBox( - width: 32, - height: 20, - child: Image.network( - country.flagUrl, - fit: BoxFit.cover, - errorBuilder: - (context, error, stackTrace) => - const Icon(Icons.flag), + items: [ + DropdownMenuItem( + value: null, + child: Text(l10n.none), + ), + ...state.countries.map( + (country) => DropdownMenuItem( + value: country, + child: Row( + children: [ + SizedBox( + width: 32, + height: 20, + child: Image.network( + country.flagUrl, + fit: BoxFit.cover, + errorBuilder: + (context, error, stackTrace) => + const Icon(Icons.flag), + ), ), - ), - const SizedBox(width: AppSpacing.md), - Text(country.name), - ], + const SizedBox(width: AppSpacing.md), + Text(country.name), + ], + ), ), ), - ), - ], - onChanged: isLoading - ? null - : (value) => context - .read() - .add(EditHeadlineCountryChanged(value)), - ); - }, - ), - const SizedBox(height: AppSpacing.lg), - DropdownButtonFormField( - value: state.contentStatus, - decoration: InputDecoration( - labelText: l10n.status, - border: const OutlineInputBorder(), + ], + onChanged: isLoading + ? null + : (value) => context + .read() + .add(EditHeadlineCountryChanged(value)), + ); + }, ), - items: ContentStatus.values.map((status) { - return DropdownMenuItem( - value: status, - child: Text(status.l10n(context)), - ); - }).toList(), - onChanged: (value) { - if (value == null) return; - context.read().add( - EditHeadlineStatusChanged(value), - ); - }, - ), - ], + const SizedBox(height: AppSpacing.lg), + DropdownButtonFormField( + value: state.contentStatus, + decoration: InputDecoration( + labelText: l10n.status, + border: const OutlineInputBorder(), + ), + items: ContentStatus.values.map((status) { + return DropdownMenuItem( + value: status, + child: Text(status.l10n(context)), + ); + }).toList(), + onChanged: (value) { + if (value == null) return; + context.read().add( + EditHeadlineStatusChanged(value), + ); + }, + ), + ], + ), ), ), - ), - ); - }, + ); + }, + ), ), ); } diff --git a/lib/content_management/view/edit_source_page.dart b/lib/content_management/view/edit_source_page.dart index 89ae1819..c834fa89 100644 --- a/lib/content_management/view/edit_source_page.dart +++ b/lib/content_management/view/edit_source_page.dart @@ -99,16 +99,17 @@ class _EditSourceViewState extends State<_EditSourceView> { body: BlocListener( listenWhen: (previous, current) => (previous.allCountriesStatus != current.allCountriesStatus && - current.allCountriesStatus == ContentManagementStatus.success) || + current.allCountriesStatus == + ContentManagementStatus.success) || (previous.allLanguagesStatus != current.allLanguagesStatus && current.allLanguagesStatus == ContentManagementStatus.success), listener: (context, contentState) { context.read().add( - EditSourceDataUpdated( - countries: contentState.allCountries, - languages: contentState.allLanguages, - ), - ); + EditSourceDataUpdated( + countries: contentState.allCountries, + languages: contentState.allLanguages, + ), + ); }, child: BlocConsumer( listenWhen: (previous, current) => @@ -118,32 +119,32 @@ class _EditSourceViewState extends State<_EditSourceView> { if (state.status == EditSourceStatus.success && state.updatedSource != null && ModalRoute.of(context)!.isCurrent) { - ScaffoldMessenger.of(context) - ..hideCurrentSnackBar() - ..showSnackBar( - SnackBar(content: Text(l10n.sourceUpdatedSuccessfully)), - ); - context.read().add( - const LoadSourcesRequested(limit: kDefaultRowsPerPage), - ); - context.pop(); - } - if (state.status == EditSourceStatus.failure) { - ScaffoldMessenger.of(context) - ..hideCurrentSnackBar() - ..showSnackBar( - SnackBar( - content: Text(state.exception!.toFriendlyMessage(context)), - backgroundColor: Theme.of(context).colorScheme.error, - ), + ScaffoldMessenger.of(context) + ..hideCurrentSnackBar() + ..showSnackBar( + SnackBar(content: Text(l10n.sourceUpdatedSuccessfully)), + ); + context.read().add( + const LoadSourcesRequested(limit: kDefaultRowsPerPage), ); - } - if (state.initialSource != null) { - _nameController.text = state.name; - _descriptionController.text = state.description; - _urlController.text = state.url; - } - }, + context.pop(); + } + if (state.status == EditSourceStatus.failure) { + ScaffoldMessenger.of(context) + ..hideCurrentSnackBar() + ..showSnackBar( + SnackBar( + content: Text(state.exception!.toFriendlyMessage(context)), + backgroundColor: Theme.of(context).colorScheme.error, + ), + ); + } + if (state.initialSource != null) { + _nameController.text = state.name; + _descriptionController.text = state.description; + _urlController.text = state.url; + } + }, builder: (context, state) { if (state.status == EditSourceStatus.loading) { return LoadingStateWidget( @@ -153,182 +154,200 @@ class _EditSourceViewState extends State<_EditSourceView> { ); } - if (state.status == EditSourceStatus.failure && - state.initialSource == null) { - return FailureStateWidget( - exception: state.exception!, - onRetry: () => - context.read().add(const EditSourceLoaded()), - ); - } + if (state.status == EditSourceStatus.failure && + state.initialSource == null) { + return FailureStateWidget( + exception: state.exception!, + onRetry: () => context.read().add( + const EditSourceLoaded(), + ), + ); + } - return SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.all(AppSpacing.lg), - child: Form( - key: _formKey, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - TextFormField( - controller: _nameController, - decoration: InputDecoration( - labelText: l10n.sourceName, - border: const OutlineInputBorder(), - ), - onChanged: (value) => context.read().add( - EditSourceNameChanged(value), - ), - ), - const SizedBox(height: AppSpacing.lg), - TextFormField( - controller: _descriptionController, - decoration: InputDecoration( - labelText: l10n.description, - border: const OutlineInputBorder(), - ), - maxLines: 3, - onChanged: (value) => context.read().add( - EditSourceDescriptionChanged(value), + return SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(AppSpacing.lg), + child: Form( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextFormField( + controller: _nameController, + decoration: InputDecoration( + labelText: l10n.sourceName, + border: const OutlineInputBorder(), + ), + onChanged: (value) => + context.read().add( + EditSourceNameChanged(value), + ), ), - ), - const SizedBox(height: AppSpacing.lg), - TextFormField( - controller: _urlController, - decoration: InputDecoration( - labelText: l10n.sourceUrl, - border: const OutlineInputBorder(), + const SizedBox(height: AppSpacing.lg), + TextFormField( + controller: _descriptionController, + decoration: InputDecoration( + labelText: l10n.description, + border: const OutlineInputBorder(), + ), + maxLines: 3, + onChanged: (value) => + context.read().add( + EditSourceDescriptionChanged(value), + ), ), - onChanged: (value) => context.read().add( - EditSourceUrlChanged(value), + const SizedBox(height: AppSpacing.lg), + TextFormField( + controller: _urlController, + decoration: InputDecoration( + labelText: l10n.sourceUrl, + border: const OutlineInputBorder(), + ), + onChanged: (value) => + context.read().add( + EditSourceUrlChanged(value), + ), ), - ), - const SizedBox(height: AppSpacing.lg), - BlocBuilder( - builder: (context, contentState) { - final isLoading = contentState.allLanguagesStatus == - ContentManagementStatus.loading; - return DropdownButtonFormField( - value: state.language, - decoration: InputDecoration( - labelText: l10n.language, - border: const OutlineInputBorder(), - helperText: - isLoading ? l10n.loadingFullList : null, - ), - items: [ - DropdownMenuItem( - value: null, child: Text(l10n.none)), - ...state.languages.map( - (language) => DropdownMenuItem( - value: language, - child: Text(language.name), - ), + const SizedBox(height: AppSpacing.lg), + BlocBuilder< + ContentManagementBloc, + ContentManagementState + >( + builder: (context, contentState) { + final isLoading = + contentState.allLanguagesStatus == + ContentManagementStatus.loading; + return DropdownButtonFormField( + value: state.language, + decoration: InputDecoration( + labelText: l10n.language, + border: const OutlineInputBorder(), + helperText: isLoading + ? l10n.loadingFullList + : null, ), - ], - onChanged: isLoading - ? null - : (value) => - context.read().add( + items: [ + DropdownMenuItem( + value: null, + child: Text(l10n.none), + ), + ...state.languages.map( + (language) => DropdownMenuItem( + value: language, + child: Text(language.name), + ), + ), + ], + onChanged: isLoading + ? null + : (value) => context.read().add( EditSourceLanguageChanged(value), ), - ); - }, - ), - const SizedBox(height: AppSpacing.lg), - DropdownButtonFormField( - value: state.sourceType, - decoration: InputDecoration( - labelText: l10n.sourceType, - border: const OutlineInputBorder(), + ); + }, ), - items: [ - DropdownMenuItem(value: null, child: Text(l10n.none)), - ...SourceType.values.map( - (type) => DropdownMenuItem( - value: type, - child: Text(type.localizedName(l10n)), - ), + const SizedBox(height: AppSpacing.lg), + DropdownButtonFormField( + value: state.sourceType, + decoration: InputDecoration( + labelText: l10n.sourceType, + border: const OutlineInputBorder(), ), - ], - onChanged: (value) => context.read().add( - EditSourceTypeChanged(value), - ), - ), - const SizedBox(height: AppSpacing.lg), - BlocBuilder( - builder: (context, contentState) { - final isLoading = contentState.allCountriesStatus == - ContentManagementStatus.loading; - return DropdownButtonFormField( - value: state.headquarters, - decoration: InputDecoration( - labelText: l10n.headquarters, - border: const OutlineInputBorder(), - helperText: - isLoading ? l10n.loadingFullList : null, + items: [ + DropdownMenuItem(value: null, child: Text(l10n.none)), + ...SourceType.values.map( + (type) => DropdownMenuItem( + value: type, + child: Text(type.localizedName(l10n)), + ), ), - items: [ - DropdownMenuItem( - value: null, child: Text(l10n.none)), - ...state.countries.map( - (country) => DropdownMenuItem( - value: country, - child: Row( - children: [ - SizedBox( - width: 32, - height: 20, - child: Image.network( - country.flagUrl, - fit: BoxFit.cover, - errorBuilder: - (context, error, stackTrace) => - const Icon(Icons.flag), + ], + onChanged: (value) => + context.read().add( + EditSourceTypeChanged(value), + ), + ), + const SizedBox(height: AppSpacing.lg), + BlocBuilder< + ContentManagementBloc, + ContentManagementState + >( + builder: (context, contentState) { + final isLoading = + contentState.allCountriesStatus == + ContentManagementStatus.loading; + return DropdownButtonFormField( + value: state.headquarters, + decoration: InputDecoration( + labelText: l10n.headquarters, + border: const OutlineInputBorder(), + helperText: isLoading + ? l10n.loadingFullList + : null, + ), + items: [ + DropdownMenuItem( + value: null, + child: Text(l10n.none), + ), + ...state.countries.map( + (country) => DropdownMenuItem( + value: country, + child: Row( + children: [ + SizedBox( + width: 32, + height: 20, + child: Image.network( + country.flagUrl, + fit: BoxFit.cover, + errorBuilder: + (context, error, stackTrace) => + const Icon(Icons.flag), + ), ), - ), - const SizedBox(width: AppSpacing.md), - Text(country.name), - ], + const SizedBox(width: AppSpacing.md), + Text(country.name), + ], + ), ), ), - ), - ], - onChanged: isLoading - ? null - : (value) => - context.read().add( + ], + onChanged: isLoading + ? null + : (value) => context.read().add( EditSourceHeadquartersChanged(value), ), - ); - }, - ), - const SizedBox(height: AppSpacing.lg), - DropdownButtonFormField( - value: state.contentStatus, - decoration: InputDecoration( - labelText: l10n.status, - border: const OutlineInputBorder(), + ); + }, + ), + const SizedBox(height: AppSpacing.lg), + DropdownButtonFormField( + value: state.contentStatus, + decoration: InputDecoration( + labelText: l10n.status, + border: const OutlineInputBorder(), + ), + items: ContentStatus.values.map((status) { + return DropdownMenuItem( + value: status, + child: Text(status.l10n(context)), + ); + }).toList(), + onChanged: (value) { + if (value == null) return; + context.read().add( + EditSourceStatusChanged(value), + ); + }, ), - items: ContentStatus.values.map((status) { - return DropdownMenuItem( - value: status, - child: Text(status.l10n(context)), - ); - }).toList(), - onChanged: (value) { - if (value == null) return; - context.read().add( - EditSourceStatusChanged(value), - ); - }, - ), - ], + ], + ), ), ), - ), - ); - }, + ); + }, + ), ), ); }