Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/stream_feeds/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
- Add `activityCount` field to `FeedData` model to track the number of activities in a feed.
- Add `ownFollowings` field to `FeedData` model to track feeds that the current user is following from this feed.
- Add batch follow and unfollow support.
- Add collections API methods: `readCollections`, `createCollections`, `updateCollections`, and `deleteCollections`.
- Add `CollectionData` model and `collections` field to `ActivityData` for enriched collection data.
- Add `collectionRefs` field to `FeedAddActivityRequest` to attach collections to activities.

## 0.5.0
- [BREAKING] Unified `ThreadedCommentData` into `CommentData` to handle both flat and threaded comments.
Expand Down
31 changes: 31 additions & 0 deletions packages/stream_feeds/lib/src/client/feeds_client_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import '../repository/activities_repository.dart';
import '../repository/app_repository.dart';
import '../repository/bookmarks_repository.dart';
import '../repository/capabilities_repository.dart';
import '../repository/collections_repository.dart';
import '../repository/comments_repository.dart';
import '../repository/devices_repository.dart';
import '../repository/feeds_repository.dart';
Expand Down Expand Up @@ -161,6 +162,7 @@ class StreamFeedsClientImpl implements StreamFeedsClient {
_activitiesRepository = ActivitiesRepository(feedsApi, attachmentUploader);
_appRepository = AppRepository(feedsApi);
_bookmarksRepository = BookmarksRepository(feedsApi);
_collectionsRepository = CollectionsRepository(feedsApi);
_commentsRepository = CommentsRepository(feedsApi, attachmentUploader);
_devicesRepository = DevicesRepository(feedsApi);
_feedsRepository = FeedsRepository(feedsApi);
Expand Down Expand Up @@ -198,6 +200,7 @@ class StreamFeedsClientImpl implements StreamFeedsClient {
late final ActivitiesRepository _activitiesRepository;
late final AppRepository _appRepository;
late final BookmarksRepository _bookmarksRepository;
late final CollectionsRepository _collectionsRepository;
late final CommentsRepository _commentsRepository;
late final DevicesRepository _devicesRepository;
late final FeedsRepository _feedsRepository;
Expand Down Expand Up @@ -541,6 +544,34 @@ class StreamFeedsClientImpl implements StreamFeedsClient {
return result;
}

@override
Future<Result<api.ReadCollectionsResponse>> readCollections({
required List<String> refs,
}) {
return _collectionsRepository.readCollections(refs: refs);
}

@override
Future<Result<api.CreateCollectionsResponse>> createCollections({
required api.CreateCollectionsRequest request,
}) {
return _collectionsRepository.createCollections(request: request);
}

@override
Future<Result<api.UpdateCollectionsResponse>> updateCollections({
required api.UpdateCollectionsRequest request,
}) {
return _collectionsRepository.updateCollections(request: request);
}

@override
Future<Result<api.DeleteCollectionsResponse>> deleteCollections({
required List<String> refs,
}) {
return _collectionsRepository.deleteCollections(refs: refs);
}

Stream<void> get onReconnectEmitter {
return connectionState.scan(
(state, connectionStatus, i) => switch (connectionStatus) {
Expand Down
124 changes: 117 additions & 7 deletions packages/stream_feeds/lib/src/feeds_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -658,7 +658,7 @@ abstract interface class StreamFeedsClient {
/// switch (result) {
/// case Success(value: final appData):
/// print('App name: ${appData.name}');
/// print('File upload enabled: ${appData.fileUploadConfig.enabled}');
/// print('File upload size limit: ${appData.fileUploadConfig.sizeLimit}');
/// case Failure(error: final error):
/// print('Failed to get app data: $error');
/// }
Expand Down Expand Up @@ -745,7 +745,7 @@ abstract interface class StreamFeedsClient {
/// Example:
/// ```dart
/// final result = await client.deleteFile(
/// 'https://cdn.stream.io/uploads/video.mp4'
/// url: 'https://cdn.stream.io/uploads/video.mp4',
/// );
///
/// switch (result) {
Expand All @@ -767,7 +767,7 @@ abstract interface class StreamFeedsClient {
/// Example:
/// ```dart
/// final result = await client.deleteImage(
/// 'https://cdn.stream.io/uploads/photo.jpg'
/// url: 'https://cdn.stream.io/uploads/photo.jpg',
/// );
///
/// switch (result) {
Expand Down Expand Up @@ -852,6 +852,115 @@ abstract interface class StreamFeedsClient {
api.UnfollowBatchRequest request,
);

/// Reads collections by their references.
///
/// By default, users can only read their own collections.
///
/// Example:
/// ```dart
/// final result = await client.readCollections(
/// refs: ['collection:123', 'collection:456'],
/// );
///
/// switch (result) {
/// case Success(value: final response):
/// print('Found ${response.collections.length} collections');
/// case Failure(error: final error):
/// print('Failed to read collections: $error');
/// }
/// ```
///
/// Returns a [Result] containing [api.ReadCollectionsResponse] or an error.
Future<Result<api.ReadCollectionsResponse>> readCollections({
required List<String> refs,
});

/// Creates new collections in a batch operation.
///
/// Collections are data objects that can be attached to activities for managing
/// shared data across multiple activities.
///
/// Example:
/// ```dart
/// final result = await client.createCollections(
/// request: api.CreateCollectionsRequest(
/// collections: [
/// api.CollectionRequest(
/// id: '123',
/// name: 'my_collection',
/// custom: const {'key': 'value'},
/// ),
/// ],
/// ),
/// );
///
/// switch (result) {
/// case Success(value: final response):
/// print('Created ${response.collections.length} collections');
/// case Failure(error: final error):
/// print('Failed to create collections: $error');
/// }
/// ```
///
/// Returns a [Result] containing [api.CreateCollectionsResponse] or an error.
Future<Result<api.CreateCollectionsResponse>> createCollections({
required api.CreateCollectionsRequest request,
});

/// Updates existing collections in a batch operation.
///
/// Only the custom data field is updatable. Users can only update their own collections.
///
/// Example:
/// ```dart
/// final result = await client.updateCollections(
/// request: api.UpdateCollectionsRequest(
/// collections: [
/// api.UpdateCollectionRequest(
/// id: '123',
/// name: 'my_collection',
/// custom: const {'updated_key': 'updated_value'},
/// ),
/// ],
/// ),
/// );
///
/// switch (result) {
/// case Success(value: final response):
/// print('Updated ${response.collections.length} collections');
/// case Failure(error: final error):
/// print('Failed to update collections: $error');
/// }
/// ```
///
/// Returns a [Result] containing [api.UpdateCollectionsResponse] or an error.
Future<Result<api.UpdateCollectionsResponse>> updateCollections({
required api.UpdateCollectionsRequest request,
});

/// Deletes collections in a batch operation.
///
/// Users can only delete their own collections.
///
/// Example:
/// ```dart
/// final result = await client.deleteCollections(
/// refs: ['collection:123', 'collection:456'],
/// );
///
/// switch (result) {
/// case Success(value: final response):
/// print('Deleted collections successfully');
/// case Failure(error: final error):
/// print('Failed to delete collections: $error');
/// }
/// ```
///
/// Returns a [Result] containing [api.DeleteCollectionsResponse] or an error.
Future<Result<api.DeleteCollectionsResponse>> deleteCollections({
required List<String> refs,
});

/// The moderation client for managing moderation-related operations.
///
/// Provides access to moderation configurations, content moderation, and moderation-related
Expand Down Expand Up @@ -882,11 +991,12 @@ extension StreamFeedsClientHelpers on StreamFeedsClient {
/// final userFeed = client.feed('user', 'john');
/// final timelineFeed = client.feed('timeline', 'flat');
///
/// // Add an activity
/// // Add an activity with collection references
/// final result = await userFeed.addActivity(FeedAddActivityRequest(
/// verb: 'post',
/// object: 'picture:1',
/// extraData: {'message': 'Check out this picture!'},
/// type: 'post',
/// text: 'Check out this picture!',
/// custom: {'object': 'picture:1'},
/// collectionRefs: ['my_collection:123'],
/// ));
///
/// // Listen to state changes
Expand Down
1 change: 1 addition & 0 deletions packages/stream_feeds/lib/src/models.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export 'models/app_data.dart';
export 'models/batch_follow_data.dart';
export 'models/bookmark_data.dart';
export 'models/bookmark_folder_data.dart';
export 'models/collection_data.dart';
export 'models/comment_data.dart';
export 'models/feed_data.dart';
export 'models/feed_id.dart';
Expand Down
13 changes: 13 additions & 0 deletions packages/stream_feeds/lib/src/models/activity_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:stream_core/stream_core.dart';

import '../generated/api/models.dart';
import 'bookmark_data.dart';
import 'collection_data.dart';
import 'comment_data.dart';
import 'feed_data.dart';
import 'feeds_reaction_data.dart';
Expand All @@ -27,6 +28,7 @@ class ActivityData with _$ActivityData {
const ActivityData({
this.attachments = const [],
this.bookmarkCount = 0,
this.collections = const {},
this.commentCount = 0,
this.comments = const [],
required this.createdAt,
Expand Down Expand Up @@ -73,6 +75,13 @@ class ActivityData with _$ActivityData {
@override
final int bookmarkCount;

/// Collections attached to this activity.
///
/// A map of collection references to their enriched data. Collection references
/// are in the format "collection_name:collection_id".
@override
final Map<String, CollectionData> collections;

/// The total number of comments on this activity.
@override
final int commentCount;
Expand Down Expand Up @@ -264,6 +273,10 @@ extension ActivityResponseMapper on ActivityResponse {
return ActivityData(
attachments: attachments,
bookmarkCount: bookmarkCount,
collections: {
for (final entry in collections.entries)
entry.key: entry.value.toModel(),
},
commentCount: commentCount,
comments: [...comments.map((c) => c.toModel())],
createdAt: createdAt,
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading