@@ -155,10 +155,10 @@ class _FeedDecoratorLoaderWidgetState extends State<FeedDecoratorLoaderWidget> {
155155 );
156156 } else if (decoratorItem is ContentCollectionItem ) {
157157 _decoratorWidget = ContentCollectionDecoratorWidget (
158- item: decoratorItem,
159- // The follow/unfollow logic is handled by the AppBloc listener
160- // in the ContentCollectionDecoratorWidget itself .
161- onFollowToggle: (_) {} ,
158+ item: decoratorItem, // The content collection item to display.
159+ // The onFollowToggle callback is handled by this widget, which
160+ // then dispatches an event to the AppBloc to update user preferences .
161+ onFollowToggle: _onFollowToggle ,
162162 onDismiss: () => _onDismissed (dueDecoratorType),
163163 );
164164 }
@@ -167,6 +167,91 @@ class _FeedDecoratorLoaderWidgetState extends State<FeedDecoratorLoaderWidget> {
167167 }
168168 }
169169
170+ /// Handles the toggling of follow/unfollow status for a [FeedItem]
171+ /// (either [Topic] or [Source] ) within a content collection decorator.
172+ ///
173+ /// This method updates the [UserContentPreferences] in the [AppBloc]
174+ /// by creating a new preferences object with the modified followed list
175+ /// and dispatching an [AppUserContentPreferencesChanged] event.
176+ ///
177+ /// The logic here ensures that the UI layer (this widget) constructs the
178+ /// desired new state, and the [AppBloc] is responsible for persisting it,
179+ /// maintaining consistency with the `AppSettingsChanged` pattern.
180+ void _onFollowToggle (FeedItem item) {
181+ _logger.fine (
182+ '[FeedDecoratorLoaderWidget] _onFollowToggle called for item: '
183+ '${item .id } (Type: ${item .runtimeType })' ,
184+ );
185+
186+ final appBlocState = context.read <AppBloc >().state;
187+ final userContentPreferences = appBlocState.userContentPreferences;
188+
189+ // Guard against null preferences. This should ideally not happen if
190+ // initialization is complete, but it's a safeguard.
191+ if (userContentPreferences == null ) {
192+ _logger.warning (
193+ '[FeedDecoratorLoaderWidget] Cannot toggle follow status: '
194+ 'UserContentPreferences are null.' ,
195+ );
196+ return ;
197+ }
198+
199+ UserContentPreferences updatedPreferences = userContentPreferences;
200+
201+ if (item is Topic ) {
202+ // Create a mutable copy of the followed topics list.
203+ final currentFollowedTopics = List <Topic >.from (
204+ userContentPreferences.followedTopics,
205+ );
206+
207+ if (currentFollowedTopics.any ((t) => t.id == item.id)) {
208+ // If already following, unfollow.
209+ currentFollowedTopics.removeWhere ((t) => t.id == item.id);
210+ _logger.info (
211+ '[FeedDecoratorLoaderWidget] Unfollowed topic: ${item .id }' ,
212+ );
213+ } else {
214+ // If not following, follow.
215+ currentFollowedTopics.add (item);
216+ _logger.info ('[FeedDecoratorLoaderWidget] Followed topic: ${item .id }' );
217+ }
218+ // Create a new UserContentPreferences object with the updated topics.
219+ updatedPreferences = userContentPreferences.copyWith (
220+ followedTopics: currentFollowedTopics,
221+ );
222+ } else if (item is Source ) {
223+ // Create a mutable copy of the followed sources list.
224+ final currentFollowedSources = List <Source >.from (
225+ userContentPreferences.followedSources,
226+ );
227+
228+ if (currentFollowedSources.any ((s) => s.id == item.id)) {
229+ // If already following, unfollow.
230+ currentFollowedSources.removeWhere ((s) => s.id == item.id);
231+ _logger.info (
232+ '[FeedDecoratorLoaderWidget] Unfollowed source: ${item .id }' ,
233+ );
234+ } else {
235+ // If not following, follow.
236+ currentFollowedSources.add (item);
237+ _logger.info ('[FeedDecoratorLoaderWidget] Followed source: ${item .id }' );
238+ }
239+ // Create a new UserContentPreferences object with the updated sources.
240+ updatedPreferences = userContentPreferences.copyWith (
241+ followedSources: currentFollowedSources,
242+ );
243+ } else {
244+ _logger.warning (
245+ '[FeedDecoratorLoaderWidget] Unsupported FeedItem type for follow toggle: ${item .runtimeType }' ,
246+ );
247+ return ;
248+ }
249+ // Dispatch the event to the AppBloc to persist the changes.
250+ context.read <AppBloc >().add (
251+ AppUserContentPreferencesChanged (preferences: updatedPreferences),
252+ );
253+ }
254+
170255 void _onDismissed (FeedDecoratorType decoratorType) {
171256 _logger.info ('Decorator $decoratorType dismissed by user.' );
172257 final userId = context.read <AppBloc >().state.user? .id;
0 commit comments