1
1
import 'package:bloc/bloc.dart' ;
2
+ import 'package:bloc_concurrency/bloc_concurrency.dart' ;
2
3
import 'package:core/core.dart' ;
3
4
import 'package:data_repository/data_repository.dart' ;
4
5
import 'package:equatable/equatable.dart' ;
@@ -7,6 +8,8 @@ import 'package:flutter/foundation.dart';
7
8
part 'edit_source_event.dart' ;
8
9
part 'edit_source_state.dart' ;
9
10
11
+ const _searchDebounceDuration = Duration (milliseconds: 300 );
12
+
10
13
/// A BLoC to manage the state of editing a single source.
11
14
class EditSourceBloc extends Bloc <EditSourceEvent , EditSourceState > {
12
15
/// {@macro edit_source_bloc}
@@ -29,6 +32,20 @@ class EditSourceBloc extends Bloc<EditSourceEvent, EditSourceState> {
29
32
on < EditSourceHeadquartersChanged > (_onHeadquartersChanged);
30
33
on < EditSourceStatusChanged > (_onStatusChanged);
31
34
on < EditSourceSubmitted > (_onSubmitted);
35
+ on < EditSourceCountrySearchChanged > (
36
+ _onCountrySearchChanged,
37
+ transformer: debounce (_searchDebounceDuration),
38
+ );
39
+ on < EditSourceLoadMoreCountriesRequested > (
40
+ _onLoadMoreCountriesRequested,
41
+ );
42
+ on < EditSourceLanguageSearchChanged > (
43
+ _onLanguageSearchChanged,
44
+ transformer: debounce (_searchDebounceDuration),
45
+ );
46
+ on < EditSourceLoadMoreLanguagesRequested > (
47
+ _onLoadMoreLanguagesRequested,
48
+ );
32
49
}
33
50
34
51
final DataRepository <Source > _sourcesRepository;
@@ -42,23 +59,20 @@ class EditSourceBloc extends Bloc<EditSourceEvent, EditSourceState> {
42
59
) async {
43
60
emit (state.copyWith (status: EditSourceStatus .loading));
44
61
try {
45
- final [
46
- sourceResponse,
47
- countriesResponse,
48
- languagesResponse,
49
- ] = await Future .wait ([
62
+ final [sourceResponse, countriesPaginated, languagesPaginated] =
63
+ await Future .wait ([
50
64
_sourcesRepository.read (id: _sourceId),
51
65
_countriesRepository.readAll (
52
66
sort: [const SortOption ('name' , SortOrder .asc)],
53
- ),
67
+ ) as Future < PaginatedResponse < Country >> ,
54
68
_languagesRepository.readAll (
55
69
sort: [const SortOption ('name' , SortOrder .asc)],
56
- ),
70
+ ) as Future < PaginatedResponse < Language >> ,
57
71
]);
58
72
59
73
final source = sourceResponse as Source ;
60
- final countries = (countriesResponse as PaginatedResponse < Country >) .items;
61
- final languages = (languagesResponse as PaginatedResponse < Language >) .items;
74
+ final countries = countriesPaginated .items;
75
+ final languages = languagesPaginated .items;
62
76
63
77
// The source contains a Language object. We need to find the equivalent
64
78
// object in the full list of languages to ensure the DropdownButton
@@ -79,8 +93,12 @@ class EditSourceBloc extends Bloc<EditSourceEvent, EditSourceState> {
79
93
language: () => selectedLanguage,
80
94
headquarters: () => source.headquarters,
81
95
contentStatus: source.status,
82
- countries: countries,
83
- languages: languages,
96
+ countries: countriesPaginated.items,
97
+ countriesCursor: countriesPaginated.cursor,
98
+ countriesHasMore: countriesPaginated.hasMore,
99
+ languages: languagesPaginated.items,
100
+ languagesCursor: languagesPaginated.cursor,
101
+ languagesHasMore: languagesPaginated.hasMore,
84
102
),
85
103
);
86
104
} on HttpException catch (e) {
@@ -219,4 +237,114 @@ class EditSourceBloc extends Bloc<EditSourceEvent, EditSourceState> {
219
237
);
220
238
}
221
239
}
240
+
241
+ Future <void > _onCountrySearchChanged (
242
+ EditSourceCountrySearchChanged event,
243
+ Emitter <EditSourceState > emit,
244
+ ) async {
245
+ emit (state.copyWith (countrySearchTerm: event.searchTerm));
246
+ try {
247
+ final countriesResponse = await _countriesRepository.readAll (
248
+ filter: {'name' : event.searchTerm},
249
+ sort: [const SortOption ('name' , SortOrder .asc)],
250
+ ) as PaginatedResponse <Country >;
251
+
252
+ emit (
253
+ state.copyWith (
254
+ countries: countriesResponse.items,
255
+ countriesCursor: countriesResponse.cursor,
256
+ countriesHasMore: countriesResponse.hasMore,
257
+ ),
258
+ );
259
+ } on HttpException catch (e) {
260
+ emit (state.copyWith (status: EditSourceStatus .failure, exception: e));
261
+ } catch (e) {
262
+ emit (
263
+ state.copyWith (
264
+ status: EditSourceStatus .failure,
265
+ exception: UnknownException ('An unexpected error occurred: $e ' ),
266
+ ),
267
+ );
268
+ }
269
+ }
270
+
271
+ Future <void > _onLoadMoreCountriesRequested (
272
+ EditSourceLoadMoreCountriesRequested event,
273
+ Emitter <EditSourceState > emit,
274
+ ) async {
275
+ if (! state.countriesHasMore) return ;
276
+
277
+ try {
278
+ final countriesResponse = await _countriesRepository.readAll (
279
+ cursor: state.countriesCursor,
280
+ filter: {'name' : state.countrySearchTerm},
281
+ sort: [const SortOption ('name' , SortOrder .asc)],
282
+ ) as PaginatedResponse <Country >;
283
+
284
+ emit (
285
+ state.copyWith (
286
+ countries: List .of (state.countries)..addAll (countriesResponse.items),
287
+ countriesCursor: countriesResponse.cursor,
288
+ countriesHasMore: countriesResponse.hasMore,
289
+ ),
290
+ );
291
+ } on HttpException catch (e) {
292
+ emit (state.copyWith (status: EditSourceStatus .failure, exception: e));
293
+ } catch (e) {
294
+ emit (
295
+ state.copyWith (
296
+ status: EditSourceStatus .failure,
297
+ exception: UnknownException ('An unexpected error occurred: $e ' ),
298
+ ),
299
+ );
300
+ }
301
+ }
302
+
303
+ Future <void > _onLanguageSearchChanged (
304
+ EditSourceLanguageSearchChanged event,
305
+ Emitter <EditSourceState > emit,
306
+ ) async {
307
+ emit (state.copyWith (languageSearchTerm: event.searchTerm));
308
+ try {
309
+ final languagesResponse = await _languagesRepository.readAll (
310
+ filter: {'name' : event.searchTerm},
311
+ sort: [const SortOption ('name' , SortOrder .asc)],
312
+ ) as PaginatedResponse <Language >;
313
+
314
+ emit (
315
+ state.copyWith (
316
+ languages: languagesResponse.items,
317
+ languagesCursor: languagesResponse.cursor,
318
+ languagesHasMore: languagesResponse.hasMore,
319
+ ),
320
+ );
321
+ } catch (e) {
322
+ // Proper error handling should be implemented here
323
+ }
324
+ }
325
+
326
+ Future <void > _onLoadMoreLanguagesRequested (
327
+ EditSourceLoadMoreLanguagesRequested event,
328
+ Emitter <EditSourceState > emit,
329
+ ) async {
330
+ if (! state.languagesHasMore) return ;
331
+
332
+ try {
333
+ final languagesResponse = await _languagesRepository.readAll (
334
+ cursor: state.languagesCursor,
335
+ filter: {'name' : state.languageSearchTerm},
336
+ sort: [const SortOption ('name' , SortOrder .asc)],
337
+ ) as PaginatedResponse <Language >;
338
+
339
+ emit (
340
+ state.copyWith (
341
+ languages: List .of (state.languages)..addAll (languagesResponse.items),
342
+ languagesCursor: languagesResponse.cursor,
343
+ languagesHasMore: languagesResponse.hasMore,
344
+ ),
345
+ );
346
+ } catch (e) {
347
+ // Proper error handling should be implemented here
348
+ }
349
+ }
222
350
}
0 commit comments