Skip to content

Commit 3a7199e

Browse files
committed
refactor(content_management): simplify source editing process
- Remove pagination and background fetching for countries and languages - Load full lists of countries and languages at once - Cache countries and languages in ContentManagementBloc - Update EditSourceBloc to use cached lists - Simplify UI by removing loading indicators for dropdowns
1 parent 5f10e99 commit 3a7199e

File tree

3 files changed

+27
-189
lines changed

3 files changed

+27
-189
lines changed

lib/content_management/bloc/edit_source/edit_source_bloc.dart

Lines changed: 12 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,22 @@ import 'package:flutter/foundation.dart';
77
part 'edit_source_event.dart';
88
part 'edit_source_state.dart';
99

10-
final class _FetchNextCountryPage extends EditSourceEvent {
11-
const _FetchNextCountryPage();
12-
}
13-
14-
final class _FetchNextLanguagePage extends EditSourceEvent {
15-
const _FetchNextLanguagePage();
16-
}
17-
1810
/// A BLoC to manage the state of editing a single source.
1911
class EditSourceBloc extends Bloc<EditSourceEvent, EditSourceState> {
2012
/// {@macro edit_source_bloc}
2113
EditSourceBloc({
2214
required DataRepository<Source> sourcesRepository,
23-
required DataRepository<Country> countriesRepository,
24-
required DataRepository<Language> languagesRepository,
15+
required List<Country> countries,
16+
required List<Language> languages,
2517
required String sourceId,
26-
}) : _sourcesRepository = sourcesRepository,
27-
_countriesRepository = countriesRepository,
28-
_languagesRepository = languagesRepository,
29-
_sourceId = sourceId,
30-
super(const EditSourceState()) {
18+
}) : _sourcesRepository = sourcesRepository,
19+
_sourceId = sourceId,
20+
super(
21+
EditSourceState(
22+
countries: countries,
23+
languages: languages,
24+
),
25+
) {
3126
on<EditSourceLoaded>(_onLoaded);
3227
on<EditSourceNameChanged>(_onNameChanged);
3328
on<EditSourceDescriptionChanged>(_onDescriptionChanged);
@@ -37,13 +32,9 @@ class EditSourceBloc extends Bloc<EditSourceEvent, EditSourceState> {
3732
on<EditSourceHeadquartersChanged>(_onHeadquartersChanged);
3833
on<EditSourceStatusChanged>(_onStatusChanged);
3934
on<EditSourceSubmitted>(_onSubmitted);
40-
on<_FetchNextCountryPage>(_onFetchNextCountryPage);
41-
on<_FetchNextLanguagePage>(_onFetchNextLanguagePage);
4235
}
4336

4437
final DataRepository<Source> _sourcesRepository;
45-
final DataRepository<Country> _countriesRepository;
46-
final DataRepository<Language> _languagesRepository;
4738
final String _sourceId;
4839

4940
Future<void> _onLoaded(
@@ -52,32 +43,7 @@ class EditSourceBloc extends Bloc<EditSourceEvent, EditSourceState> {
5243
) async {
5344
emit(state.copyWith(status: EditSourceStatus.loading));
5445
try {
55-
final responses = await Future.wait<dynamic>([
56-
_sourcesRepository.read(id: _sourceId),
57-
_countriesRepository.readAll(
58-
sort: [const SortOption('name', SortOrder.asc)],
59-
filter: {'status': ContentStatus.active.name},
60-
),
61-
_languagesRepository.readAll(
62-
sort: [const SortOption('name', SortOrder.asc)],
63-
filter: {'status': ContentStatus.active.name},
64-
),
65-
]);
66-
67-
final source = responses[0] as Source;
68-
final countriesPaginated = responses[1] as PaginatedResponse<Country>;
69-
final languagesPaginated = responses[2] as PaginatedResponse<Language>;
70-
71-
Language? selectedLanguage;
72-
try {
73-
// Find the equivalent language object from the full list.
74-
// This ensures the DropdownButton can identify it by reference.
75-
selectedLanguage = languagesPaginated.items.firstWhere(
76-
(listLanguage) => listLanguage.id == source.language.id,
77-
);
78-
} catch (_) {
79-
selectedLanguage = source.language;
80-
}
46+
final source = await _sourcesRepository.read(id: _sourceId);
8147

8248
emit(
8349
state.copyWith(
@@ -87,26 +53,11 @@ class EditSourceBloc extends Bloc<EditSourceEvent, EditSourceState> {
8753
description: source.description,
8854
url: source.url,
8955
sourceType: () => source.sourceType,
90-
language: () => selectedLanguage,
56+
language: () => source.language,
9157
headquarters: () => source.headquarters,
9258
contentStatus: source.status,
93-
countries: countriesPaginated.items,
94-
countriesCursor: countriesPaginated.cursor,
95-
countriesHasMore: countriesPaginated.hasMore,
96-
languages: languagesPaginated.items,
97-
languagesCursor: languagesPaginated.cursor,
98-
languagesHasMore: languagesPaginated.hasMore,
9959
),
10060
);
101-
102-
// After the initial page is loaded, start background processes to
103-
// fetch all remaining pages for countries and languages.
104-
if (state.countriesHasMore) {
105-
add(const _FetchNextCountryPage());
106-
}
107-
if (state.languagesHasMore) {
108-
add(const _FetchNextLanguagePage());
109-
}
11061
} on HttpException catch (e) {
11162
emit(state.copyWith(status: EditSourceStatus.failure, exception: e));
11263
} catch (e) {
@@ -194,84 +145,6 @@ class EditSourceBloc extends Bloc<EditSourceEvent, EditSourceState> {
194145
}
195146

196147
// --- Background Data Fetching for Dropdown ---
197-
// The DropdownButtonFormField widget does not natively support on-scroll
198-
// pagination. To preserve UI consistency across the application, this BLoC
199-
// employs an event-driven background fetching mechanism.
200-
//
201-
// After the first page of items is loaded, a chain of events is initiated
202-
// to progressively fetch all remaining pages. This process is throttled
203-
// and runs in the background, ensuring the UI remains responsive while the
204-
// full list of dropdown options is populated over time.
205-
Future<void> _onFetchNextCountryPage(
206-
_FetchNextCountryPage event,
207-
Emitter<EditSourceState> emit,
208-
) async {
209-
if (!state.countriesHasMore || state.countriesIsLoadingMore) return;
210-
211-
try {
212-
emit(state.copyWith(countriesIsLoadingMore: true));
213-
214-
// ignore: inference_failure_on_instance_creation
215-
await Future.delayed(const Duration(milliseconds: 400));
216-
217-
final nextCountries = await _countriesRepository.readAll(
218-
pagination: PaginationOptions(cursor: state.countriesCursor),
219-
sort: [const SortOption('name', SortOrder.asc)],
220-
);
221-
222-
emit(
223-
state.copyWith(
224-
countries: List.of(state.countries)..addAll(nextCountries.items),
225-
countriesCursor: nextCountries.cursor,
226-
countriesHasMore: nextCountries.hasMore,
227-
countriesIsLoadingMore: false,
228-
),
229-
);
230-
231-
if (nextCountries.hasMore) {
232-
add(const _FetchNextCountryPage());
233-
}
234-
} catch (e) {
235-
emit(state.copyWith(countriesIsLoadingMore: false));
236-
// Optionally log the error without disrupting the user
237-
}
238-
}
239-
240-
Future<void> _onFetchNextLanguagePage(
241-
_FetchNextLanguagePage event,
242-
Emitter<EditSourceState> emit,
243-
) async {
244-
if (!state.languagesHasMore || state.languagesIsLoadingMore) return;
245-
246-
try {
247-
emit(state.copyWith(languagesIsLoadingMore: true));
248-
249-
// ignore: inference_failure_on_instance_creation
250-
await Future.delayed(const Duration(milliseconds: 400));
251-
252-
final nextLanguages = await _languagesRepository.readAll(
253-
pagination: PaginationOptions(cursor: state.languagesCursor),
254-
sort: [const SortOption('name', SortOrder.asc)],
255-
);
256-
257-
emit(
258-
state.copyWith(
259-
languages: List.of(state.languages)..addAll(nextLanguages.items),
260-
languagesCursor: nextLanguages.cursor,
261-
languagesHasMore: nextLanguages.hasMore,
262-
languagesIsLoadingMore: false,
263-
),
264-
);
265-
266-
if (nextLanguages.hasMore) {
267-
add(const _FetchNextLanguagePage());
268-
}
269-
} catch (e) {
270-
emit(state.copyWith(languagesIsLoadingMore: false));
271-
// Optionally log the error without disrupting the user
272-
}
273-
}
274-
275148
Future<void> _onSubmitted(
276149
EditSourceSubmitted event,
277150
Emitter<EditSourceState> emit,

lib/content_management/bloc/edit_source/edit_source_state.dart

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,7 @@ final class EditSourceState extends Equatable {
3030
this.language,
3131
this.headquarters,
3232
this.countries = const [],
33-
this.countriesHasMore = true,
34-
this.countriesIsLoadingMore = false,
35-
this.countriesCursor,
3633
this.languages = const [],
37-
this.languagesHasMore = true,
38-
this.languagesIsLoadingMore = false,
39-
this.languagesCursor,
4034
this.contentStatus = ContentStatus.active,
4135
this.exception,
4236
this.updatedSource,
@@ -51,13 +45,7 @@ final class EditSourceState extends Equatable {
5145
final Language? language;
5246
final Country? headquarters;
5347
final List<Country> countries;
54-
final bool countriesHasMore;
55-
final bool countriesIsLoadingMore;
56-
final String? countriesCursor;
5748
final List<Language> languages;
58-
final bool languagesHasMore;
59-
final bool languagesIsLoadingMore;
60-
final String? languagesCursor;
6149
final ContentStatus contentStatus;
6250
final HttpException? exception;
6351
final Source? updatedSource;
@@ -81,13 +69,7 @@ final class EditSourceState extends Equatable {
8169
ValueGetter<Language?>? language,
8270
ValueGetter<Country?>? headquarters,
8371
List<Country>? countries,
84-
bool? countriesHasMore,
85-
bool? countriesIsLoadingMore,
86-
String? countriesCursor,
8772
List<Language>? languages,
88-
bool? languagesHasMore,
89-
bool? languagesIsLoadingMore,
90-
String? languagesCursor,
9173
ContentStatus? contentStatus,
9274
HttpException? exception,
9375
Source? updatedSource,
@@ -102,15 +84,7 @@ final class EditSourceState extends Equatable {
10284
language: language != null ? language() : this.language,
10385
headquarters: headquarters != null ? headquarters() : this.headquarters,
10486
countries: countries ?? this.countries,
105-
countriesHasMore: countriesHasMore ?? this.countriesHasMore,
106-
countriesIsLoadingMore:
107-
countriesIsLoadingMore ?? this.countriesIsLoadingMore,
108-
countriesCursor: countriesCursor ?? this.countriesCursor,
10987
languages: languages ?? this.languages,
110-
languagesHasMore: languagesHasMore ?? this.languagesHasMore,
111-
languagesIsLoadingMore:
112-
languagesIsLoadingMore ?? this.languagesIsLoadingMore,
113-
languagesCursor: languagesCursor ?? this.languagesCursor,
11488
contentStatus: contentStatus ?? this.contentStatus,
11589
exception: exception,
11690
updatedSource: updatedSource ?? this.updatedSource,
@@ -128,13 +102,7 @@ final class EditSourceState extends Equatable {
128102
language,
129103
headquarters,
130104
countries,
131-
countriesHasMore,
132-
countriesIsLoadingMore,
133-
countriesCursor,
134105
languages,
135-
languagesHasMore,
136-
languagesIsLoadingMore,
137-
languagesCursor,
138106
contentStatus,
139107
exception,
140108
updatedSource,

lib/content_management/view/edit_source_page.dart

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,18 @@ class EditSourcePage extends StatelessWidget {
2424

2525
@override
2626
Widget build(BuildContext context) {
27+
// The lists of all countries and languages are fetched once and cached in
28+
// the ContentManagementBloc. We read them here and provide them to the
29+
// EditSourceBloc.
30+
final contentManagementState = context.read<ContentManagementBloc>().state;
31+
final allCountries = contentManagementState.allCountries;
32+
final allLanguages = contentManagementState.allLanguages;
33+
2734
return BlocProvider(
2835
create: (context) => EditSourceBloc(
2936
sourcesRepository: context.read<DataRepository<Source>>(),
30-
countriesRepository: context.read<DataRepository<Country>>(),
31-
languagesRepository: context.read<DataRepository<Language>>(),
37+
countries: allCountries,
38+
languages: allLanguages,
3239
sourceId: sourceId,
3340
)..add(const EditSourceLoaded()),
3441
child: const _EditSourceView(),
@@ -197,9 +204,6 @@ class _EditSourceViewState extends State<_EditSourceView> {
197204
decoration: InputDecoration(
198205
labelText: l10n.language,
199206
border: const OutlineInputBorder(),
200-
helperText: state.languagesIsLoadingMore
201-
? l10n.loadingFullList
202-
: null,
203207
),
204208
items: [
205209
DropdownMenuItem(value: null, child: Text(l10n.none)),
@@ -210,11 +214,9 @@ class _EditSourceViewState extends State<_EditSourceView> {
210214
),
211215
),
212216
],
213-
onChanged: state.languagesIsLoadingMore
214-
? null
215-
: (value) => context.read<EditSourceBloc>().add(
216-
EditSourceLanguageChanged(value),
217-
),
217+
onChanged: (value) => context
218+
.read<EditSourceBloc>()
219+
.add(EditSourceLanguageChanged(value)),
218220
),
219221
const SizedBox(height: AppSpacing.lg),
220222
DropdownButtonFormField<SourceType?>(
@@ -242,9 +244,6 @@ class _EditSourceViewState extends State<_EditSourceView> {
242244
decoration: InputDecoration(
243245
labelText: l10n.headquarters,
244246
border: const OutlineInputBorder(),
245-
helperText: state.countriesIsLoadingMore
246-
? l10n.loadingFullList
247-
: null,
248247
),
249248
items: [
250249
DropdownMenuItem(value: null, child: Text(l10n.none)),
@@ -271,11 +270,9 @@ class _EditSourceViewState extends State<_EditSourceView> {
271270
),
272271
),
273272
],
274-
onChanged: state.countriesIsLoadingMore
275-
? null
276-
: (value) => context.read<EditSourceBloc>().add(
277-
EditSourceHeadquartersChanged(value),
278-
),
273+
onChanged: (value) => context
274+
.read<EditSourceBloc>()
275+
.add(EditSourceHeadquartersChanged(value)),
279276
),
280277
const SizedBox(height: AppSpacing.lg),
281278
DropdownButtonFormField<ContentStatus>(

0 commit comments

Comments
 (0)