Skip to content

Commit f9061d8

Browse files
committed
feat(feed_decorators): add dismiss option to content collection
Updates `ContentCollectionDecoratorWidget` to support dismissal. - Adds a new `onDismiss` callback to the constructor. - Introduces a `PopupMenuButton` in the header, allowing users to dismiss the decorator. - Refactors the widget to read followed status directly from `AppBloc` and dispatch follow/unfollow events, simplifying its API and making it more self-contained.
1 parent d99c429 commit f9061d8

File tree

1 file changed

+60
-16
lines changed

1 file changed

+60
-16
lines changed

lib/shared/widgets/feed_decorators/content_collection_decorator_widget.dart

Lines changed: 60 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ import 'package:ui_kit/ui_kit.dart';
99
/// (e.g., Topics or Sources).
1010
///
1111
/// This widget presents a title and a horizontal list of [SuggestionItemWidget]s.
12+
/// It now includes a dismiss option in a popup menu.
1213
/// {@endtemplate}
1314
class ContentCollectionDecoratorWidget extends StatelessWidget {
1415
/// {@macro content_collection_decorator_widget}
1516
const ContentCollectionDecoratorWidget({
1617
required this.item,
1718
required this.onFollowToggle,
18-
required this.followedTopicIds,
19-
required this.followedSourceIds,
19+
this.onDismiss,
2020
super.key,
2121
});
2222

@@ -27,19 +27,16 @@ class ContentCollectionDecoratorWidget extends StatelessWidget {
2727
/// is pressed.
2828
final ValueSetter<FeedItem> onFollowToggle;
2929

30-
/// List of IDs of topics the user is currently following.
31-
final List<String> followedTopicIds;
32-
33-
/// List of IDs of sources the user is currently following.
34-
final List<String> followedSourceIds;
30+
/// An optional callback that is triggered when the user dismisses the
31+
/// decorator.
32+
final VoidCallback? onDismiss;
3533

3634
@override
3735
Widget build(BuildContext context) {
3836
return _ContentCollectionView(
3937
item: item,
4038
onFollowToggle: onFollowToggle,
41-
followedTopicIds: followedTopicIds,
42-
followedSourceIds: followedSourceIds,
39+
onDismiss: onDismiss,
4340
);
4441
}
4542
}
@@ -48,14 +45,12 @@ class _ContentCollectionView extends StatefulWidget {
4845
const _ContentCollectionView({
4946
required this.item,
5047
required this.onFollowToggle,
51-
required this.followedTopicIds,
52-
required this.followedSourceIds,
48+
this.onDismiss,
5349
});
5450

5551
final ContentCollectionItem item;
5652
final ValueSetter<FeedItem> onFollowToggle;
57-
final List<String> followedTopicIds;
58-
final List<String> followedSourceIds;
53+
final VoidCallback? onDismiss;
5954

6055
@override
6156
State<_ContentCollectionView> createState() => _ContentCollectionViewState();
@@ -115,6 +110,19 @@ class _ContentCollectionViewState extends State<_ContentCollectionView> {
115110
overflow: TextOverflow.ellipsis,
116111
),
117112
),
113+
if (widget.onDismiss != null)
114+
PopupMenuButton<void>(
115+
icon: const Icon(Icons.more_vert),
116+
tooltip: l10n.manageFiltersDeleteTooltip,
117+
onSelected: (_) => widget.onDismiss!(),
118+
itemBuilder: (BuildContext context) => [
119+
PopupMenuItem<void>(
120+
value: null,
121+
// TODO(fulleni): Replace with a localized string.
122+
child: Text(l10n.savedFiltersMenuDelete),
123+
),
124+
],
125+
),
118126
],
119127
),
120128
),
@@ -123,6 +131,20 @@ class _ContentCollectionViewState extends State<_ContentCollectionView> {
123131
height: 180,
124132
child: LayoutBuilder(
125133
builder: (context, constraints) {
134+
// Access AppBloc to get the user's content preferences,
135+
// which is the source of truth for followed items.
136+
final appState = context.watch<AppBloc>().state;
137+
final followedTopicIds =
138+
appState.userContentPreferences?.followedTopics
139+
.map((t) => t.id)
140+
.toList() ??
141+
[];
142+
final followedSourceIds =
143+
appState.userContentPreferences?.followedSources
144+
.map((s) => s.id)
145+
.toList() ??
146+
[];
147+
126148
final listView = ListView.builder(
127149
controller: _scrollController,
128150
scrollDirection: Axis.horizontal,
@@ -134,12 +156,34 @@ class _ContentCollectionViewState extends State<_ContentCollectionView> {
134156
final suggestion = widget.item.items[index];
135157
final isFollowing =
136158
(suggestion is Topic &&
137-
widget.followedTopicIds.contains(suggestion.id)) ||
159+
followedTopicIds.contains(suggestion.id)) ||
138160
(suggestion is Source &&
139-
widget.followedSourceIds.contains(suggestion.id));
161+
followedSourceIds.contains(suggestion.id));
140162
return SuggestionItemWidget(
141163
item: suggestion,
142-
onFollowToggle: widget.onFollowToggle,
164+
onFollowToggle: (toggledItem) {
165+
final currentUserPreferences =
166+
appState.userContentPreferences;
167+
if (currentUserPreferences == null) return;
168+
169+
UserContentPreferences updatedPreferences;
170+
171+
if (toggledItem is Topic) {
172+
updatedPreferences = currentUserPreferences
173+
.toggleFollowedTopic(toggledItem);
174+
} else if (toggledItem is Source) {
175+
updatedPreferences = currentUserPreferences
176+
.toggleFollowedSource(toggledItem);
177+
} else {
178+
return;
179+
}
180+
181+
context.read<AppBloc>().add(
182+
AppUserContentPreferencesChanged(
183+
preferences: updatedPreferences,
184+
),
185+
);
186+
},
143187
isFollowing: isFollowing,
144188
);
145189
},

0 commit comments

Comments
 (0)