diff --git a/lib/app/view/app.dart b/lib/app/view/app.dart index 34fdb65..fe75ef4 100644 --- a/lib/app/view/app.dart +++ b/lib/app/view/app.dart @@ -108,12 +108,14 @@ class App extends StatelessWidget { headlinesRepository: context.read>(), topicsRepository: context.read>(), sourcesRepository: context.read>(), - ), + countriesRepository: context.read>(), + languagesRepository: context.read>(), + )..add(const SharedDataRequested()), ), BlocProvider( create: (context) => DashboardBloc( - dashboardSummaryRepository: - context.read>(), + dashboardSummaryRepository: context + .read>(), headlinesRepository: context.read>(), topicsRepository: context.read>(), sourcesRepository: context.read>(), diff --git a/lib/content_management/bloc/archived_headlines/archived_headlines_bloc.dart b/lib/content_management/bloc/archived_headlines/archived_headlines_bloc.dart index ad47402..63a7ab3 100644 --- a/lib/content_management/bloc/archived_headlines/archived_headlines_bloc.dart +++ b/lib/content_management/bloc/archived_headlines/archived_headlines_bloc.dart @@ -12,8 +12,8 @@ class ArchivedHeadlinesBloc extends Bloc { ArchivedHeadlinesBloc({ required DataRepository headlinesRepository, - }) : _headlinesRepository = headlinesRepository, - super(const ArchivedHeadlinesState()) { + }) : _headlinesRepository = headlinesRepository, + super(const ArchivedHeadlinesState()) { on(_onLoadArchivedHeadlinesRequested); on(_onRestoreHeadlineRequested); on(_onDeleteHeadlineForeverRequested); diff --git a/lib/content_management/bloc/archived_sources/archived_sources_bloc.dart b/lib/content_management/bloc/archived_sources/archived_sources_bloc.dart index 45fa8ef..97758a9 100644 --- a/lib/content_management/bloc/archived_sources/archived_sources_bloc.dart +++ b/lib/content_management/bloc/archived_sources/archived_sources_bloc.dart @@ -10,8 +10,8 @@ class ArchivedSourcesBloc extends Bloc { ArchivedSourcesBloc({ required DataRepository sourcesRepository, - }) : _sourcesRepository = sourcesRepository, - super(const ArchivedSourcesState()) { + }) : _sourcesRepository = sourcesRepository, + super(const ArchivedSourcesState()) { on(_onLoadArchivedSourcesRequested); on(_onRestoreSourceRequested); } diff --git a/lib/content_management/bloc/archived_sources/archived_sources_state.dart b/lib/content_management/bloc/archived_sources/archived_sources_state.dart index 4685a4d..67e43b1 100644 --- a/lib/content_management/bloc/archived_sources/archived_sources_state.dart +++ b/lib/content_management/bloc/archived_sources/archived_sources_state.dart @@ -46,11 +46,11 @@ class ArchivedSourcesState extends Equatable { @override List get props => [ - status, - sources, - cursor, - hasMore, - exception, - restoredSource, - ]; + status, + sources, + cursor, + hasMore, + exception, + restoredSource, + ]; } diff --git a/lib/content_management/bloc/archived_topics/archived_topics_bloc.dart b/lib/content_management/bloc/archived_topics/archived_topics_bloc.dart index 7e3a66c..a991fad 100644 --- a/lib/content_management/bloc/archived_topics/archived_topics_bloc.dart +++ b/lib/content_management/bloc/archived_topics/archived_topics_bloc.dart @@ -10,8 +10,8 @@ class ArchivedTopicsBloc extends Bloc { ArchivedTopicsBloc({ required DataRepository topicsRepository, - }) : _topicsRepository = topicsRepository, - super(const ArchivedTopicsState()) { + }) : _topicsRepository = topicsRepository, + super(const ArchivedTopicsState()) { on(_onLoadArchivedTopicsRequested); on(_onRestoreTopicRequested); } diff --git a/lib/content_management/bloc/archived_topics/archived_topics_state.dart b/lib/content_management/bloc/archived_topics/archived_topics_state.dart index e13a785..2afa306 100644 --- a/lib/content_management/bloc/archived_topics/archived_topics_state.dart +++ b/lib/content_management/bloc/archived_topics/archived_topics_state.dart @@ -46,11 +46,11 @@ class ArchivedTopicsState extends Equatable { @override List get props => [ - status, - topics, - cursor, - hasMore, - exception, - restoredTopic, - ]; + status, + topics, + cursor, + hasMore, + exception, + restoredTopic, + ]; } diff --git a/lib/content_management/bloc/content_management_bloc.dart b/lib/content_management/bloc/content_management_bloc.dart index 8f860ac..2404f16 100644 --- a/lib/content_management/bloc/content_management_bloc.dart +++ b/lib/content_management/bloc/content_management_bloc.dart @@ -24,10 +24,15 @@ class ContentManagementBloc required DataRepository headlinesRepository, required DataRepository topicsRepository, required DataRepository sourcesRepository, + required DataRepository countriesRepository, + required DataRepository languagesRepository, }) : _headlinesRepository = headlinesRepository, _topicsRepository = topicsRepository, _sourcesRepository = sourcesRepository, + _countriesRepository = countriesRepository, + _languagesRepository = languagesRepository, super(const ContentManagementState()) { + on(_onSharedDataRequested); on(_onContentManagementTabChanged); on(_onLoadHeadlinesRequested); on(_onHeadlineUpdated); @@ -43,6 +48,99 @@ class ContentManagementBloc final DataRepository _headlinesRepository; final DataRepository _topicsRepository; final DataRepository _sourcesRepository; + final DataRepository _countriesRepository; + final DataRepository _languagesRepository; + + // --- Background Data Fetching for countries/languages for the ui Dropdown --- + // + // The DropdownButtonFormField widget does not natively support on-scroll + // pagination. To preserve UI consistency across the application, this BLoC + // employs an event-driven background fetching mechanism. + Future _onSharedDataRequested( + SharedDataRequested event, + Emitter emit, + ) async { + // Helper function to fetch all items of a given type. + Future> fetchAll({ + required DataRepository repository, + required List sort, + }) async { + final allItems = []; + String? cursor; + bool hasMore; + + do { + final response = await repository.readAll( + sort: sort, + pagination: PaginationOptions(cursor: cursor), + filter: {'status': ContentStatus.active.name}, + ); + allItems.addAll(response.items); + cursor = response.cursor; + hasMore = response.hasMore; + } while (hasMore); + + return allItems; + } + + // Check if data is already loaded or is currently loading to prevent + // redundant fetches. + if (state.allCountriesStatus == ContentManagementStatus.success && + state.allLanguagesStatus == ContentManagementStatus.success) { + return; + } + + // Set loading status for both lists. + emit( + state.copyWith( + allCountriesStatus: ContentManagementStatus.loading, + allLanguagesStatus: ContentManagementStatus.loading, + ), + ); + + try { + // Fetch both lists in parallel. + final results = await Future.wait([ + fetchAll( + repository: _countriesRepository, + sort: [const SortOption('name', SortOrder.asc)], + ), + fetchAll( + repository: _languagesRepository, + sort: [const SortOption('name', SortOrder.asc)], + ), + ]); + + final countries = results[0] as List; + final languages = results[1] as List; + + // Update the state with the complete lists. + emit( + state.copyWith( + allCountries: countries, + allCountriesStatus: ContentManagementStatus.success, + allLanguages: languages, + allLanguagesStatus: ContentManagementStatus.success, + ), + ); + } on HttpException catch (e) { + emit( + state.copyWith( + allCountriesStatus: ContentManagementStatus.failure, + allLanguagesStatus: ContentManagementStatus.failure, + exception: e, + ), + ); + } catch (e) { + emit( + state.copyWith( + allCountriesStatus: ContentManagementStatus.failure, + allLanguagesStatus: ContentManagementStatus.failure, + exception: UnknownException('An unexpected error occurred: $e'), + ), + ); + } + } void _onContentManagementTabChanged( ContentManagementTabChanged event, diff --git a/lib/content_management/bloc/content_management_event.dart b/lib/content_management/bloc/content_management_event.dart index 3e1d95b..23711dd 100644 --- a/lib/content_management/bloc/content_management_event.dart +++ b/lib/content_management/bloc/content_management_event.dart @@ -155,3 +155,12 @@ final class SourceUpdated extends ContentManagementEvent { @override List get props => [source]; } + +/// {@template shared_data_requested} +/// Event to request loading of shared data like countries and languages. +/// This should be dispatched once when the content management section is loaded. +/// {@endtemplate} +final class SharedDataRequested extends ContentManagementEvent { + /// {@macro shared_data_requested} + const SharedDataRequested(); +} diff --git a/lib/content_management/bloc/content_management_state.dart b/lib/content_management/bloc/content_management_state.dart index d71a388..71afbc2 100644 --- a/lib/content_management/bloc/content_management_state.dart +++ b/lib/content_management/bloc/content_management_state.dart @@ -32,6 +32,10 @@ class ContentManagementState extends Equatable { this.sources = const [], this.sourcesCursor, this.sourcesHasMore = false, + this.allCountriesStatus = ContentManagementStatus.initial, + this.allCountries = const [], + this.allLanguagesStatus = ContentManagementStatus.initial, + this.allLanguages = const [], this.exception, }); @@ -74,6 +78,18 @@ class ContentManagementState extends Equatable { /// Indicates if there are more sources to load. final bool sourcesHasMore; + /// Status of all countries data operations. + final ContentManagementStatus allCountriesStatus; + + /// Cached list of all countries. + final List allCountries; + + /// Status of all languages data operations. + final ContentManagementStatus allLanguagesStatus; + + /// Cached list of all languages. + final List allLanguages; + /// The error describing an operation failure, if any. final HttpException? exception; @@ -92,6 +108,10 @@ class ContentManagementState extends Equatable { List? sources, String? sourcesCursor, bool? sourcesHasMore, + ContentManagementStatus? allCountriesStatus, + List? allCountries, + ContentManagementStatus? allLanguagesStatus, + List? allLanguages, HttpException? exception, }) { return ContentManagementState( @@ -108,6 +128,10 @@ class ContentManagementState extends Equatable { sources: sources ?? this.sources, sourcesCursor: sourcesCursor ?? this.sourcesCursor, sourcesHasMore: sourcesHasMore ?? this.sourcesHasMore, + allCountriesStatus: allCountriesStatus ?? this.allCountriesStatus, + allCountries: allCountries ?? this.allCountries, + allLanguagesStatus: allLanguagesStatus ?? this.allLanguagesStatus, + allLanguages: allLanguages ?? this.allLanguages, exception: exception ?? this.exception, ); } @@ -127,6 +151,10 @@ class ContentManagementState extends Equatable { sources, sourcesCursor, sourcesHasMore, + allCountriesStatus, + allCountries, + allLanguagesStatus, + allLanguages, exception, ]; } 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 68655f5..91520e6 100644 --- a/lib/content_management/bloc/create_headline/create_headline_bloc.dart +++ b/lib/content_management/bloc/create_headline/create_headline_bloc.dart @@ -8,10 +8,6 @@ import 'package:uuid/uuid.dart'; part 'create_headline_event.dart'; part 'create_headline_state.dart'; -final class _FetchNextCountryPage extends CreateHeadlineEvent { - const _FetchNextCountryPage(); -} - /// A BLoC to manage the state of creating a new headline. class CreateHeadlineBloc extends Bloc { @@ -20,12 +16,11 @@ class CreateHeadlineBloc required DataRepository headlinesRepository, required DataRepository sourcesRepository, required DataRepository topicsRepository, - required DataRepository countriesRepository, + required List countries, }) : _headlinesRepository = headlinesRepository, _sourcesRepository = sourcesRepository, _topicsRepository = topicsRepository, - _countriesRepository = countriesRepository, - super(const CreateHeadlineState()) { + super(CreateHeadlineState(countries: countries)) { on(_onDataLoaded); on(_onTitleChanged); on(_onExcerptChanged); @@ -36,13 +31,11 @@ class CreateHeadlineBloc on(_onCountryChanged); on(_onStatusChanged); on(_onSubmitted); - on<_FetchNextCountryPage>(_onFetchNextCountryPage); } final DataRepository _headlinesRepository; final DataRepository _sourcesRepository; final DataRepository _topicsRepository; - final DataRepository _countriesRepository; final _uuid = const Uuid(); Future _onDataLoaded( @@ -65,26 +58,13 @@ class CreateHeadlineBloc final sources = (sourcesResponse as PaginatedResponse).items; final topics = (topicsResponse as PaginatedResponse).items; - final countriesResponse = await _countriesRepository.readAll( - sort: [const SortOption('name', SortOrder.asc)], - ); - emit( state.copyWith( status: CreateHeadlineStatus.initial, sources: sources, topics: topics, - countries: countriesResponse.items, - countriesCursor: countriesResponse.cursor, - countriesHasMore: countriesResponse.hasMore, ), ); - - // After the initial page of countries is loaded, start a background - // process to fetch all remaining pages. - if (state.countriesHasMore) { - add(const _FetchNextCountryPage()); - } } on HttpException catch (e) { emit(state.copyWith(status: CreateHeadlineStatus.failure, exception: e)); } catch (e) { @@ -159,49 +139,6 @@ class CreateHeadlineBloc } // --- Background Data Fetching for Dropdown --- - // The DropdownButtonFormField widget does not natively support on-scroll - // pagination. To preserve UI consistency across the application, this BLoC - // employs an event-driven background fetching mechanism. - // - // After the first page of items is loaded, a chain of events is initiated - // to progressively fetch all remaining pages. This process is throttled - // and runs in the background, ensuring the UI remains responsive while the - // full list of dropdown options is populated over time. - Future _onFetchNextCountryPage( - _FetchNextCountryPage event, - Emitter emit, - ) async { - if (!state.countriesHasMore || state.countriesIsLoadingMore) return; - - try { - emit(state.copyWith(countriesIsLoadingMore: true)); - - // ignore: inference_failure_on_instance_creation - await Future.delayed(const Duration(milliseconds: 400)); - - final nextCountries = await _countriesRepository.readAll( - pagination: PaginationOptions(cursor: state.countriesCursor), - sort: [const SortOption('name', SortOrder.asc)], - ); - - emit( - state.copyWith( - countries: List.of(state.countries)..addAll(nextCountries.items), - countriesCursor: nextCountries.cursor, - countriesHasMore: nextCountries.hasMore, - countriesIsLoadingMore: false, - ), - ); - - if (nextCountries.hasMore) { - add(const _FetchNextCountryPage()); - } - } catch (e) { - emit(state.copyWith(countriesIsLoadingMore: false)); - // Optionally log the error without disrupting the user - } - } - Future _onSubmitted( CreateHeadlineSubmitted event, Emitter emit, 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 1fb3f0c..0739db1 100644 --- a/lib/content_management/bloc/create_headline/create_headline_state.dart +++ b/lib/content_management/bloc/create_headline/create_headline_state.dart @@ -32,9 +32,6 @@ final class CreateHeadlineState extends Equatable { this.sources = const [], this.topics = const [], this.countries = const [], - this.countriesHasMore = true, - this.countriesIsLoadingMore = false, - this.countriesCursor, this.contentStatus = ContentStatus.active, this.exception, this.createdHeadline, @@ -51,9 +48,6 @@ final class CreateHeadlineState extends Equatable { final List sources; final List topics; final List countries; - final bool countriesHasMore; - final bool countriesIsLoadingMore; - final String? countriesCursor; final ContentStatus contentStatus; final HttpException? exception; final Headline? createdHeadline; @@ -80,9 +74,6 @@ final class CreateHeadlineState extends Equatable { List? sources, List? topics, List? countries, - bool? countriesHasMore, - bool? countriesIsLoadingMore, - String? countriesCursor, ContentStatus? contentStatus, HttpException? exception, Headline? createdHeadline, @@ -99,10 +90,6 @@ final class CreateHeadlineState extends Equatable { sources: sources ?? this.sources, topics: topics ?? this.topics, countries: countries ?? this.countries, - countriesHasMore: countriesHasMore ?? this.countriesHasMore, - countriesIsLoadingMore: - countriesIsLoadingMore ?? this.countriesIsLoadingMore, - countriesCursor: countriesCursor ?? this.countriesCursor, contentStatus: contentStatus ?? this.contentStatus, exception: exception, createdHeadline: createdHeadline ?? this.createdHeadline, @@ -122,9 +109,6 @@ final class CreateHeadlineState extends Equatable { sources, topics, countries, - countriesHasMore, - countriesIsLoadingMore, - countriesCursor, contentStatus, exception, createdHeadline, 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 7a6beee..0f99f7f 100644 --- a/lib/content_management/bloc/create_source/create_source_bloc.dart +++ b/lib/content_management/bloc/create_source/create_source_bloc.dart @@ -8,26 +8,20 @@ import 'package:uuid/uuid.dart'; part 'create_source_event.dart'; part 'create_source_state.dart'; -final class _FetchNextCountryPage extends CreateSourceEvent { - const _FetchNextCountryPage(); -} - -final class _FetchNextLanguagePage extends CreateSourceEvent { - const _FetchNextLanguagePage(); -} - /// A BLoC to manage the state of creating a new source. class CreateSourceBloc extends Bloc { /// {@macro create_source_bloc} CreateSourceBloc({ required DataRepository sourcesRepository, - required DataRepository countriesRepository, - required DataRepository languagesRepository, + required List countries, + required List languages, }) : _sourcesRepository = sourcesRepository, - _countriesRepository = countriesRepository, - _languagesRepository = languagesRepository, - super(const CreateSourceState()) { - on(_onDataLoaded); + super( + CreateSourceState( + countries: countries, + languages: languages, + ), + ) { on(_onNameChanged); on(_onDescriptionChanged); on(_onUrlChanged); @@ -36,65 +30,11 @@ class CreateSourceBloc extends Bloc { on(_onHeadquartersChanged); on(_onStatusChanged); on(_onSubmitted); - on<_FetchNextCountryPage>(_onFetchNextCountryPage); - on<_FetchNextLanguagePage>(_onFetchNextLanguagePage); } final DataRepository _sourcesRepository; - final DataRepository _countriesRepository; - final DataRepository _languagesRepository; final _uuid = const Uuid(); - Future _onDataLoaded( - CreateSourceDataLoaded event, - Emitter emit, - ) async { - emit(state.copyWith(status: CreateSourceStatus.loading)); - try { - final responses = await Future.wait([ - _countriesRepository.readAll( - sort: [const SortOption('name', SortOrder.asc)], - filter: {'status': ContentStatus.active.name}, - ), - _languagesRepository.readAll( - sort: [const SortOption('name', SortOrder.asc)], - filter: {'status': ContentStatus.active.name}, - ), - ]); - final countriesPaginated = responses[0] as PaginatedResponse; - final languagesPaginated = responses[1] as PaginatedResponse; - emit( - state.copyWith( - status: CreateSourceStatus.initial, - countries: countriesPaginated.items, - countriesCursor: countriesPaginated.cursor, - countriesHasMore: countriesPaginated.hasMore, - languages: languagesPaginated.items, - languagesCursor: languagesPaginated.cursor, - languagesHasMore: languagesPaginated.hasMore, - ), - ); - - // After the initial page is loaded, start background processes to - // fetch all remaining pages for countries and languages. - if (state.countriesHasMore) { - add(const _FetchNextCountryPage()); - } - if (state.languagesHasMore) { - add(const _FetchNextLanguagePage()); - } - } on HttpException catch (e) { - emit(state.copyWith(status: CreateSourceStatus.failure, exception: e)); - } catch (e) { - emit( - state.copyWith( - status: CreateSourceStatus.failure, - exception: UnknownException('An unexpected error occurred: $e'), - ), - ); - } - } - void _onNameChanged( CreateSourceNameChanged event, Emitter emit, @@ -150,84 +90,6 @@ class CreateSourceBloc extends Bloc { } // --- Background Data Fetching for Dropdown --- - // The DropdownButtonFormField widget does not natively support on-scroll - // pagination. To preserve UI consistency across the application, this BLoC - // employs an event-driven background fetching mechanism. - // - // After the first page of items is loaded, a chain of events is initiated - // to progressively fetch all remaining pages. This process is throttled - // and runs in the background, ensuring the UI remains responsive while the - // full list of dropdown options is populated over time. - Future _onFetchNextCountryPage( - _FetchNextCountryPage event, - Emitter emit, - ) async { - if (!state.countriesHasMore || state.countriesIsLoadingMore) return; - - try { - emit(state.copyWith(countriesIsLoadingMore: true)); - - // ignore: inference_failure_on_instance_creation - await Future.delayed(const Duration(milliseconds: 400)); - - final nextCountries = await _countriesRepository.readAll( - pagination: PaginationOptions(cursor: state.countriesCursor), - sort: [const SortOption('name', SortOrder.asc)], - ); - - emit( - state.copyWith( - countries: List.of(state.countries)..addAll(nextCountries.items), - countriesCursor: nextCountries.cursor, - countriesHasMore: nextCountries.hasMore, - countriesIsLoadingMore: false, - ), - ); - - if (nextCountries.hasMore) { - add(const _FetchNextCountryPage()); - } - } catch (e) { - emit(state.copyWith(countriesIsLoadingMore: false)); - // Optionally log the error without disrupting the user - } - } - - Future _onFetchNextLanguagePage( - _FetchNextLanguagePage event, - Emitter emit, - ) async { - if (!state.languagesHasMore || state.languagesIsLoadingMore) return; - - try { - emit(state.copyWith(languagesIsLoadingMore: true)); - - // ignore: inference_failure_on_instance_creation - await Future.delayed(const Duration(milliseconds: 400)); - - final nextLanguages = await _languagesRepository.readAll( - pagination: PaginationOptions(cursor: state.languagesCursor), - sort: [const SortOption('name', SortOrder.asc)], - ); - - emit( - state.copyWith( - languages: List.of(state.languages)..addAll(nextLanguages.items), - languagesCursor: nextLanguages.cursor, - languagesHasMore: nextLanguages.hasMore, - languagesIsLoadingMore: false, - ), - ); - - if (nextLanguages.hasMore) { - add(const _FetchNextLanguagePage()); - } - } catch (e) { - emit(state.copyWith(languagesIsLoadingMore: false)); - // Optionally log the error without disrupting the user - } - } - Future _onSubmitted( CreateSourceSubmitted event, Emitter emit, 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 e4c4f56..c2351b0 100644 --- a/lib/content_management/bloc/create_source/create_source_event.dart +++ b/lib/content_management/bloc/create_source/create_source_event.dart @@ -8,11 +8,6 @@ sealed class CreateSourceEvent extends Equatable { List get props => []; } -/// Event to signal that the data for dropdowns should be loaded. -final class CreateSourceDataLoaded extends CreateSourceEvent { - const CreateSourceDataLoaded(); -} - /// Event for when the source's name is changed. final class CreateSourceNameChanged extends CreateSourceEvent { const CreateSourceNameChanged(this.name); 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 8d358da..d8f7ac6 100644 --- a/lib/content_management/bloc/create_source/create_source_state.dart +++ b/lib/content_management/bloc/create_source/create_source_state.dart @@ -30,13 +30,7 @@ final class CreateSourceState extends Equatable { this.language, this.headquarters, this.countries = const [], - this.countriesHasMore = true, - this.countriesIsLoadingMore = false, - this.countriesCursor, this.languages = const [], - this.languagesHasMore = true, - this.languagesIsLoadingMore = false, - this.languagesCursor, this.contentStatus = ContentStatus.active, this.exception, this.createdSource, @@ -50,13 +44,7 @@ final class CreateSourceState extends Equatable { final Language? language; final Country? headquarters; final List countries; - final bool countriesHasMore; - final bool countriesIsLoadingMore; - final String? countriesCursor; final List languages; - final bool languagesHasMore; - final bool languagesIsLoadingMore; - final String? languagesCursor; final ContentStatus contentStatus; final HttpException? exception; final Source? createdSource; @@ -79,13 +67,7 @@ final class CreateSourceState extends Equatable { ValueGetter? language, ValueGetter? headquarters, List? countries, - bool? countriesHasMore, - bool? countriesIsLoadingMore, - String? countriesCursor, List? languages, - bool? languagesHasMore, - bool? languagesIsLoadingMore, - String? languagesCursor, ContentStatus? contentStatus, HttpException? exception, Source? createdSource, @@ -99,15 +81,7 @@ final class CreateSourceState extends Equatable { language: language != null ? language() : this.language, headquarters: headquarters != null ? headquarters() : this.headquarters, countries: countries ?? this.countries, - countriesHasMore: countriesHasMore ?? this.countriesHasMore, - countriesIsLoadingMore: - countriesIsLoadingMore ?? this.countriesIsLoadingMore, - countriesCursor: countriesCursor ?? this.countriesCursor, languages: languages ?? this.languages, - languagesHasMore: languagesHasMore ?? this.languagesHasMore, - languagesIsLoadingMore: - languagesIsLoadingMore ?? this.languagesIsLoadingMore, - languagesCursor: languagesCursor ?? this.languagesCursor, contentStatus: contentStatus ?? this.contentStatus, exception: exception, createdSource: createdSource ?? this.createdSource, @@ -124,13 +98,7 @@ final class CreateSourceState extends Equatable { language, headquarters, countries, - countriesHasMore, - countriesIsLoadingMore, - countriesCursor, languages, - languagesHasMore, - languagesIsLoadingMore, - languagesCursor, contentStatus, exception, createdSource, 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 788bba1..6fc464f 100644 --- a/lib/content_management/bloc/edit_headline/edit_headline_bloc.dart +++ b/lib/content_management/bloc/edit_headline/edit_headline_bloc.dart @@ -7,10 +7,6 @@ import 'package:flutter/foundation.dart'; part 'edit_headline_event.dart'; part 'edit_headline_state.dart'; -final class _FetchNextCountryPage extends EditHeadlineEvent { - const _FetchNextCountryPage(); -} - /// A BLoC to manage the state of editing a single headline. class EditHeadlineBloc extends Bloc { /// {@macro edit_headline_bloc} @@ -18,14 +14,13 @@ class EditHeadlineBloc extends Bloc { required DataRepository headlinesRepository, required DataRepository sourcesRepository, required DataRepository topicsRepository, - required DataRepository countriesRepository, + required List countries, required String headlineId, }) : _headlinesRepository = headlinesRepository, _sourcesRepository = sourcesRepository, _topicsRepository = topicsRepository, - _countriesRepository = countriesRepository, _headlineId = headlineId, - super(const EditHeadlineState()) { + super(EditHeadlineState(countries: countries)) { on(_onLoaded); on(_onTitleChanged); on(_onExcerptChanged); @@ -36,13 +31,11 @@ class EditHeadlineBloc extends Bloc { on(_onCountryChanged); on(_onStatusChanged); on(_onSubmitted); - on<_FetchNextCountryPage>(_onFetchNextCountryPage); } final DataRepository _headlinesRepository; final DataRepository _sourcesRepository; final DataRepository _topicsRepository; - final DataRepository _countriesRepository; final String _headlineId; Future _onLoaded( @@ -67,10 +60,6 @@ class EditHeadlineBloc extends Bloc { final sources = (responses[1] as PaginatedResponse).items; final topics = (responses[2] as PaginatedResponse).items; - final countriesResponse = await _countriesRepository.readAll( - sort: [const SortOption('name', SortOrder.asc)], - ); - emit( state.copyWith( status: EditHeadlineStatus.initial, @@ -84,18 +73,9 @@ class EditHeadlineBloc extends Bloc { eventCountry: () => headline.eventCountry, sources: sources, topics: topics, - countries: countriesResponse.items, - countriesCursor: countriesResponse.cursor, - countriesHasMore: countriesResponse.hasMore, contentStatus: headline.status, ), ); - - // After the initial page of countries is loaded, start a background - // process to fetch all remaining pages. - if (state.countriesHasMore) { - add(const _FetchNextCountryPage()); - } } on HttpException catch (e) { emit(state.copyWith(status: EditHeadlineStatus.failure, exception: e)); } catch (e) { @@ -197,49 +177,6 @@ class EditHeadlineBloc extends Bloc { } // --- Background Data Fetching for Dropdown --- - // The DropdownButtonFormField widget does not natively support on-scroll - // pagination. To preserve UI consistency across the application, this BLoC - // employs an event-driven background fetching mechanism. - // - // After the first page of items is loaded, a chain of events is initiated - // to progressively fetch all remaining pages. This process is throttled - // and runs in the background, ensuring the UI remains responsive while the - // full list of dropdown options is populated over time. - Future _onFetchNextCountryPage( - _FetchNextCountryPage event, - Emitter emit, - ) async { - if (!state.countriesHasMore || state.countriesIsLoadingMore) return; - - try { - emit(state.copyWith(countriesIsLoadingMore: true)); - - // ignore: inference_failure_on_instance_creation - await Future.delayed(const Duration(milliseconds: 400)); - - final nextCountries = await _countriesRepository.readAll( - pagination: PaginationOptions(cursor: state.countriesCursor), - sort: [const SortOption('name', SortOrder.asc)], - ); - - emit( - state.copyWith( - countries: List.of(state.countries)..addAll(nextCountries.items), - countriesCursor: nextCountries.cursor, - countriesHasMore: nextCountries.hasMore, - countriesIsLoadingMore: false, - ), - ); - - if (nextCountries.hasMore) { - add(const _FetchNextCountryPage()); - } - } catch (e) { - emit(state.copyWith(countriesIsLoadingMore: false)); - // Optionally log the error without disrupting the user - } - } - Future _onSubmitted( EditHeadlineSubmitted event, Emitter emit, 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 30688de..fba983f 100644 --- a/lib/content_management/bloc/edit_headline/edit_headline_state.dart +++ b/lib/content_management/bloc/edit_headline/edit_headline_state.dart @@ -33,9 +33,6 @@ final class EditHeadlineState extends Equatable { this.sources = const [], this.topics = const [], this.countries = const [], - this.countriesHasMore = true, - this.countriesIsLoadingMore = false, - this.countriesCursor, this.contentStatus = ContentStatus.active, this.exception, this.updatedHeadline, @@ -53,9 +50,6 @@ final class EditHeadlineState extends Equatable { final List sources; final List topics; final List countries; - final bool countriesHasMore; - final bool countriesIsLoadingMore; - final String? countriesCursor; final ContentStatus contentStatus; final HttpException? exception; final Headline? updatedHeadline; @@ -83,9 +77,6 @@ final class EditHeadlineState extends Equatable { List? sources, List? topics, List? countries, - bool? countriesHasMore, - bool? countriesIsLoadingMore, - String? countriesCursor, ContentStatus? contentStatus, HttpException? exception, Headline? updatedHeadline, @@ -103,10 +94,6 @@ final class EditHeadlineState extends Equatable { sources: sources ?? this.sources, topics: topics ?? this.topics, countries: countries ?? this.countries, - countriesHasMore: countriesHasMore ?? this.countriesHasMore, - countriesIsLoadingMore: - countriesIsLoadingMore ?? this.countriesIsLoadingMore, - countriesCursor: countriesCursor ?? this.countriesCursor, contentStatus: contentStatus ?? this.contentStatus, exception: exception, updatedHeadline: updatedHeadline ?? this.updatedHeadline, @@ -127,9 +114,6 @@ final class EditHeadlineState extends Equatable { sources, topics, countries, - countriesHasMore, - countriesIsLoadingMore, - countriesCursor, 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 c564457..3c5f78b 100644 --- a/lib/content_management/bloc/edit_source/edit_source_bloc.dart +++ b/lib/content_management/bloc/edit_source/edit_source_bloc.dart @@ -7,27 +7,22 @@ import 'package:flutter/foundation.dart'; part 'edit_source_event.dart'; part 'edit_source_state.dart'; -final class _FetchNextCountryPage extends EditSourceEvent { - const _FetchNextCountryPage(); -} - -final class _FetchNextLanguagePage extends EditSourceEvent { - const _FetchNextLanguagePage(); -} - /// A BLoC to manage the state of editing a single source. class EditSourceBloc extends Bloc { /// {@macro edit_source_bloc} EditSourceBloc({ required DataRepository sourcesRepository, - required DataRepository countriesRepository, - required DataRepository languagesRepository, + required List countries, + required List languages, required String sourceId, }) : _sourcesRepository = sourcesRepository, - _countriesRepository = countriesRepository, - _languagesRepository = languagesRepository, _sourceId = sourceId, - super(const EditSourceState()) { + super( + EditSourceState( + countries: countries, + languages: languages, + ), + ) { on(_onLoaded); on(_onNameChanged); on(_onDescriptionChanged); @@ -37,13 +32,9 @@ class EditSourceBloc extends Bloc { on(_onHeadquartersChanged); on(_onStatusChanged); on(_onSubmitted); - on<_FetchNextCountryPage>(_onFetchNextCountryPage); - on<_FetchNextLanguagePage>(_onFetchNextLanguagePage); } final DataRepository _sourcesRepository; - final DataRepository _countriesRepository; - final DataRepository _languagesRepository; final String _sourceId; Future _onLoaded( @@ -52,32 +43,7 @@ class EditSourceBloc extends Bloc { ) async { emit(state.copyWith(status: EditSourceStatus.loading)); try { - final responses = await Future.wait([ - _sourcesRepository.read(id: _sourceId), - _countriesRepository.readAll( - sort: [const SortOption('name', SortOrder.asc)], - filter: {'status': ContentStatus.active.name}, - ), - _languagesRepository.readAll( - sort: [const SortOption('name', SortOrder.asc)], - filter: {'status': ContentStatus.active.name}, - ), - ]); - - final source = responses[0] as Source; - final countriesPaginated = responses[1] as PaginatedResponse; - final languagesPaginated = responses[2] as PaginatedResponse; - - Language? selectedLanguage; - try { - // Find the equivalent language object from the full list. - // This ensures the DropdownButton can identify it by reference. - selectedLanguage = languagesPaginated.items.firstWhere( - (listLanguage) => listLanguage.id == source.language.id, - ); - } catch (_) { - selectedLanguage = source.language; - } + final source = await _sourcesRepository.read(id: _sourceId); emit( state.copyWith( @@ -87,26 +53,11 @@ class EditSourceBloc extends Bloc { description: source.description, url: source.url, sourceType: () => source.sourceType, - language: () => selectedLanguage, + language: () => source.language, headquarters: () => source.headquarters, contentStatus: source.status, - countries: countriesPaginated.items, - countriesCursor: countriesPaginated.cursor, - countriesHasMore: countriesPaginated.hasMore, - languages: languagesPaginated.items, - languagesCursor: languagesPaginated.cursor, - languagesHasMore: languagesPaginated.hasMore, ), ); - - // After the initial page is loaded, start background processes to - // fetch all remaining pages for countries and languages. - if (state.countriesHasMore) { - add(const _FetchNextCountryPage()); - } - if (state.languagesHasMore) { - add(const _FetchNextLanguagePage()); - } } on HttpException catch (e) { emit(state.copyWith(status: EditSourceStatus.failure, exception: e)); } catch (e) { @@ -194,84 +145,6 @@ class EditSourceBloc extends Bloc { } // --- Background Data Fetching for Dropdown --- - // The DropdownButtonFormField widget does not natively support on-scroll - // pagination. To preserve UI consistency across the application, this BLoC - // employs an event-driven background fetching mechanism. - // - // After the first page of items is loaded, a chain of events is initiated - // to progressively fetch all remaining pages. This process is throttled - // and runs in the background, ensuring the UI remains responsive while the - // full list of dropdown options is populated over time. - Future _onFetchNextCountryPage( - _FetchNextCountryPage event, - Emitter emit, - ) async { - if (!state.countriesHasMore || state.countriesIsLoadingMore) return; - - try { - emit(state.copyWith(countriesIsLoadingMore: true)); - - // ignore: inference_failure_on_instance_creation - await Future.delayed(const Duration(milliseconds: 400)); - - final nextCountries = await _countriesRepository.readAll( - pagination: PaginationOptions(cursor: state.countriesCursor), - sort: [const SortOption('name', SortOrder.asc)], - ); - - emit( - state.copyWith( - countries: List.of(state.countries)..addAll(nextCountries.items), - countriesCursor: nextCountries.cursor, - countriesHasMore: nextCountries.hasMore, - countriesIsLoadingMore: false, - ), - ); - - if (nextCountries.hasMore) { - add(const _FetchNextCountryPage()); - } - } catch (e) { - emit(state.copyWith(countriesIsLoadingMore: false)); - // Optionally log the error without disrupting the user - } - } - - Future _onFetchNextLanguagePage( - _FetchNextLanguagePage event, - Emitter emit, - ) async { - if (!state.languagesHasMore || state.languagesIsLoadingMore) return; - - try { - emit(state.copyWith(languagesIsLoadingMore: true)); - - // ignore: inference_failure_on_instance_creation - await Future.delayed(const Duration(milliseconds: 400)); - - final nextLanguages = await _languagesRepository.readAll( - pagination: PaginationOptions(cursor: state.languagesCursor), - sort: [const SortOption('name', SortOrder.asc)], - ); - - emit( - state.copyWith( - languages: List.of(state.languages)..addAll(nextLanguages.items), - languagesCursor: nextLanguages.cursor, - languagesHasMore: nextLanguages.hasMore, - languagesIsLoadingMore: false, - ), - ); - - if (nextLanguages.hasMore) { - add(const _FetchNextLanguagePage()); - } - } catch (e) { - emit(state.copyWith(languagesIsLoadingMore: false)); - // Optionally log the error without disrupting the user - } - } - Future _onSubmitted( EditSourceSubmitted event, Emitter emit, 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 c40b167..1c65c6a 100644 --- a/lib/content_management/bloc/edit_source/edit_source_state.dart +++ b/lib/content_management/bloc/edit_source/edit_source_state.dart @@ -30,13 +30,7 @@ final class EditSourceState extends Equatable { this.language, this.headquarters, this.countries = const [], - this.countriesHasMore = true, - this.countriesIsLoadingMore = false, - this.countriesCursor, this.languages = const [], - this.languagesHasMore = true, - this.languagesIsLoadingMore = false, - this.languagesCursor, this.contentStatus = ContentStatus.active, this.exception, this.updatedSource, @@ -51,13 +45,7 @@ final class EditSourceState extends Equatable { final Language? language; final Country? headquarters; final List countries; - final bool countriesHasMore; - final bool countriesIsLoadingMore; - final String? countriesCursor; final List languages; - final bool languagesHasMore; - final bool languagesIsLoadingMore; - final String? languagesCursor; final ContentStatus contentStatus; final HttpException? exception; final Source? updatedSource; @@ -81,13 +69,7 @@ final class EditSourceState extends Equatable { ValueGetter? language, ValueGetter? headquarters, List? countries, - bool? countriesHasMore, - bool? countriesIsLoadingMore, - String? countriesCursor, List? languages, - bool? languagesHasMore, - bool? languagesIsLoadingMore, - String? languagesCursor, ContentStatus? contentStatus, HttpException? exception, Source? updatedSource, @@ -102,15 +84,7 @@ final class EditSourceState extends Equatable { language: language != null ? language() : this.language, headquarters: headquarters != null ? headquarters() : this.headquarters, countries: countries ?? this.countries, - countriesHasMore: countriesHasMore ?? this.countriesHasMore, - countriesIsLoadingMore: - countriesIsLoadingMore ?? this.countriesIsLoadingMore, - countriesCursor: countriesCursor ?? this.countriesCursor, languages: languages ?? this.languages, - languagesHasMore: languagesHasMore ?? this.languagesHasMore, - languagesIsLoadingMore: - languagesIsLoadingMore ?? this.languagesIsLoadingMore, - languagesCursor: languagesCursor ?? this.languagesCursor, contentStatus: contentStatus ?? this.contentStatus, exception: exception, updatedSource: updatedSource ?? this.updatedSource, @@ -128,13 +102,7 @@ final class EditSourceState extends Equatable { language, headquarters, countries, - countriesHasMore, - countriesIsLoadingMore, - countriesCursor, languages, - languagesHasMore, - languagesIsLoadingMore, - languagesCursor, contentStatus, exception, updatedSource, diff --git a/lib/content_management/view/archived_headlines_page.dart b/lib/content_management/view/archived_headlines_page.dart index 3ce33db..1e8ae36 100644 --- a/lib/content_management/view/archived_headlines_page.dart +++ b/lib/content_management/view/archived_headlines_page.dart @@ -44,8 +44,8 @@ class _ArchivedHeadlinesView extends StatelessWidget { listener: (context, state) { if (state.restoredHeadline != null) { context.read().add( - const LoadHeadlinesRequested(limit: kDefaultRowsPerPage), - ); + const LoadHeadlinesRequested(limit: kDefaultRowsPerPage), + ); } if (state.lastDeletedHeadline != null) { final truncatedTitle = @@ -80,80 +80,80 @@ class _ArchivedHeadlinesView extends StatelessWidget { ); } - if (state.status == ArchivedHeadlinesStatus.failure) { - return FailureStateWidget( - exception: state.exception!, - onRetry: () => context.read().add( - const LoadArchivedHeadlinesRequested( - limit: kDefaultRowsPerPage, - ), + if (state.status == ArchivedHeadlinesStatus.failure) { + return FailureStateWidget( + exception: state.exception!, + onRetry: () => context.read().add( + const LoadArchivedHeadlinesRequested( + limit: kDefaultRowsPerPage, ), - ); - } + ), + ); + } - if (state.headlines.isEmpty) { - return Center(child: Text(l10n.noArchivedHeadlinesFound)); - } + if (state.headlines.isEmpty) { + return Center(child: Text(l10n.noArchivedHeadlinesFound)); + } - return Column( - children: [ - if (state.status == ArchivedHeadlinesStatus.loading && - state.headlines.isNotEmpty) - const LinearProgressIndicator(), - Expanded( - child: PaginatedDataTable2( - columns: [ - DataColumn2( - label: Text(l10n.headlineTitle), - size: ColumnSize.L, - ), - DataColumn2( - label: Text(l10n.sourceName), - size: ColumnSize.M, - ), - DataColumn2( - label: Text(l10n.lastUpdated), - size: ColumnSize.M, - ), - DataColumn2( - label: Text(l10n.actions), - size: ColumnSize.S, - fixedWidth: 120, + return Column( + children: [ + if (state.status == ArchivedHeadlinesStatus.loading && + state.headlines.isNotEmpty) + const LinearProgressIndicator(), + Expanded( + child: PaginatedDataTable2( + columns: [ + DataColumn2( + label: Text(l10n.headlineTitle), + size: ColumnSize.L, + ), + DataColumn2( + label: Text(l10n.sourceName), + size: ColumnSize.M, + ), + DataColumn2( + label: Text(l10n.lastUpdated), + size: ColumnSize.M, + ), + DataColumn2( + label: Text(l10n.actions), + size: ColumnSize.S, + fixedWidth: 120, + ), + ], + source: _HeadlinesDataSource( + context: context, + headlines: state.headlines, + hasMore: state.hasMore, + l10n: l10n, ), - ], - source: _HeadlinesDataSource( - context: context, - headlines: state.headlines, - hasMore: state.hasMore, - l10n: l10n, + rowsPerPage: kDefaultRowsPerPage, + availableRowsPerPage: const [kDefaultRowsPerPage], + onPageChanged: (pageIndex) { + final newOffset = pageIndex * kDefaultRowsPerPage; + if (newOffset >= state.headlines.length && + state.hasMore && + state.status != ArchivedHeadlinesStatus.loading) { + context.read().add( + LoadArchivedHeadlinesRequested( + startAfterId: state.cursor, + limit: kDefaultRowsPerPage, + ), + ); + } + }, + empty: Center(child: Text(l10n.noHeadlinesFound)), + showCheckboxColumn: false, + showFirstLastButtons: true, + fit: FlexFit.tight, + headingRowHeight: 56, + dataRowHeight: 56, + columnSpacing: AppSpacing.md, + horizontalMargin: AppSpacing.md, ), - rowsPerPage: kDefaultRowsPerPage, - availableRowsPerPage: const [kDefaultRowsPerPage], - onPageChanged: (pageIndex) { - final newOffset = pageIndex * kDefaultRowsPerPage; - if (newOffset >= state.headlines.length && - state.hasMore && - state.status != ArchivedHeadlinesStatus.loading) { - context.read().add( - LoadArchivedHeadlinesRequested( - startAfterId: state.cursor, - limit: kDefaultRowsPerPage, - ), - ); - } - }, - empty: Center(child: Text(l10n.noHeadlinesFound)), - showCheckboxColumn: false, - showFirstLastButtons: true, - fit: FlexFit.tight, - headingRowHeight: 56, - dataRowHeight: 56, - columnSpacing: AppSpacing.md, - horizontalMargin: AppSpacing.md, ), - ), - ], - ); + ], + ); }, ), ), @@ -204,8 +204,8 @@ class _HeadlinesDataSource extends DataTableSource { tooltip: l10n.restore, onPressed: () { context.read().add( - RestoreHeadlineRequested(headline.id), - ); + RestoreHeadlineRequested(headline.id), + ); }, ), IconButton( @@ -213,8 +213,8 @@ class _HeadlinesDataSource extends DataTableSource { tooltip: l10n.deleteForever, onPressed: () { context.read().add( - DeleteHeadlineForeverRequested(headline.id), - ); + DeleteHeadlineForeverRequested(headline.id), + ); }, ), ], diff --git a/lib/content_management/view/archived_sources_page.dart b/lib/content_management/view/archived_sources_page.dart index c8a4f57..b9a5fdb 100644 --- a/lib/content_management/view/archived_sources_page.dart +++ b/lib/content_management/view/archived_sources_page.dart @@ -5,7 +5,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/content_management/bloc/archived_sources/archived_sources_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/dashboard/bloc/dashboard_bloc.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/l10n/app_localizations.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/l10n/l10n.dart'; import 'package:intl/intl.dart'; @@ -43,91 +42,91 @@ class _ArchivedSourcesView extends StatelessWidget { listener: (context, state) { if (state.restoredSource != null) { context.read().add( - const LoadSourcesRequested(limit: kDefaultRowsPerPage), - ); + const LoadSourcesRequested(limit: kDefaultRowsPerPage), + ); } }, child: BlocBuilder( builder: (context, state) { if (state.status == ArchivedSourcesStatus.loading && state.sources.isEmpty) { - return LoadingStateWidget( - icon: Icons.source, - headline: l10n.loadingArchivedSources, - subheadline: l10n.pleaseWait, - ); - } + return LoadingStateWidget( + icon: Icons.source, + headline: l10n.loadingArchivedSources, + subheadline: l10n.pleaseWait, + ); + } - if (state.status == ArchivedSourcesStatus.failure) { - return FailureStateWidget( - exception: state.exception!, - onRetry: () => context.read().add( - const LoadArchivedSourcesRequested( - limit: kDefaultRowsPerPage, - ), + if (state.status == ArchivedSourcesStatus.failure) { + return FailureStateWidget( + exception: state.exception!, + onRetry: () => context.read().add( + const LoadArchivedSourcesRequested( + limit: kDefaultRowsPerPage, ), - ); - } + ), + ); + } - if (state.sources.isEmpty) { - return Center(child: Text(l10n.noArchivedSourcesFound)); - } + if (state.sources.isEmpty) { + return Center(child: Text(l10n.noArchivedSourcesFound)); + } - return Column( - children: [ - if (state.status == ArchivedSourcesStatus.loading && - state.sources.isNotEmpty) - const LinearProgressIndicator(), - Expanded( - child: PaginatedDataTable2( - columns: [ - DataColumn2( - label: Text(l10n.sourceName), - size: ColumnSize.L, - ), - DataColumn2( - label: Text(l10n.lastUpdated), - size: ColumnSize.M, + return Column( + children: [ + if (state.status == ArchivedSourcesStatus.loading && + state.sources.isNotEmpty) + const LinearProgressIndicator(), + Expanded( + child: PaginatedDataTable2( + columns: [ + DataColumn2( + label: Text(l10n.sourceName), + size: ColumnSize.L, + ), + DataColumn2( + label: Text(l10n.lastUpdated), + size: ColumnSize.M, + ), + DataColumn2( + label: Text(l10n.actions), + size: ColumnSize.S, + fixedWidth: 120, + ), + ], + source: _SourcesDataSource( + context: context, + sources: state.sources, + hasMore: state.hasMore, + l10n: l10n, ), - DataColumn2( - label: Text(l10n.actions), - size: ColumnSize.S, - fixedWidth: 120, - ), - ], - source: _SourcesDataSource( - context: context, - sources: state.sources, - hasMore: state.hasMore, - l10n: l10n, + rowsPerPage: kDefaultRowsPerPage, + availableRowsPerPage: const [kDefaultRowsPerPage], + onPageChanged: (pageIndex) { + final newOffset = pageIndex * kDefaultRowsPerPage; + if (newOffset >= state.sources.length && + state.hasMore && + state.status != ArchivedSourcesStatus.loading) { + context.read().add( + LoadArchivedSourcesRequested( + startAfterId: state.cursor, + limit: kDefaultRowsPerPage, + ), + ); + } + }, + empty: Center(child: Text(l10n.noSourcesFound)), + showCheckboxColumn: false, + showFirstLastButtons: true, + fit: FlexFit.tight, + headingRowHeight: 56, + dataRowHeight: 56, + columnSpacing: AppSpacing.md, + horizontalMargin: AppSpacing.md, ), - rowsPerPage: kDefaultRowsPerPage, - availableRowsPerPage: const [kDefaultRowsPerPage], - onPageChanged: (pageIndex) { - final newOffset = pageIndex * kDefaultRowsPerPage; - if (newOffset >= state.sources.length && - state.hasMore && - state.status != ArchivedSourcesStatus.loading) { - context.read().add( - LoadArchivedSourcesRequested( - startAfterId: state.cursor, - limit: kDefaultRowsPerPage, - ), - ); - } - }, - empty: Center(child: Text(l10n.noSourcesFound)), - showCheckboxColumn: false, - showFirstLastButtons: true, - fit: FlexFit.tight, - headingRowHeight: 56, - dataRowHeight: 56, - columnSpacing: AppSpacing.md, - horizontalMargin: AppSpacing.md, ), - ), - ], - ); + ], + ); }, ), ), @@ -177,8 +176,8 @@ class _SourcesDataSource extends DataTableSource { tooltip: l10n.restore, onPressed: () { context.read().add( - RestoreSourceRequested(source.id), - ); + RestoreSourceRequested(source.id), + ); }, ), ], diff --git a/lib/content_management/view/archived_topics_page.dart b/lib/content_management/view/archived_topics_page.dart index 315ffe0..46db2de 100644 --- a/lib/content_management/view/archived_topics_page.dart +++ b/lib/content_management/view/archived_topics_page.dart @@ -5,7 +5,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/content_management/bloc/archived_topics/archived_topics_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/dashboard/bloc/dashboard_bloc.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/l10n/app_localizations.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/l10n/l10n.dart'; import 'package:intl/intl.dart'; @@ -42,92 +41,92 @@ class _ArchivedTopicsView extends StatelessWidget { previous.restoredTopic != current.restoredTopic, listener: (context, state) { if (state.restoredTopic != null) { - context - .read() - .add(const LoadTopicsRequested(limit: kDefaultRowsPerPage)); + context.read().add( + const LoadTopicsRequested(limit: kDefaultRowsPerPage), + ); } }, child: BlocBuilder( builder: (context, state) { if (state.status == ArchivedTopicsStatus.loading && state.topics.isEmpty) { - return LoadingStateWidget( - icon: Icons.topic, - headline: l10n.loadingArchivedTopics, - subheadline: l10n.pleaseWait, - ); - } + return LoadingStateWidget( + icon: Icons.topic, + headline: l10n.loadingArchivedTopics, + subheadline: l10n.pleaseWait, + ); + } - if (state.status == ArchivedTopicsStatus.failure) { - return FailureStateWidget( - exception: state.exception!, - onRetry: () => context.read().add( - const LoadArchivedTopicsRequested( - limit: kDefaultRowsPerPage, - ), + if (state.status == ArchivedTopicsStatus.failure) { + return FailureStateWidget( + exception: state.exception!, + onRetry: () => context.read().add( + const LoadArchivedTopicsRequested( + limit: kDefaultRowsPerPage, ), - ); - } + ), + ); + } - if (state.topics.isEmpty) { - return Center(child: Text(l10n.noArchivedTopicsFound)); - } + if (state.topics.isEmpty) { + return Center(child: Text(l10n.noArchivedTopicsFound)); + } - return Column( - children: [ - if (state.status == ArchivedTopicsStatus.loading && - state.topics.isNotEmpty) - const LinearProgressIndicator(), - Expanded( - child: PaginatedDataTable2( - columns: [ - DataColumn2( - label: Text(l10n.topicName), - size: ColumnSize.L, - ), - DataColumn2( - label: Text(l10n.lastUpdated), - size: ColumnSize.M, + return Column( + children: [ + if (state.status == ArchivedTopicsStatus.loading && + state.topics.isNotEmpty) + const LinearProgressIndicator(), + Expanded( + child: PaginatedDataTable2( + columns: [ + DataColumn2( + label: Text(l10n.topicName), + size: ColumnSize.L, + ), + DataColumn2( + label: Text(l10n.lastUpdated), + size: ColumnSize.M, + ), + DataColumn2( + label: Text(l10n.actions), + size: ColumnSize.S, + fixedWidth: 120, + ), + ], + source: _TopicsDataSource( + context: context, + topics: state.topics, + hasMore: state.hasMore, + l10n: l10n, ), - DataColumn2( - label: Text(l10n.actions), - size: ColumnSize.S, - fixedWidth: 120, - ), - ], - source: _TopicsDataSource( - context: context, - topics: state.topics, - hasMore: state.hasMore, - l10n: l10n, + rowsPerPage: kDefaultRowsPerPage, + availableRowsPerPage: const [kDefaultRowsPerPage], + onPageChanged: (pageIndex) { + final newOffset = pageIndex * kDefaultRowsPerPage; + if (newOffset >= state.topics.length && + state.hasMore && + state.status != ArchivedTopicsStatus.loading) { + context.read().add( + LoadArchivedTopicsRequested( + startAfterId: state.cursor, + limit: kDefaultRowsPerPage, + ), + ); + } + }, + empty: Center(child: Text(l10n.noTopicsFound)), + showCheckboxColumn: false, + showFirstLastButtons: true, + fit: FlexFit.tight, + headingRowHeight: 56, + dataRowHeight: 56, + columnSpacing: AppSpacing.md, + horizontalMargin: AppSpacing.md, ), - rowsPerPage: kDefaultRowsPerPage, - availableRowsPerPage: const [kDefaultRowsPerPage], - onPageChanged: (pageIndex) { - final newOffset = pageIndex * kDefaultRowsPerPage; - if (newOffset >= state.topics.length && - state.hasMore && - state.status != ArchivedTopicsStatus.loading) { - context.read().add( - LoadArchivedTopicsRequested( - startAfterId: state.cursor, - limit: kDefaultRowsPerPage, - ), - ); - } - }, - empty: Center(child: Text(l10n.noTopicsFound)), - showCheckboxColumn: false, - showFirstLastButtons: true, - fit: FlexFit.tight, - headingRowHeight: 56, - dataRowHeight: 56, - columnSpacing: AppSpacing.md, - horizontalMargin: AppSpacing.md, ), - ), - ], - ); + ], + ); }, ), ), @@ -177,8 +176,8 @@ class _TopicsDataSource extends DataTableSource { tooltip: l10n.restore, onPressed: () { context.read().add( - RestoreTopicRequested(topic.id), - ); + RestoreTopicRequested(topic.id), + ); }, ), ], diff --git a/lib/content_management/view/content_management_page.dart b/lib/content_management/view/content_management_page.dart index 1ded64f..5925efc 100644 --- a/lib/content_management/view/content_management_page.dart +++ b/lib/content_management/view/content_management_page.dart @@ -96,8 +96,10 @@ class _ContentManagementPageState extends State icon: const Icon(Icons.inventory_2_outlined), tooltip: l10n.archivedItems, onPressed: () { - final currentTab = - context.read().state.activeTab; + final currentTab = context + .read() + .state + .activeTab; switch (currentTab) { case ContentManagementTab.headlines: context.goNamed(Routes.archivedHeadlinesName); diff --git a/lib/content_management/view/create_headline_page.dart b/lib/content_management/view/create_headline_page.dart index ecc66f8..3d56e20 100644 --- a/lib/content_management/view/create_headline_page.dart +++ b/lib/content_management/view/create_headline_page.dart @@ -4,7 +4,6 @@ import 'package:flutter/material.dart'; 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_headline/create_headline_bloc.dart'; -import 'package:flutter_news_app_web_dashboard_full_source_code/dashboard/bloc/dashboard_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/shared.dart'; import 'package:go_router/go_router.dart'; @@ -20,12 +19,19 @@ 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 allCountries = context + .read() + .state + .allCountries; return BlocProvider( create: (context) => CreateHeadlineBloc( headlinesRepository: context.read>(), sourcesRepository: context.read>(), topicsRepository: context.read>(), - countriesRepository: context.read>(), + countries: allCountries, )..add(const CreateHeadlineDataLoaded()), child: const _CreateHeadlineView(), ); @@ -220,9 +226,6 @@ class _CreateHeadlineViewState extends State<_CreateHeadlineView> { decoration: InputDecoration( labelText: l10n.countryName, border: const OutlineInputBorder(), - helperText: state.countriesIsLoadingMore - ? l10n.loadingFullList - : null, ), items: [ DropdownMenuItem(value: null, child: Text(l10n.none)), @@ -249,11 +252,9 @@ class _CreateHeadlineViewState extends State<_CreateHeadlineView> { ), ), ], - onChanged: state.countriesIsLoadingMore - ? null - : (value) => context.read().add( - CreateHeadlineCountryChanged(value), - ), + onChanged: (value) => context + .read() + .add(CreateHeadlineCountryChanged(value)), ), const SizedBox(height: AppSpacing.lg), DropdownButtonFormField( diff --git a/lib/content_management/view/create_source_page.dart b/lib/content_management/view/create_source_page.dart index 2f3ef8c..2d44daa 100644 --- a/lib/content_management/view/create_source_page.dart +++ b/lib/content_management/view/create_source_page.dart @@ -4,7 +4,6 @@ import 'package:flutter/material.dart'; 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/dashboard/bloc/dashboard_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'; @@ -21,12 +20,19 @@ 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>(), - countriesRepository: context.read>(), - languagesRepository: context.read>(), - )..add(const CreateSourceDataLoaded()), + countries: allCountries, + languages: allLanguages, + ), child: const _CreateSourceView(), ); } @@ -114,8 +120,8 @@ class _CreateSourceViewState extends State<_CreateSourceView> { if (state.status == CreateSourceStatus.failure) { return FailureStateWidget( exception: state.exception!, - onRetry: () => context.read().add( - const CreateSourceDataLoaded(), + onRetry: () => context.read().add( + const SharedDataRequested(), ), ); } @@ -167,9 +173,6 @@ class _CreateSourceViewState extends State<_CreateSourceView> { decoration: InputDecoration( labelText: l10n.language, border: const OutlineInputBorder(), - helperText: state.languagesIsLoadingMore - ? l10n.loadingFullList - : null, ), items: [ DropdownMenuItem(value: null, child: Text(l10n.none)), @@ -180,11 +183,9 @@ class _CreateSourceViewState extends State<_CreateSourceView> { ), ), ], - onChanged: state.languagesIsLoadingMore - ? null - : (value) => context.read().add( - CreateSourceLanguageChanged(value), - ), + onChanged: (value) => context + .read() + .add(CreateSourceLanguageChanged(value)), ), const SizedBox(height: AppSpacing.lg), DropdownButtonFormField( @@ -212,9 +213,6 @@ class _CreateSourceViewState extends State<_CreateSourceView> { decoration: InputDecoration( labelText: l10n.headquarters, border: const OutlineInputBorder(), - helperText: state.countriesIsLoadingMore - ? l10n.loadingFullList - : null, ), items: [ DropdownMenuItem(value: null, child: Text(l10n.none)), @@ -241,11 +239,9 @@ class _CreateSourceViewState extends State<_CreateSourceView> { ), ), ], - onChanged: state.countriesIsLoadingMore - ? null - : (value) => context.read().add( - CreateSourceHeadquartersChanged(value), - ), + onChanged: (value) => context + .read() + .add(CreateSourceHeadquartersChanged(value)), ), const SizedBox(height: AppSpacing.lg), DropdownButtonFormField( diff --git a/lib/content_management/view/create_topic_page.dart b/lib/content_management/view/create_topic_page.dart index 6fdac2a..575ca93 100644 --- a/lib/content_management/view/create_topic_page.dart +++ b/lib/content_management/view/create_topic_page.dart @@ -4,7 +4,6 @@ import 'package:flutter/material.dart'; 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_topic/create_topic_bloc.dart'; -import 'package:flutter_news_app_web_dashboard_full_source_code/dashboard/bloc/dashboard_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/shared.dart'; import 'package:go_router/go_router.dart'; diff --git a/lib/content_management/view/edit_headline_page.dart b/lib/content_management/view/edit_headline_page.dart index 2c7dd21..f301492 100644 --- a/lib/content_management/view/edit_headline_page.dart +++ b/lib/content_management/view/edit_headline_page.dart @@ -4,7 +4,6 @@ import 'package:flutter/material.dart'; 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_headline/edit_headline_bloc.dart'; -import 'package:flutter_news_app_web_dashboard_full_source_code/dashboard/bloc/dashboard_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/shared.dart'; import 'package:go_router/go_router.dart'; @@ -23,12 +22,19 @@ 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 allCountries = context + .read() + .state + .allCountries; return BlocProvider( create: (context) => EditHeadlineBloc( headlinesRepository: context.read>(), sourcesRepository: context.read>(), topicsRepository: context.read>(), - countriesRepository: context.read>(), + countries: allCountries, headlineId: headlineId, )..add(const EditHeadlineLoaded()), child: const _EditHeadlineView(), @@ -288,9 +294,6 @@ class _EditHeadlineViewState extends State<_EditHeadlineView> { decoration: InputDecoration( labelText: l10n.countryName, border: const OutlineInputBorder(), - helperText: state.countriesIsLoadingMore - ? l10n.loadingFullList - : null, ), items: [ DropdownMenuItem(value: null, child: Text(l10n.none)), @@ -317,11 +320,9 @@ class _EditHeadlineViewState extends State<_EditHeadlineView> { ), ), ], - onChanged: state.countriesIsLoadingMore - ? null - : (value) => context.read().add( - EditHeadlineCountryChanged(value), - ), + onChanged: (value) => context + .read() + .add(EditHeadlineCountryChanged(value)), ), const SizedBox(height: AppSpacing.lg), DropdownButtonFormField( diff --git a/lib/content_management/view/edit_source_page.dart b/lib/content_management/view/edit_source_page.dart index 3ef6aba..185d0c4 100644 --- a/lib/content_management/view/edit_source_page.dart +++ b/lib/content_management/view/edit_source_page.dart @@ -4,7 +4,6 @@ import 'package:flutter/material.dart'; 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/dashboard/bloc/dashboard_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'; @@ -24,11 +23,18 @@ 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>(), - countriesRepository: context.read>(), - languagesRepository: context.read>(), + countries: allCountries, + languages: allLanguages, sourceId: sourceId, )..add(const EditSourceLoaded()), child: const _EditSourceView(), @@ -197,9 +203,6 @@ class _EditSourceViewState extends State<_EditSourceView> { decoration: InputDecoration( labelText: l10n.language, border: const OutlineInputBorder(), - helperText: state.languagesIsLoadingMore - ? l10n.loadingFullList - : null, ), items: [ DropdownMenuItem(value: null, child: Text(l10n.none)), @@ -210,11 +213,9 @@ class _EditSourceViewState extends State<_EditSourceView> { ), ), ], - onChanged: state.languagesIsLoadingMore - ? null - : (value) => context.read().add( - EditSourceLanguageChanged(value), - ), + onChanged: (value) => context.read().add( + EditSourceLanguageChanged(value), + ), ), const SizedBox(height: AppSpacing.lg), DropdownButtonFormField( @@ -242,9 +243,6 @@ class _EditSourceViewState extends State<_EditSourceView> { decoration: InputDecoration( labelText: l10n.headquarters, border: const OutlineInputBorder(), - helperText: state.countriesIsLoadingMore - ? l10n.loadingFullList - : null, ), items: [ DropdownMenuItem(value: null, child: Text(l10n.none)), @@ -271,11 +269,9 @@ class _EditSourceViewState extends State<_EditSourceView> { ), ), ], - onChanged: state.countriesIsLoadingMore - ? null - : (value) => context.read().add( - EditSourceHeadquartersChanged(value), - ), + onChanged: (value) => context.read().add( + EditSourceHeadquartersChanged(value), + ), ), const SizedBox(height: AppSpacing.lg), DropdownButtonFormField( diff --git a/lib/content_management/view/edit_topic_page.dart b/lib/content_management/view/edit_topic_page.dart index 04c3008..92f2421 100644 --- a/lib/content_management/view/edit_topic_page.dart +++ b/lib/content_management/view/edit_topic_page.dart @@ -4,7 +4,6 @@ import 'package:flutter/material.dart'; 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_topic/edit_topic_bloc.dart'; -import 'package:flutter_news_app_web_dashboard_full_source_code/dashboard/bloc/dashboard_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/shared.dart'; import 'package:go_router/go_router.dart'; diff --git a/lib/content_management/view/headlines_page.dart b/lib/content_management/view/headlines_page.dart index fd84750..c67cc69 100644 --- a/lib/content_management/view/headlines_page.dart +++ b/lib/content_management/view/headlines_page.dart @@ -27,8 +27,8 @@ class _HeadlinesPageState extends State { void initState() { super.initState(); context.read().add( - const LoadHeadlinesRequested(limit: kDefaultRowsPerPage), - ); + const LoadHeadlinesRequested(limit: kDefaultRowsPerPage), + ); } @override @@ -51,8 +51,8 @@ class _HeadlinesPageState extends State { return FailureStateWidget( exception: state.exception!, onRetry: () => context.read().add( - const LoadHeadlinesRequested(limit: kDefaultRowsPerPage), - ), + const LoadHeadlinesRequested(limit: kDefaultRowsPerPage), + ), ); } @@ -102,11 +102,11 @@ class _HeadlinesPageState extends State { state.headlinesStatus != ContentManagementStatus.loading) { context.read().add( - LoadHeadlinesRequested( - startAfterId: state.headlinesCursor, - limit: kDefaultRowsPerPage, - ), - ); + LoadHeadlinesRequested( + startAfterId: state.headlinesCursor, + limit: kDefaultRowsPerPage, + ), + ); } }, empty: Center(child: Text(l10n.noHeadlinesFound)), @@ -189,8 +189,8 @@ class _HeadlinesDataSource extends DataTableSource { tooltip: l10n.archive, onPressed: () { context.read().add( - ArchiveHeadlineRequested(headline.id), - ); + ArchiveHeadlineRequested(headline.id), + ); }, ), ], diff --git a/lib/content_management/view/sources_page.dart b/lib/content_management/view/sources_page.dart index 9c14cc8..0a99314 100644 --- a/lib/content_management/view/sources_page.dart +++ b/lib/content_management/view/sources_page.dart @@ -103,11 +103,11 @@ class _SourcesPageState extends State { state.sourcesStatus != ContentManagementStatus.loading) { context.read().add( - LoadSourcesRequested( - startAfterId: state.sourcesCursor, - limit: kDefaultRowsPerPage, - ), - ); + LoadSourcesRequested( + startAfterId: state.sourcesCursor, + limit: kDefaultRowsPerPage, + ), + ); } }, empty: Center(child: Text(l10n.noSourcesFound)), @@ -191,8 +191,8 @@ class _SourcesDataSource extends DataTableSource { onPressed: () { // Dispatch delete event context.read().add( - ArchiveSourceRequested(source.id), - ); + ArchiveSourceRequested(source.id), + ); }, ), ], diff --git a/lib/content_management/view/topics_page.dart b/lib/content_management/view/topics_page.dart index c9b6439..5af0fe1 100644 --- a/lib/content_management/view/topics_page.dart +++ b/lib/content_management/view/topics_page.dart @@ -27,8 +27,8 @@ class _TopicPageState extends State { void initState() { super.initState(); context.read().add( - const LoadTopicsRequested(limit: kDefaultRowsPerPage), - ); + const LoadTopicsRequested(limit: kDefaultRowsPerPage), + ); } @override @@ -51,8 +51,8 @@ class _TopicPageState extends State { return FailureStateWidget( exception: state.exception!, onRetry: () => context.read().add( - const LoadTopicsRequested(limit: kDefaultRowsPerPage), - ), + const LoadTopicsRequested(limit: kDefaultRowsPerPage), + ), ); } @@ -97,11 +97,11 @@ class _TopicPageState extends State { state.topicsHasMore && state.topicsStatus != ContentManagementStatus.loading) { context.read().add( - LoadTopicsRequested( - startAfterId: state.topicsCursor, - limit: kDefaultRowsPerPage, - ), - ); + LoadTopicsRequested( + startAfterId: state.topicsCursor, + limit: kDefaultRowsPerPage, + ), + ); } }, empty: Center(child: Text(l10n.noTopicsFound)), @@ -184,8 +184,8 @@ class _TopicsDataSource extends DataTableSource { onPressed: () { // Dispatch delete event context.read().add( - ArchiveTopicRequested(topic.id), - ); + ArchiveTopicRequested(topic.id), + ); }, ), ],