Skip to content

Commit d5036dd

Browse files
committed
refactor(sources): Filter sources locally
- Remove _FetchFilteredSourcesRequested - Filter in bloc using helper method - Use initial capsule selections
1 parent 12ec7a7 commit d5036dd

File tree

1 file changed

+82
-80
lines changed

1 file changed

+82
-80
lines changed

lib/headlines-feed/bloc/sources_filter_bloc.dart

Lines changed: 82 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ class SourcesFilterBloc extends Bloc<SourcesFilterEvent, SourcesFilterState> {
1818
super(const SourcesFilterState()) {
1919
on<LoadSourceFilterData>(_onLoadSourceFilterData);
2020
on<CountryCapsuleToggled>(_onCountryCapsuleToggled);
21-
on<AllSourceTypesCapsuleToggled>(_onAllSourceTypesCapsuleToggled); // Added
21+
on<AllSourceTypesCapsuleToggled>(_onAllSourceTypesCapsuleToggled);
2222
on<SourceTypeCapsuleToggled>(_onSourceTypeCapsuleToggled);
2323
on<SourceCheckboxToggled>(_onSourceCheckboxToggled);
2424
on<ClearSourceFiltersRequested>(_onClearSourceFiltersRequested);
25-
on<_FetchFilteredSourcesRequested>(_onFetchFilteredSourcesRequested);
25+
// Removed _FetchFilteredSourcesRequested event listener
2626
}
2727

2828
final HtDataRepository<Source> _sourcesRepository;
@@ -40,32 +40,35 @@ class SourcesFilterBloc extends Bloc<SourcesFilterEvent, SourcesFilterState> {
4040
final initialSelectedSourceIds =
4141
event.initialSelectedSources.map((s) => s.id).toSet();
4242

43-
// Initialize selected capsules based on initialSelectedSources
44-
final initialSelectedCountryIsoCodes = <String>{};
45-
final initialSelectedSourceTypes = <SourceType>{};
46-
47-
if (event.initialSelectedSources.isNotEmpty) {
48-
for (final source in event.initialSelectedSources) {
49-
if (source.headquarters?.isoCode != null) {
50-
initialSelectedCountryIsoCodes.add(source.headquarters!.isoCode);
51-
}
52-
if (source.sourceType != null) {
53-
initialSelectedSourceTypes.add(source.sourceType!);
54-
}
55-
}
56-
}
43+
// Use the passed-in initial capsule selections directly
44+
final initialSelectedCountryIsoCodes =
45+
event.initialSelectedCountryIsoCodes;
46+
final initialSelectedSourceTypes = event.initialSelectedSourceTypes;
47+
48+
final allSourcesResponse = await _sourcesRepository.readAll();
49+
final allAvailableSources = allSourcesResponse.items;
50+
51+
// Initially, display all sources. Capsules are visually set but don't filter the list yet.
52+
// Filtering will occur if a capsule is manually toggled.
53+
// However, if initial capsule filters ARE provided, we should respect them for the initial display.
54+
final displayableSources = _getFilteredSources(
55+
allSources: allAvailableSources,
56+
selectedCountries: initialSelectedCountryIsoCodes, // Use event's data
57+
selectedTypes: initialSelectedSourceTypes, // Use event's data
58+
);
5759

5860
emit(
5961
state.copyWith(
6062
availableCountries: availableCountries.items,
63+
allAvailableSources: allAvailableSources,
64+
displayableSources: displayableSources, // Now correctly filtered if initial capsules were set
6165
finallySelectedSourceIds: initialSelectedSourceIds,
62-
selectedCountryIsoCodes: initialSelectedCountryIsoCodes,
63-
selectedSourceTypes: initialSelectedSourceTypes,
64-
// Keep loading status until sources are fetched
66+
selectedCountryIsoCodes: initialSelectedCountryIsoCodes, // Use event's data
67+
selectedSourceTypes: initialSelectedSourceTypes, // Use event's data
68+
dataLoadingStatus: SourceFilterDataLoadingStatus.success,
69+
clearErrorMessage: true,
6570
),
6671
);
67-
// Trigger initial fetch of displayable sources
68-
add(const _FetchFilteredSourcesRequested());
6972
} catch (e) {
7073
emit(
7174
state.copyWith(
@@ -95,34 +98,57 @@ class SourcesFilterBloc extends Bloc<SourcesFilterEvent, SourcesFilterState> {
9598
currentSelected.add(event.countryIsoCode);
9699
}
97100
}
98-
emit(state.copyWith(selectedCountryIsoCodes: currentSelected));
99-
add(const _FetchFilteredSourcesRequested());
101+
final newDisplayableSources = _getFilteredSources(
102+
allSources: state.allAvailableSources,
103+
selectedCountries: currentSelected,
104+
selectedTypes: state.selectedSourceTypes,
105+
);
106+
emit(
107+
state.copyWith(
108+
selectedCountryIsoCodes: currentSelected,
109+
displayableSources: newDisplayableSources,
110+
),
111+
);
100112
}
101113

102-
Future<void> _onAllSourceTypesCapsuleToggled(
114+
void _onAllSourceTypesCapsuleToggled(
103115
AllSourceTypesCapsuleToggled event,
104116
Emitter<SourcesFilterState> emit,
105-
) async {
106-
// Toggling "All" for source types means clearing any specific selections.
107-
// If already clear, it remains clear.
108-
emit(state.copyWith(selectedSourceTypes: {}));
109-
add(const _FetchFilteredSourcesRequested());
117+
) {
118+
final newDisplayableSources = _getFilteredSources(
119+
allSources: state.allAvailableSources,
120+
selectedCountries: state.selectedCountryIsoCodes,
121+
selectedTypes: {}, // Cleared source types
122+
);
123+
emit(
124+
state.copyWith(
125+
selectedSourceTypes: {},
126+
displayableSources: newDisplayableSources,
127+
),
128+
);
110129
}
111130

112-
Future<void> _onSourceTypeCapsuleToggled(
131+
void _onSourceTypeCapsuleToggled(
113132
SourceTypeCapsuleToggled event,
114133
Emitter<SourcesFilterState> emit,
115-
) async {
134+
) {
116135
final currentSelected = Set<SourceType>.from(state.selectedSourceTypes);
117136
if (currentSelected.contains(event.sourceType)) {
118137
currentSelected.remove(event.sourceType);
119138
} else {
120139
currentSelected.add(event.sourceType);
121140
}
122-
// If specific types are selected, "All" is no longer true.
123-
// The UI will derive "All" state from selectedSourceTypes.isEmpty
124-
emit(state.copyWith(selectedSourceTypes: currentSelected));
125-
add(const _FetchFilteredSourcesRequested());
141+
final newDisplayableSources = _getFilteredSources(
142+
allSources: state.allAvailableSources,
143+
selectedCountries: state.selectedCountryIsoCodes,
144+
selectedTypes: currentSelected,
145+
);
146+
emit(
147+
state.copyWith(
148+
selectedSourceTypes: currentSelected,
149+
displayableSources: newDisplayableSources,
150+
),
151+
);
126152
}
127153

128154
void _onSourceCheckboxToggled(
@@ -147,55 +173,31 @@ class SourcesFilterBloc extends Bloc<SourcesFilterEvent, SourcesFilterState> {
147173
selectedCountryIsoCodes: {},
148174
selectedSourceTypes: {},
149175
finallySelectedSourceIds: {},
150-
// Keep availableCountries and availableSourceTypes
151-
),
152-
);
153-
add(const _FetchFilteredSourcesRequested());
154-
}
155-
156-
Future<void> _onFetchFilteredSourcesRequested(
157-
_FetchFilteredSourcesRequested event,
158-
Emitter<SourcesFilterState> emit,
159-
) async {
160-
emit(
161-
state.copyWith(
162-
dataLoadingStatus: SourceFilterDataLoadingStatus.loading,
163-
displayableSources: [], // Clear previous sources
176+
displayableSources: List.from(state.allAvailableSources), // Reset
177+
dataLoadingStatus: SourceFilterDataLoadingStatus.success,
164178
clearErrorMessage: true,
165179
),
166180
);
167-
try {
168-
final queryParameters = <String, dynamic>{};
169-
if (state.selectedCountryIsoCodes.isNotEmpty) {
170-
queryParameters['countries'] = state.selectedCountryIsoCodes.join(',');
171-
}
172-
if (state.selectedSourceTypes.isNotEmpty) {
173-
queryParameters['sourceTypes'] = state.selectedSourceTypes
174-
.map((st) => st.name)
175-
.join(',');
176-
}
181+
}
177182

178-
final response = await _sourcesRepository.readAllByQuery(queryParameters);
179-
emit(
180-
state.copyWith(
181-
displayableSources: response.items,
182-
dataLoadingStatus: SourceFilterDataLoadingStatus.success,
183-
),
184-
);
185-
} on HtHttpException catch (e) {
186-
emit(
187-
state.copyWith(
188-
dataLoadingStatus: SourceFilterDataLoadingStatus.failure,
189-
errorMessage: e.message,
190-
),
191-
);
192-
} catch (e) {
193-
emit(
194-
state.copyWith(
195-
dataLoadingStatus: SourceFilterDataLoadingStatus.failure,
196-
errorMessage: 'An unexpected error occurred while fetching sources.',
197-
),
198-
);
183+
// Helper method to filter sources based on selected countries and types
184+
List<Source> _getFilteredSources({
185+
required List<Source> allSources,
186+
required Set<String> selectedCountries,
187+
required Set<SourceType> selectedTypes,
188+
}) {
189+
if (selectedCountries.isEmpty && selectedTypes.isEmpty) {
190+
return List.from(allSources); // Return all if no filters
199191
}
192+
193+
return allSources.where((source) {
194+
final matchesCountry = selectedCountries.isEmpty ||
195+
(source.headquarters != null &&
196+
selectedCountries.contains(source.headquarters!.isoCode));
197+
final matchesType = selectedTypes.isEmpty ||
198+
(source.sourceType != null &&
199+
selectedTypes.contains(source.sourceType!));
200+
return matchesCountry && matchesType;
201+
}).toList();
200202
}
201203
}

0 commit comments

Comments
 (0)