Skip to content

Commit ca6729f

Browse files
committed
refactor(search): streamline search logic
- Simplified event handling - Improved state management
1 parent ca1d840 commit ca6729f

File tree

4 files changed

+155
-181
lines changed

4 files changed

+155
-181
lines changed
Lines changed: 54 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import 'package:bloc/bloc.dart';
22
import 'package:equatable/equatable.dart';
33
import 'package:ht_headlines_repository/ht_headlines_repository.dart';
4-
import 'package:stream_transform/stream_transform.dart';
54

65
part 'headlines_search_event.dart';
76
part 'headlines_search_state.dart';
@@ -10,88 +9,71 @@ class HeadlinesSearchBloc
109
extends Bloc<HeadlinesSearchEvent, HeadlinesSearchState> {
1110
HeadlinesSearchBloc({required HtHeadlinesRepository headlinesRepository})
1211
: _headlinesRepository = headlinesRepository,
13-
super(HeadlinesSearchInitial()) {
14-
on<HeadlinesSearchTermChanged>(
15-
_onSearchTermChanged,
16-
transformer: (events, mapper) => events
17-
.debounce(const Duration(milliseconds: 300))
18-
.asyncExpand(mapper),
19-
);
20-
on<HeadlinesSearchRequested>(_onSearchRequested);
21-
on<HeadlinesSearchLoadMore>(_onSearchLoadMore);
12+
super(HeadlinesSearchLoading()) {
13+
on<HeadlinesSearchFetchRequested>(_onSearchFetchRequested);
2214
}
2315

2416
final HtHeadlinesRepository _headlinesRepository;
25-
String _searchTerm = '';
2617
static const _limit = 10;
2718

28-
Future<void> _onSearchTermChanged(
29-
HeadlinesSearchTermChanged event,
19+
Future<void> _onSearchFetchRequested(
20+
HeadlinesSearchFetchRequested event,
3021
Emitter<HeadlinesSearchState> emit,
3122
) async {
32-
_searchTerm = event.searchTerm;
33-
if (_searchTerm.isEmpty) {
34-
emit(HeadlinesSearchInitial());
35-
}
36-
}
37-
38-
Future<void> _onSearchRequested(
39-
HeadlinesSearchRequested event,
40-
Emitter<HeadlinesSearchState> emit,
41-
) async {
42-
if (_searchTerm.isEmpty) {
23+
if (event.searchTerm.isEmpty) {
24+
emit(const HeadlinesSearchSuccess(
25+
headlines: [], hasMore: false, lastSearchTerm: '',),);
4326
return;
4427
}
45-
emit(HeadlinesSearchLoading());
46-
try {
47-
final response = await _headlinesRepository.searchHeadlines(
48-
query: _searchTerm,
49-
limit: _limit,
50-
);
51-
emit(
52-
HeadlinesSearchLoaded(
53-
headlines: response.items,
54-
hasReachedMax: !response.hasMore,
55-
cursor: response.cursor,
56-
),
57-
);
58-
} on HeadlinesSearchException catch (e) {
59-
emit(HeadlinesSearchError(message: e.message));
60-
} catch (e) {
61-
emit(HeadlinesSearchError(message: e.toString()));
62-
}
63-
}
64-
65-
Future<void> _onSearchLoadMore(
66-
HeadlinesSearchLoadMore event,
67-
Emitter<HeadlinesSearchState> emit,
68-
) async {
69-
if (state is! HeadlinesSearchLoaded) return;
70-
71-
final currentState = state as HeadlinesSearchLoaded;
7228

73-
if (currentState.hasReachedMax) return;
29+
if (state is HeadlinesSearchSuccess &&
30+
event.searchTerm == state.lastSearchTerm) {
31+
final currentState = state as HeadlinesSearchSuccess;
32+
if (!currentState.hasMore) return;
7433

75-
try {
76-
final response = await _headlinesRepository.searchHeadlines(
77-
query: _searchTerm,
78-
limit: _limit,
79-
startAfterId: currentState.cursor,
80-
);
81-
emit(
82-
response.items.isEmpty
83-
? currentState.copyWith(hasReachedMax: true)
84-
: currentState.copyWith(
85-
headlines: List.of(currentState.headlines)
86-
..addAll(response.items),
87-
hasReachedMax: !response.hasMore,
88-
cursor: response.cursor,
89-
),
90-
);
91-
} on HeadlinesSearchException catch (e) {
92-
emit(HeadlinesSearchError(message: e.message));
93-
} catch (e) {
94-
emit(HeadlinesSearchError(message: e.toString()));
34+
try {
35+
final response = await _headlinesRepository.searchHeadlines(
36+
query: event.searchTerm,
37+
limit: _limit,
38+
startAfterId: currentState.cursor,
39+
);
40+
emit(
41+
response.items.isEmpty
42+
? currentState.copyWith(hasMore: false)
43+
: currentState.copyWith(
44+
headlines: List.of(currentState.headlines)
45+
..addAll(response.items),
46+
hasMore: response.hasMore,
47+
cursor: response.cursor,
48+
),
49+
);
50+
} catch (e) {
51+
emit(currentState.copyWith(errorMessage: e.toString()));
52+
}
53+
} else {
54+
try {
55+
final response = await _headlinesRepository.searchHeadlines(
56+
query: event.searchTerm,
57+
limit: _limit,
58+
);
59+
emit(
60+
HeadlinesSearchSuccess(
61+
headlines: response.items,
62+
hasMore: response.hasMore,
63+
cursor: response.cursor,
64+
lastSearchTerm: event.searchTerm,
65+
),
66+
);
67+
} catch (e) {
68+
emit(
69+
HeadlinesSearchSuccess(
70+
headlines: const [],
71+
hasMore: false,
72+
errorMessage: e.toString(),
73+
lastSearchTerm: event.searchTerm,
74+
),
75+
);
76+
}
9577
}
9678
}
9779
}

lib/headlines-search/bloc/headlines_search_event.dart

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,11 @@ sealed class HeadlinesSearchEvent extends Equatable {
77
List<Object> get props => [];
88
}
99

10-
final class HeadlinesSearchTermChanged extends HeadlinesSearchEvent {
11-
const HeadlinesSearchTermChanged({required this.searchTerm});
10+
final class HeadlinesSearchFetchRequested extends HeadlinesSearchEvent {
11+
const HeadlinesSearchFetchRequested({required this.searchTerm});
1212

1313
final String searchTerm;
1414

1515
@override
1616
List<Object> get props => [searchTerm];
1717
}
18-
19-
final class HeadlinesSearchRequested extends HeadlinesSearchEvent {}
20-
21-
final class HeadlinesSearchLoadMore extends HeadlinesSearchEvent {}
Lines changed: 32 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,49 @@
11
part of 'headlines_search_bloc.dart';
22

3-
sealed class HeadlinesSearchState extends Equatable {
3+
abstract class HeadlinesSearchState extends Equatable {
44
const HeadlinesSearchState();
5-
5+
abstract final String? lastSearchTerm;
66
@override
77
List<Object?> get props => [];
88
}
99

10-
final class HeadlinesSearchInitial extends HeadlinesSearchState {}
11-
12-
final class HeadlinesSearchLoading extends HeadlinesSearchState {}
10+
class HeadlinesSearchLoading extends HeadlinesSearchState {
11+
@override
12+
final String? lastSearchTerm = null;
13+
@override
14+
List<Object?> get props => [];
15+
}
1316

14-
final class HeadlinesSearchLoaded extends HeadlinesSearchState {
15-
const HeadlinesSearchLoaded({
17+
class HeadlinesSearchSuccess extends HeadlinesSearchState {
18+
const HeadlinesSearchSuccess({
1619
required this.headlines,
17-
this.hasReachedMax = false,
18-
this.cursor,
20+
required this.hasMore,
21+
required this.lastSearchTerm, this.cursor,
22+
this.errorMessage,
1923
});
2024

2125
final List<Headline> headlines;
22-
final bool hasReachedMax;
26+
final bool hasMore;
2327
final String? cursor;
24-
25-
HeadlinesSearchLoaded copyWith({
26-
List<Headline>? headlines,
27-
bool? hasReachedMax,
28-
String? cursor,
29-
}) {
30-
return HeadlinesSearchLoaded(
31-
headlines: headlines ?? this.headlines,
32-
hasReachedMax: hasReachedMax ?? this.hasReachedMax,
33-
cursor: cursor ?? this.cursor,
34-
);
35-
}
36-
28+
final String? errorMessage;
3729
@override
38-
List<Object?> get props => [headlines, hasReachedMax, cursor];
39-
}
40-
41-
final class HeadlinesSearchError extends HeadlinesSearchState {
42-
const HeadlinesSearchError({required this.message});
43-
44-
final String message;
30+
final String? lastSearchTerm;
31+
32+
HeadlinesSearchSuccess copyWith(
33+
{List<Headline>? headlines,
34+
bool? hasMore,
35+
String? cursor,
36+
String? errorMessage,
37+
String? lastSearchTerm,}) {
38+
return HeadlinesSearchSuccess(
39+
headlines: headlines ?? this.headlines,
40+
hasMore: hasMore ?? this.hasMore,
41+
cursor: cursor ?? this.cursor,
42+
errorMessage: errorMessage ?? this.errorMessage,
43+
lastSearchTerm: lastSearchTerm ?? this.lastSearchTerm,);
44+
}
4545

4646
@override
47-
List<Object> get props => [message];
47+
List<Object?> get props =>
48+
[headlines, hasMore, cursor, errorMessage, lastSearchTerm];
4849
}

0 commit comments

Comments
 (0)