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' ;
@@ -8,6 +9,8 @@ import 'package:uuid/uuid.dart';
8
9
part 'create_headline_event.dart' ;
9
10
part 'create_headline_state.dart' ;
10
11
12
+ const _searchDebounceDuration = Duration (milliseconds: 300 );
13
+
11
14
/// A BLoC to manage the state of creating a new headline.
12
15
class CreateHeadlineBloc
13
16
extends Bloc <CreateHeadlineEvent , CreateHeadlineState > {
@@ -32,6 +35,12 @@ class CreateHeadlineBloc
32
35
on < CreateHeadlineCountryChanged > (_onCountryChanged);
33
36
on < CreateHeadlineStatusChanged > (_onStatusChanged);
34
37
on < CreateHeadlineSubmitted > (_onSubmitted);
38
+ on < CreateHeadlineCountrySearchChanged > (
39
+ _onCountrySearchChanged,
40
+ transformer: restartable (),
41
+ );
42
+ on < CreateHeadlineLoadMoreCountriesRequested > (
43
+ _onLoadMoreCountriesRequested);
35
44
}
36
45
37
46
final DataRepository <Headline > _headlinesRepository;
@@ -46,33 +55,30 @@ class CreateHeadlineBloc
46
55
) async {
47
56
emit (state.copyWith (status: CreateHeadlineStatus .loading));
48
57
try {
49
- final [
50
- sourcesResponse,
51
- topicsResponse,
52
- countriesResponse,
53
- ] = await Future .wait ([
58
+ final [sourcesResponse, topicsResponse] = await Future .wait ([
54
59
_sourcesRepository.readAll (
55
60
sort: [const SortOption ('updatedAt' , SortOrder .desc)],
56
61
),
57
62
_topicsRepository.readAll (
58
63
sort: [const SortOption ('updatedAt' , SortOrder .desc)],
59
64
),
60
- _countriesRepository.readAll (
61
- sort: [const SortOption ('name' , SortOrder .asc)],
62
- ),
63
65
]);
64
66
65
67
final sources = (sourcesResponse as PaginatedResponse <Source >).items;
66
68
final topics = (topicsResponse as PaginatedResponse <Topic >).items;
67
- final countries =
68
- (countriesResponse as PaginatedResponse <Country >).items;
69
+
70
+ final countriesResponse = await _countriesRepository.readAll (
71
+ sort: [const SortOption ('name' , SortOrder .asc)],
72
+ );
69
73
70
74
emit (
71
75
state.copyWith (
72
76
status: CreateHeadlineStatus .initial,
73
77
sources: sources,
74
78
topics: topics,
75
- countries: countries,
79
+ countries: countriesResponse.items,
80
+ countriesCursor: countriesResponse.cursor,
81
+ countriesHasMore: countriesResponse.hasMore,
76
82
),
77
83
);
78
84
} on HttpException catch (e) {
@@ -189,4 +195,72 @@ class CreateHeadlineBloc
189
195
);
190
196
}
191
197
}
198
+
199
+ Future <void > _onCountrySearchChanged (
200
+ CreateHeadlineCountrySearchChanged event,
201
+ Emitter <CreateHeadlineState > emit,
202
+ ) async {
203
+ await Future <void >.delayed (_searchDebounceDuration);
204
+ emit (state.copyWith (countrySearchTerm: event.searchTerm));
205
+ try {
206
+ final countriesResponse = await _countriesRepository.readAll (
207
+ filter:
208
+ event.searchTerm.isNotEmpty ? {'name' : event.searchTerm} : null ,
209
+ sort: [const SortOption ('name' , SortOrder .asc)],
210
+ );
211
+
212
+ emit (
213
+ state.copyWith (
214
+ countries: countriesResponse.items,
215
+ countriesCursor: countriesResponse.cursor,
216
+ countriesHasMore: countriesResponse.hasMore,
217
+ ),
218
+ );
219
+ } on HttpException catch (e) {
220
+ emit (state.copyWith (status: CreateHeadlineStatus .failure, exception: e));
221
+ } catch (e) {
222
+ emit (
223
+ state.copyWith (
224
+ status: CreateHeadlineStatus .failure,
225
+ exception: UnknownException ('An unexpected error occurred: $e ' ),
226
+ ),
227
+ );
228
+ }
229
+ }
230
+
231
+ Future <void > _onLoadMoreCountriesRequested (
232
+ CreateHeadlineLoadMoreCountriesRequested event,
233
+ Emitter <CreateHeadlineState > emit,
234
+ ) async {
235
+ if (! state.countriesHasMore) return ;
236
+
237
+ try {
238
+ final countriesResponse = await _countriesRepository.readAll (
239
+ pagination: state.countriesCursor != null
240
+ ? PaginationOptions (cursor: state.countriesCursor)
241
+ : null ,
242
+ filter: state.countrySearchTerm.isNotEmpty
243
+ ? {'name' : state.countrySearchTerm}
244
+ : null ,
245
+ sort: [const SortOption ('name' , SortOrder .asc)],
246
+ );
247
+
248
+ emit (
249
+ state.copyWith (
250
+ countries: List .of (state.countries)..addAll (countriesResponse.items),
251
+ countriesCursor: countriesResponse.cursor,
252
+ countriesHasMore: countriesResponse.hasMore,
253
+ ),
254
+ );
255
+ } on HttpException catch (e) {
256
+ emit (state.copyWith (status: CreateHeadlineStatus .failure, exception: e));
257
+ } catch (e) {
258
+ emit (
259
+ state.copyWith (
260
+ status: CreateHeadlineStatus .failure,
261
+ exception: UnknownException ('An unexpected error occurred: $e ' ),
262
+ ),
263
+ );
264
+ }
265
+ }
192
266
}
0 commit comments