Skip to content

Commit e70e790

Browse files
committed
fix: Implement follow/unfollow logic for suggested items in feed decorator
Introduces `_onFollowToggle` method in `FeedDecoratorLoaderWidget` to handle user interactions with suggested topics and sources. This method retrieves current user content preferences, updates the `followedTopics` or `followedSources` list based on the item's type and current status, and then dispatches an `AppUserContentPreferencesChanged` event to the `AppBloc` to persist these changes. The `onFollowToggle` callback in `ContentCollectionDecoratorWidget` is now wired to this new method.
1 parent 04f260e commit e70e790

File tree

1 file changed

+89
-4
lines changed

1 file changed

+89
-4
lines changed

lib/feed_decorators/widgets/feed_decorator_loader_widget.dart

Lines changed: 89 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)