Skip to content

Commit e2ce94d

Browse files
committed
refactor(headlines): replace save icon with 'Apply or Save' dialog
This commit refactors the filter application workflow on the `HeadlinesFilterPage`. The separate "Save" and "Apply" icon buttons in the `AppBar` have been removed. The "Apply" button now triggers a dialog that prompts the user to either "Apply Only" for a one-time custom filter or "Apply & Save" to name and persist the filter for future use. This change clarifies user intent, prevents accidental application of unsaved filters, and streamlines the UI by consolidating actions into a single, clear workflow.
1 parent 7d32bc8 commit e2ce94d

File tree

1 file changed

+85
-53
lines changed

1 file changed

+85
-53
lines changed

lib/headlines-feed/view/headlines_filter_page.dart

Lines changed: 85 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,90 @@ class _HeadlinesFilterViewState extends State<_HeadlinesFilterView> {
101101
);
102102
}
103103

104+
/// Shows the dialog to let the user choose between applying the filter
105+
/// for one-time use or saving it for future use.
106+
Future<void> _showApplyOptionsDialog() async {
107+
final l10n = AppLocalizationsX(context).l10n;
108+
return showDialog<void>(
109+
context: context,
110+
builder: (BuildContext dialogContext) {
111+
return AlertDialog(
112+
title: Text(l10n.applyFilterDialogTitle),
113+
content: Text(l10n.applyFilterDialogContent),
114+
actions: <Widget>[
115+
TextButton(
116+
child: Text(l10n.applyFilterDialogApplyOnlyButton),
117+
onPressed: () {
118+
// Pop the dialog first.
119+
Navigator.of(dialogContext).pop();
120+
// Apply the filter as a "custom" one-time filter.
121+
_applyAndExit(null);
122+
},
123+
),
124+
FilledButton(
125+
child: Text(l10n.applyFilterDialogApplyAndSaveButton),
126+
onPressed: () {
127+
// Pop the dialog first.
128+
Navigator.of(dialogContext).pop();
129+
// Initiate the save and apply flow.
130+
_saveAndApplyFilter();
131+
},
132+
),
133+
],
134+
);
135+
},
136+
);
137+
}
138+
139+
/// Initiates the process of saving a filter by showing the naming dialog,
140+
/// and then applies it.
141+
void _saveAndApplyFilter() {
142+
final filterState = context.read<HeadlinesFilterBloc>().state;
143+
showDialog<void>(
144+
context: context,
145+
builder: (_) {
146+
return SaveFilterDialog(
147+
onSave: (name) {
148+
final newFilter = SavedFilter(
149+
id: const Uuid().v4(),
150+
name: name,
151+
topics: filterState.selectedTopics.toList(),
152+
sources: filterState.selectedSources.toList(),
153+
countries: filterState.selectedCountries.toList(),
154+
);
155+
// Add the filter to the global state.
156+
context.read<AppBloc>().add(SavedFilterAdded(filter: newFilter));
157+
// Apply the newly saved filter and exit the page.
158+
_applyAndExit(newFilter);
159+
},
160+
);
161+
},
162+
);
163+
}
164+
165+
/// Applies the current filter selections to the feed and pops the page.
166+
///
167+
/// If a [savedFilter] is provided, it's passed to the event to ensure
168+
/// its chip is correctly selected on the feed. Otherwise, the filter is
169+
/// treated as a "custom" one.
170+
void _applyAndExit(SavedFilter? savedFilter) {
171+
final filterState = context.read<HeadlinesFilterBloc>().state;
172+
final newFilter = HeadlineFilter(
173+
topics: filterState.selectedTopics.toList(),
174+
sources: filterState.selectedSources.toList(),
175+
eventCountries: filterState.selectedCountries.toList(),
176+
isFromFollowedItems: filterState.isUsingFollowedItems,
177+
);
178+
context.read<HeadlinesFeedBloc>().add(
179+
HeadlinesFeedFiltersApplied(
180+
filter: newFilter,
181+
savedFilter: savedFilter,
182+
adThemeStyle: AdThemeStyle.fromTheme(Theme.of(context)),
183+
),
184+
);
185+
context.pop();
186+
}
187+
104188
@override
105189
Widget build(BuildContext context) {
106190
final l10n = AppLocalizationsX(context).l10n;
@@ -134,63 +218,11 @@ class _HeadlinesFilterViewState extends State<_HeadlinesFilterView> {
134218
icon: const Icon(Icons.edit_note_outlined),
135219
onPressed: () => context.pushNamed(Routes.manageSavedFiltersName),
136220
),
137-
// Save Filter Button
138-
IconButton(
139-
tooltip: l10n.headlinesFilterSaveTooltip,
140-
icon: const Icon(Icons.save_outlined),
141-
onPressed: () {
142-
showDialog<void>(
143-
context: context,
144-
builder: (_) {
145-
final filterState = context.read<HeadlinesFilterBloc>().state;
146-
return SaveFilterDialog(
147-
onSave: (name) {
148-
final newFilter = SavedFilter(
149-
id: const Uuid().v4(),
150-
name: name,
151-
topics: filterState.selectedTopics.toList(),
152-
sources: filterState.selectedSources.toList(),
153-
countries: filterState.selectedCountries.toList(),
154-
);
155-
// Keep track of the newly saved filter.
156-
setState(() => _newlySavedFilter = newFilter);
157-
context.read<AppBloc>().add(
158-
SavedFilterAdded(filter: newFilter),
159-
);
160-
},
161-
);
162-
},
163-
);
164-
},
165-
),
166221
// Apply Filters Button
167222
IconButton(
168223
icon: const Icon(Icons.check),
169224
tooltip: l10n.headlinesFeedFilterApplyButton,
170-
onPressed: () {
171-
final filterState = context.read<HeadlinesFilterBloc>().state;
172-
final newFilter = HeadlineFilter(
173-
topics: filterState.selectedTopics.isNotEmpty
174-
? filterState.selectedTopics.toList()
175-
: null,
176-
sources: filterState.selectedSources.isNotEmpty
177-
? filterState.selectedSources.toList()
178-
: null,
179-
eventCountries: filterState.selectedCountries.isNotEmpty
180-
? filterState.selectedCountries.toList()
181-
: null,
182-
isFromFollowedItems: filterState.isUsingFollowedItems,
183-
);
184-
context.read<HeadlinesFeedBloc>().add(
185-
HeadlinesFeedFiltersApplied(
186-
filter: newFilter,
187-
// Pass the newly saved filter if it exists.
188-
savedFilter: _newlySavedFilter,
189-
adThemeStyle: AdThemeStyle.fromTheme(Theme.of(context)),
190-
),
191-
);
192-
context.pop();
193-
},
225+
onPressed: _showApplyOptionsDialog,
194226
),
195227
],
196228
),

0 commit comments

Comments
 (0)