Skip to content

Commit f571bac

Browse files
committed
refactor(headlines-feed): simplify followed filters logic
- Remove unused state variables related to followed filters - Replace manual followed filters fetching with BlocBuilder - Simplify UI logic for displaying followed filters - Remove loading and error handling for followed filters
1 parent 3089c69 commit f571bac

File tree

1 file changed

+52
-180
lines changed

1 file changed

+52
-180
lines changed

lib/headlines-feed/view/headlines_filter_page.dart

Lines changed: 52 additions & 180 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,8 @@ class _HeadlinesFilterPageState extends State<HeadlinesFilterPage> {
4242
late List<Source> _tempSelectedSources;
4343
late List<Country> _tempSelectedEventCountries;
4444

45-
// New state variables for the "Apply my followed items" feature
45+
/// Flag to indicate if the "Apply my followed items" filter is active.
4646
bool _useFollowedFilters = false;
47-
bool _isLoadingFollowedFilters = false;
48-
String? _loadFollowedFiltersError;
49-
UserContentPreferences? _currentUserPreferences;
5047

5148
@override
5249
void initState() {
@@ -61,125 +58,6 @@ class _HeadlinesFilterPageState extends State<HeadlinesFilterPage> {
6158
_tempSelectedEventCountries = List.from(currentFilter.eventCountries ?? []);
6259

6360
_useFollowedFilters = currentFilter.isFromFollowedItems;
64-
_isLoadingFollowedFilters = false;
65-
_loadFollowedFiltersError = null;
66-
_currentUserPreferences = null;
67-
68-
// If the "Apply my followed items" feature is initially active,
69-
// fetch the followed items to populate the temporary filter lists.
70-
if (_useFollowedFilters) {
71-
WidgetsBinding.instance.addPostFrameCallback((_) {
72-
if (mounted) {
73-
_fetchAndApplyFollowedFilters();
74-
}
75-
});
76-
}
77-
}
78-
79-
/// Fetches the user's followed items (topics, sources, countries) and
80-
/// applies them to the temporary filter state.
81-
///
82-
/// This method is called when the "Apply my followed items" toggle is
83-
/// activated. It handles loading states, errors, and updates the UI.
84-
Future<void> _fetchAndApplyFollowedFilters() async {
85-
setState(() {
86-
_isLoadingFollowedFilters = true;
87-
_loadFollowedFiltersError = null;
88-
});
89-
90-
final appState = context.read<AppBloc>().state;
91-
final currentUser = appState.user!;
92-
93-
try {
94-
final preferencesRepo = context
95-
.read<DataRepository<UserContentPreferences>>();
96-
final preferences = await preferencesRepo.read(
97-
id: currentUser.id,
98-
userId: currentUser.id,
99-
);
100-
101-
// Check if followed items are empty across all categories
102-
if (preferences.followedTopics.isEmpty &&
103-
preferences.followedSources.isEmpty &&
104-
preferences.followedCountries.isEmpty) {
105-
setState(() {
106-
_isLoadingFollowedFilters = false;
107-
_useFollowedFilters = false;
108-
_tempSelectedTopics = [];
109-
_tempSelectedSources = [];
110-
_tempSelectedEventCountries = [];
111-
});
112-
if (mounted) {
113-
ScaffoldMessenger.of(context)
114-
..hideCurrentSnackBar()
115-
..showSnackBar(
116-
SnackBar(
117-
content: Text(
118-
AppLocalizationsX(
119-
context,
120-
).l10n.noFollowedItemsForFilterSnackbar,
121-
),
122-
duration: const Duration(seconds: 3),
123-
),
124-
);
125-
}
126-
return;
127-
} else {
128-
setState(() {
129-
_currentUserPreferences = preferences;
130-
_tempSelectedTopics = List.from(preferences.followedTopics);
131-
_tempSelectedSources = List.from(preferences.followedSources);
132-
_tempSelectedEventCountries = List.from(
133-
preferences.followedCountries,
134-
);
135-
_isLoadingFollowedFilters = false;
136-
});
137-
}
138-
} on NotFoundException {
139-
// If user preferences are not found, treat as empty followed items.
140-
setState(() {
141-
_currentUserPreferences = UserContentPreferences(
142-
id: currentUser.id,
143-
followedTopics: const [],
144-
followedSources: const [],
145-
followedCountries: const [],
146-
savedHeadlines: const [],
147-
);
148-
_tempSelectedTopics = [];
149-
_tempSelectedSources = [];
150-
_tempSelectedEventCountries = [];
151-
_isLoadingFollowedFilters = false;
152-
_useFollowedFilters = false;
153-
});
154-
if (mounted) {
155-
ScaffoldMessenger.of(context)
156-
..hideCurrentSnackBar()
157-
..showSnackBar(
158-
SnackBar(
159-
content: Text(
160-
AppLocalizationsX(
161-
context,
162-
).l10n.noFollowedItemsForFilterSnackbar,
163-
),
164-
duration: const Duration(seconds: 3),
165-
),
166-
);
167-
}
168-
} on HttpException catch (e) {
169-
setState(() {
170-
_isLoadingFollowedFilters = false;
171-
_useFollowedFilters = false;
172-
_loadFollowedFiltersError = e.message;
173-
});
174-
} catch (e) {
175-
setState(() {
176-
_isLoadingFollowedFilters = false;
177-
_useFollowedFilters = false;
178-
_loadFollowedFiltersError = AppLocalizationsX(
179-
context,
180-
).l10n.unknownError;
181-
});
182-
}
18361
}
18462

18563
/// Clears all temporary filter selections.
@@ -243,10 +121,9 @@ class _HeadlinesFilterPageState extends State<HeadlinesFilterPage> {
243121
final l10n = AppLocalizationsX(context).l10n;
244122
final theme = Theme.of(context);
245123

246-
// Determine if the "Apply my followed items" feature is active and loading.
124+
// Determine if the "Apply my followed items" feature is active.
247125
// This will disable the individual filter tiles.
248-
final isFollowedFilterActiveOrLoading =
249-
_useFollowedFilters || _isLoadingFollowedFilters;
126+
final isFollowedFilterActive = _useFollowedFilters;
250127

251128
return Scaffold(
252129
appBar: AppBar(
@@ -270,34 +147,59 @@ class _HeadlinesFilterPageState extends State<HeadlinesFilterPage> {
270147
// Also reset local state for the "Apply my followed items"
271148
setState(() {
272149
_useFollowedFilters = false;
273-
_isLoadingFollowedFilters = false;
274-
_loadFollowedFiltersError = null;
275150
_clearTemporaryFilters();
276151
});
277152
context.pop();
278153
},
279154
),
280155
// Apply My Followed Items Button
281-
IconButton(
282-
icon: _useFollowedFilters
283-
? const Icon(Icons.favorite)
284-
: const Icon(Icons.favorite_border),
285-
color: _useFollowedFilters ? theme.colorScheme.primary : null,
286-
tooltip: l10n.headlinesFeedFilterApplyFollowedLabel,
287-
onPressed: _isLoadingFollowedFilters
288-
? null // Disable while loading
289-
: () {
290-
setState(() {
291-
_useFollowedFilters = !_useFollowedFilters;
292-
if (_useFollowedFilters) {
293-
_fetchAndApplyFollowedFilters();
294-
} else {
295-
_isLoadingFollowedFilters = false;
296-
_loadFollowedFiltersError = null;
297-
_clearTemporaryFilters();
156+
BlocBuilder<AppBloc, AppState>(
157+
builder: (context, appState) {
158+
final followedTopics =
159+
appState.userContentPreferences?.followedTopics ?? [];
160+
final followedSources =
161+
appState.userContentPreferences?.followedSources ?? [];
162+
final followedCountries =
163+
appState.userContentPreferences?.followedCountries ?? [];
164+
165+
final hasFollowedItems = followedTopics.isNotEmpty ||
166+
followedSources.isNotEmpty ||
167+
followedCountries.isNotEmpty;
168+
169+
return IconButton(
170+
icon: _useFollowedFilters
171+
? const Icon(Icons.favorite)
172+
: const Icon(Icons.favorite_border),
173+
color: _useFollowedFilters ? theme.colorScheme.primary : null,
174+
tooltip: l10n.headlinesFeedFilterApplyFollowedLabel,
175+
onPressed: hasFollowedItems
176+
? () {
177+
setState(() {
178+
_useFollowedFilters = !_useFollowedFilters;
179+
if (_useFollowedFilters) {
180+
_tempSelectedTopics = List.from(followedTopics);
181+
_tempSelectedSources = List.from(followedSources);
182+
_tempSelectedEventCountries =
183+
List.from(followedCountries);
184+
} else {
185+
_clearTemporaryFilters();
186+
}
187+
});
298188
}
299-
});
300-
},
189+
: () {
190+
ScaffoldMessenger.of(context)
191+
..hideCurrentSnackBar()
192+
..showSnackBar(
193+
SnackBar(
194+
content: Text(
195+
l10n.noFollowedItemsForFilterSnackbar,
196+
),
197+
duration: const Duration(seconds: 3),
198+
),
199+
);
200+
},
201+
);
202+
},
301203
),
302204
// Apply Filters Button
303205
IconButton(
@@ -330,41 +232,11 @@ class _HeadlinesFilterPageState extends State<HeadlinesFilterPage> {
330232
body: ListView(
331233
padding: const EdgeInsets.symmetric(vertical: AppSpacing.md),
332234
children: [
333-
if (_isLoadingFollowedFilters)
334-
Padding(
335-
padding: const EdgeInsets.symmetric(
336-
horizontal: AppSpacing.paddingLarge,
337-
vertical: AppSpacing.sm,
338-
),
339-
child: Row(
340-
mainAxisAlignment: MainAxisAlignment.center,
341-
children: [
342-
const SizedBox(
343-
width: 24,
344-
height: 24,
345-
child: CircularProgressIndicator(strokeWidth: 2),
346-
),
347-
const SizedBox(width: AppSpacing.md),
348-
Text(l10n.headlinesFeedLoadingHeadline),
349-
],
350-
),
351-
),
352-
if (_loadFollowedFiltersError != null)
353-
Padding(
354-
padding: const EdgeInsets.symmetric(
355-
horizontal: AppSpacing.paddingLarge,
356-
vertical: AppSpacing.sm,
357-
),
358-
child: Text(
359-
_loadFollowedFiltersError!,
360-
style: TextStyle(color: theme.colorScheme.error),
361-
),
362-
),
363235
const Divider(),
364236
_buildFilterTile<Topic>(
365237
context: context,
366238
title: l10n.headlinesFeedFilterTopicLabel,
367-
enabled: !isFollowedFilterActiveOrLoading,
239+
enabled: !isFollowedFilterActive,
368240
selectedCount: _tempSelectedTopics.length,
369241
routeName: Routes.feedFilterTopicsName,
370242
currentSelectionData: _tempSelectedTopics,
@@ -375,7 +247,7 @@ class _HeadlinesFilterPageState extends State<HeadlinesFilterPage> {
375247
_buildFilterTile<Source>(
376248
context: context,
377249
title: l10n.headlinesFeedFilterSourceLabel,
378-
enabled: !isFollowedFilterActiveOrLoading,
250+
enabled: !isFollowedFilterActive,
379251
selectedCount: _tempSelectedSources.length,
380252
routeName: Routes.feedFilterSourcesName,
381253
currentSelectionData: _tempSelectedSources,
@@ -386,7 +258,7 @@ class _HeadlinesFilterPageState extends State<HeadlinesFilterPage> {
386258
_buildFilterTile<Country>(
387259
context: context,
388260
title: l10n.headlinesFeedFilterEventCountryLabel,
389-
enabled: !isFollowedFilterActiveOrLoading,
261+
enabled: !isFollowedFilterActive,
390262
selectedCount: _tempSelectedEventCountries.length,
391263
routeName: Routes.feedFilterEventCountriesName,
392264
currentSelectionData: _tempSelectedEventCountries,

0 commit comments

Comments
 (0)