Skip to content

Commit 45943f3

Browse files
committed
feat(search): add multi-model search support
- Implemented model type selection - Added category, source, country search - Refactored search logic - Updated state management
1 parent 6a5bd19 commit 45943f3

File tree

1 file changed

+119
-44
lines changed

1 file changed

+119
-44
lines changed

lib/headlines-search/bloc/headlines_search_bloc.dart

Lines changed: 119 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,108 +2,183 @@ import 'package:bloc/bloc.dart';
22
import 'package:bloc_concurrency/bloc_concurrency.dart';
33
import 'package:equatable/equatable.dart';
44
import 'package:ht_data_repository/ht_data_repository.dart'; // Generic Data Repository
5+
import 'package:ht_main/headlines-search/models/search_model_type.dart'; // Import SearchModelType
56
import 'package:ht_shared/ht_shared.dart'; // Shared models, including Headline
67

78
part 'headlines_search_event.dart';
89
part 'headlines_search_state.dart';
910

1011
class HeadlinesSearchBloc
1112
extends Bloc<HeadlinesSearchEvent, HeadlinesSearchState> {
12-
HeadlinesSearchBloc({required HtDataRepository<Headline> headlinesRepository})
13-
: _headlinesRepository = headlinesRepository,
14-
super(const HeadlinesSearchInitial()) {
15-
// Start with Initial state
13+
HeadlinesSearchBloc({
14+
required HtDataRepository<Headline> headlinesRepository,
15+
required HtDataRepository<Category> categoryRepository,
16+
required HtDataRepository<Source> sourceRepository,
17+
required HtDataRepository<Country> countryRepository,
18+
}) : _headlinesRepository = headlinesRepository,
19+
_categoryRepository = categoryRepository,
20+
_sourceRepository = sourceRepository,
21+
_countryRepository = countryRepository,
22+
super(const HeadlinesSearchInitial()) {
23+
on<HeadlinesSearchModelTypeChanged>(_onHeadlinesSearchModelTypeChanged);
1624
on<HeadlinesSearchFetchRequested>(
1725
_onSearchFetchRequested,
1826
transformer: restartable(), // Process only the latest search
1927
);
2028
}
2129

2230
final HtDataRepository<Headline> _headlinesRepository;
31+
final HtDataRepository<Category> _categoryRepository;
32+
final HtDataRepository<Source> _sourceRepository;
33+
final HtDataRepository<Country> _countryRepository;
2334
static const _limit = 10;
2435

36+
Future<void> _onHeadlinesSearchModelTypeChanged(
37+
HeadlinesSearchModelTypeChanged event,
38+
Emitter<HeadlinesSearchState> emit,
39+
) async {
40+
// If there's an active search term, re-trigger search with new model type
41+
final currentSearchTerm = state is HeadlinesSearchLoading
42+
? (state as HeadlinesSearchLoading).lastSearchTerm
43+
: state is HeadlinesSearchSuccess
44+
? (state as HeadlinesSearchSuccess).lastSearchTerm
45+
: state is HeadlinesSearchFailure
46+
? (state as HeadlinesSearchFailure).lastSearchTerm
47+
: null;
48+
49+
emit(HeadlinesSearchInitial(selectedModelType: event.newModelType));
50+
51+
if (currentSearchTerm != null && currentSearchTerm.isNotEmpty) {
52+
add(HeadlinesSearchFetchRequested(searchTerm: currentSearchTerm));
53+
}
54+
}
55+
2556
Future<void> _onSearchFetchRequested(
2657
HeadlinesSearchFetchRequested event,
2758
Emitter<HeadlinesSearchState> emit,
2859
) async {
29-
if (event.searchTerm.isEmpty) {
60+
final searchTerm = event.searchTerm;
61+
final modelType = state.selectedModelType;
62+
63+
if (searchTerm.isEmpty) {
3064
emit(
31-
const HeadlinesSearchSuccess(
32-
headlines: [],
65+
HeadlinesSearchSuccess(
66+
results: const [],
3367
hasMore: false,
3468
lastSearchTerm: '',
69+
selectedModelType: modelType,
3570
),
3671
);
3772
return;
3873
}
3974

40-
// Check if current state is success and if the search term is the same for pagination
75+
// Handle pagination
4176
if (state is HeadlinesSearchSuccess) {
4277
final successState = state as HeadlinesSearchSuccess;
43-
if (event.searchTerm == successState.lastSearchTerm) {
44-
// This is a pagination request for the current search term
45-
if (!successState.hasMore) return; // No more items to paginate
78+
if (searchTerm == successState.lastSearchTerm &&
79+
modelType == successState.selectedModelType) {
80+
if (!successState.hasMore) return;
4681

47-
// It's a bit unusual to emit Loading here for pagination,
48-
// typically UI handles this. Let's keep it simple for now.
49-
// emit(HeadlinesSearchLoading(lastSearchTerm: event.searchTerm));
5082
try {
51-
final response = await _headlinesRepository.readAllByQuery(
52-
{'q': event.searchTerm},
53-
limit: _limit,
54-
startAfterId: successState.cursor,
55-
);
83+
PaginatedResponse<dynamic> response;
84+
switch (modelType) {
85+
case SearchModelType.headline:
86+
response = await _headlinesRepository.readAllByQuery(
87+
{'q': searchTerm, 'model': modelType.toJson()},
88+
limit: _limit,
89+
startAfterId: successState.cursor,
90+
);
91+
case SearchModelType.category:
92+
response = await _categoryRepository.readAllByQuery(
93+
{'q': searchTerm, 'model': modelType.toJson()},
94+
limit: _limit,
95+
startAfterId: successState.cursor,
96+
);
97+
case SearchModelType.source:
98+
response = await _sourceRepository.readAllByQuery(
99+
{'q': searchTerm, 'model': modelType.toJson()},
100+
limit: _limit,
101+
startAfterId: successState.cursor,
102+
);
103+
case SearchModelType.country:
104+
response = await _countryRepository.readAllByQuery(
105+
{'q': searchTerm, 'model': modelType.toJson()},
106+
limit: _limit,
107+
startAfterId: successState.cursor,
108+
);
109+
}
56110
emit(
57-
response.items.isEmpty
58-
? successState.copyWith(hasMore: false)
59-
: successState.copyWith(
60-
headlines: List.of(successState.headlines)
61-
..addAll(response.items),
62-
hasMore: response.hasMore,
63-
cursor: response.cursor,
64-
),
111+
successState.copyWith(
112+
results: List.of(successState.results)..addAll(response.items),
113+
hasMore: response.hasMore,
114+
cursor: response.cursor,
115+
),
65116
);
66117
} on HtHttpException catch (e) {
67118
emit(successState.copyWith(errorMessage: e.message));
68119
} catch (e, st) {
69-
print('Search pagination error: $e\n$st');
70-
emit(
71-
successState.copyWith(errorMessage: 'Failed to load more results.'),
72-
);
120+
print('Search pagination error ($modelType): $e\n$st');
121+
emit(successState.copyWith(
122+
errorMessage: 'Failed to load more results.',
123+
));
73124
}
74-
return; // Pagination handled
125+
return;
75126
}
76127
}
77128

78-
// If not paginating for the same term, it's a new search or different term
79-
emit(
80-
HeadlinesSearchLoading(lastSearchTerm: event.searchTerm),
81-
); // Show loading for new search
129+
// New search
130+
emit(HeadlinesSearchLoading(
131+
lastSearchTerm: searchTerm,
132+
selectedModelType: modelType,
133+
));
82134
try {
83-
final response = await _headlinesRepository.readAllByQuery({
84-
'q': event.searchTerm,
85-
}, limit: _limit);
135+
PaginatedResponse<dynamic> response;
136+
switch (modelType) {
137+
case SearchModelType.headline:
138+
response = await _headlinesRepository.readAllByQuery(
139+
{'q': searchTerm, 'model': modelType.toJson()},
140+
limit: _limit,
141+
);
142+
case SearchModelType.category:
143+
response = await _categoryRepository.readAllByQuery(
144+
{'q': searchTerm, 'model': modelType.toJson()},
145+
limit: _limit,
146+
);
147+
case SearchModelType.source:
148+
response = await _sourceRepository.readAllByQuery(
149+
{'q': searchTerm, 'model': modelType.toJson()},
150+
limit: _limit,
151+
);
152+
case SearchModelType.country:
153+
response = await _countryRepository.readAllByQuery(
154+
{'q': searchTerm, 'model': modelType.toJson()},
155+
limit: _limit,
156+
);
157+
}
86158
emit(
87159
HeadlinesSearchSuccess(
88-
headlines: response.items,
160+
results: response.items,
89161
hasMore: response.hasMore,
90162
cursor: response.cursor,
91-
lastSearchTerm: event.searchTerm,
163+
lastSearchTerm: searchTerm,
164+
selectedModelType: modelType,
92165
),
93166
);
94167
} on HtHttpException catch (e) {
95168
emit(
96169
HeadlinesSearchFailure(
97170
errorMessage: e.message,
98-
lastSearchTerm: event.searchTerm,
171+
lastSearchTerm: searchTerm,
172+
selectedModelType: modelType,
99173
),
100174
);
101175
} catch (e, st) {
102-
print('Search error: $e\n$st');
176+
print('Search error ($modelType): $e\n$st');
103177
emit(
104178
HeadlinesSearchFailure(
105179
errorMessage: 'An unexpected error occurred during search.',
106-
lastSearchTerm: event.searchTerm,
180+
lastSearchTerm: searchTerm,
181+
selectedModelType: modelType,
107182
),
108183
);
109184
}

0 commit comments

Comments
 (0)