Skip to content
Open
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
4 changes: 2 additions & 2 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ melos run generate:all
melos run generate:dart # Pure Dart packages only
melos run generate:flutter # Flutter packages only

# Generate OpenAPI client (rarely needed, requires OpenAPI spec)
melos run gen:feeds
# Generate OpenAPI client (rarely needed, requires CHAT_DIR to point at a chat checkout)
CHAT_DIR=/path/to/chat melos run gen:feeds
```

**Important**: NEVER manually edit generated files (`*.freezed.dart`, `*.g.dart`, `src/generated/`). Changes must be made in source files, then regenerate.
Expand Down
10 changes: 5 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,9 @@ The project uses extensive code generation:

### Generated Code

1. **OpenAPI Client** (`src/generated/api/`): Auto-generated from OpenAPI spec
1. **OpenAPI Client** (`src/generated/api/`): Auto-generated from the OpenAPI spec
- Generated via `scripts/generate.sh`
- The spec is fetched from [GetStream/protocol](https://github.com/GetStream/protocol) at its latest `openapi-v*` tag; `api.dart` records which tag the checked-in client came from
- Includes API models, endpoints, and serialization

2. **Freezed Models** (`*.freezed.dart`): Immutable data classes
Expand All @@ -85,8 +86,8 @@ melos run generate:dart
# Generate only Flutter packages
melos run generate:flutter

# Generate OpenAPI client (requires OpenAPI spec)
melos run gen:feeds
# Generate OpenAPI client (requires CHAT_DIR to point at a chat checkout)
CHAT_DIR=/path/to/chat melos run gen:feeds
```

**Important**: Never manually edit generated files. All changes must be made in source files or OpenAPI specs.
Expand Down Expand Up @@ -381,8 +382,7 @@ melos run lint:all
- Commit both source and generated files

2. **Updating OpenAPI Client**:
- Update OpenAPI spec (if you have access)
- Run `melos run gen:feeds`
- Run `CHAT_DIR=/path/to/chat melos run gen:feeds` (pulls the latest tagged spec from GetStream/protocol)
- Review generated changes in `src/generated/`

3. **Adding New Features**:
Expand Down
8 changes: 6 additions & 2 deletions melos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,18 @@ command:
internet_connection_checker_plus: ^3.0.0
jiffy: ^6.3.2
photo_view: ^0.15.0
json_annotation: ^4.9.0
json_annotation: ^4.12.0
meta: ^1.9.1
retrofit: ^4.9.2
rxdart: ^0.28.0
shared_preferences: ^2.5.3
state_notifier: ^1.0.0
stream_feeds: ^0.5.1
stream_core: ^0.4.0
stream_core:
git:
url: https://github.com/GetStream/stream-core-flutter.git
ref: 813026f2712b9a82065b1f50e007c049cb63eaed
path: packages/stream_core
video_player: ^2.10.0
uuid: ^4.5.1
web_socket_channel: ^3.0.0
Expand Down
3 changes: 3 additions & 0 deletions packages/stream_feeds/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
- Added `location` (`LocationCoordinate?`) field to `FeedData`.
- Added `createNotificationActivity`, `skipPush`, and `enrichOwnFields` optional flags to `FeedAddActivityRequest`.
- Added optional `deleteNotificationActivity` parameter to `Feed.deleteActivity`, `Feed.deleteComment`, `Feed.deleteActivityReaction`, `Feed.deleteCommentReaction`, `Activity.deleteComment`, and `Activity.deleteCommentReaction` — when `true`, the corresponding notification activity is also deleted.
- Added `ModerationClient.queryModerationConfigs`, which queries moderation configurations with filters and pagination and returns a `PaginationResult<ModerationConfigData>`.

### WebSocket events
- `ActivityRestoredEvent` and `CommentRestoredEvent` are now handled: restored items are upserted back into feed/list state.
Expand Down Expand Up @@ -51,6 +52,8 @@ the new names at your earliest convenience.
- [BREAKING] `PollResponseData.votingVisibility` is now a required field (was optional in the old `Poll` class). Code constructing `Poll`/`PollResponseData` directly (e.g. in tests) must supply `votingVisibility`.
- [BREAKING] The following types were removed from the public API. They belonged to video/call/chat functionality not relevant to the Feeds SDK and should not have been exported: `AudioSettingsResponse`, `BackstageSettingsResponse`, `BroadcastSettingsResponse`, `CallIngressResponse`, `CallParticipantResponse`, `CallSessionResponse`, `CallSettingsResponse`, `Channel`, `ChannelConfig`, `ChannelMember`, `ChannelMemberLookup`, `ChannelPushPreferences`, `CompositeRecordingResponse`, `ConfigOverrides`, `DeliveryReceipts`, `DenormalizedChannelFields`, `Device`, `EgressHlsResponse`, `EgressResponse`, `EgressRtmpResponse`, `FrameRecordingResponse`, `FrameRecordingSettingsResponse`, `GeofenceSettingsResponse`, `HlsSettingsResponse`, `IndividualRecordingResponse`, `IndividualRecordingSettingsResponse`, `IngressAudioEncodingResponse`, `IngressSettingsResponse`, `IngressSourceResponse`, `IngressVideoEncodingResponse`, `IngressVideoLayerResponse`, `LimitsSettingsResponse`, `Message`, `MessageReminder`, `ModerationActionConfig`, `NoiseCancellationSettings`, `PrivacySettings`, `RawRecordingResponse`, `RawRecordingSettingsResponse`, `ReadReceipts`, `RecordSettingsResponse`, `RingSettingsResponse`, `RtmpIngress`, `RtmpSettingsResponse`, `ScreensharingSettingsResponse`, `SessionSettingsResponse`, `SharedLocation`, `SpeechSegmentConfig`, `SrtIngress`, `TargetResolution`, `ThumbnailResponse`, `ThumbnailsSettingsResponse`, `TranscriptionSettingsResponse`, `TranslationSettings`, `TypingIndicators`, `UserMutedEvent`, `VideoSettingsResponse`, `WhipIngress`.
- [BREAKING] Changed `ActivityCommentList.state` getter return type from `StateNotifier<ActivityCommentListState>` to `ActivityCommentListState` to be consistent with all other state classes.
- [BREAKING] All generated enums are now `extension type`s over `String` instead of Dart `enum`s. Existing usages such as `ActivityResponseVisibility.public` still compile, but `.values`, `.name`, `.index` and exhaustive `switch` no longer work, and the `unknown` sentinel member is gone. In exchange the types are now forward compatible: a value the SDK does not know is passed through as-is rather than collapsing to `unknown`, and they can be compared to and used as plain `String`s.
- [BREAKING] `EpochDateTimeConverter` removed, replaced by `StreamDateTimeConverter` (from `stream_core`). Besides accepting both RFC3339 strings and epoch nanoseconds, it differs in two observable ways: deserialized `DateTime`s are now in UTC rather than local time (so `isUtc` is `true`, calendar getters like `hour` report UTC, and `==` against a local `DateTime` for the same instant is now `false` — use `isAtSameMomentAs` to compare across zones), and serialization emits an RFC3339 string instead of an epoch-nanosecond integer.

### 🔄 Changed

Expand Down
8 changes: 4 additions & 4 deletions packages/stream_feeds/lib/src/client/moderation_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ class ModerationClient {
///
/// Prohibits the user specified in [banRequest] from accessing platform features.
///
/// Returns a [Result] containing a [api.BanResponse] or an error.
Future<Result<api.BanResponse>> ban({required api.BanRequest banRequest}) {
/// Returns a [Result] containing a [api.ModerationBanResponse] or an error.
Future<Result<api.ModerationBanResponse>> ban({required api.BanRequest banRequest}) {
return _moderationRepository.ban(banRequest);
}

Expand Down Expand Up @@ -93,8 +93,8 @@ class ModerationClient {
///
/// Reports inappropriate content specified in [flagRequest] for review.
///
/// Returns a [Result] containing a [api.FlagResponse] or an error.
Future<Result<api.FlagResponse>> flag({
/// Returns a [Result] containing a [api.FlagItemResponse] or an error.
Future<Result<api.FlagItemResponse>> flag({
required api.FlagRequest flagRequest,
}) {
return _moderationRepository.flag(flagRequest);
Expand Down
1 change: 1 addition & 0 deletions packages/stream_feeds/lib/src/generated/api/api.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Code generated by GetStream internal OpenAPI code generator. DO NOT EDIT.
// Source: GetStream/protocol openapi/v2/feeds-clientside-api.yaml @ openapi-v236.3.0

// ignore_for_file: directives_ordering

Expand Down
102 changes: 93 additions & 9 deletions packages/stream_feeds/lib/src/generated/api/api/default_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,20 @@ abstract interface class DefaultApi {
});

@POST('/api/v2/moderation/ban')
Future<Result<BanResponse>> ban({
Future<Result<ModerationBanResponse>> ban({
@Body() required BanRequest banRequest,
});

@POST('/api/v2/feeds/activities/reactions/query')
Future<Result<BatchQueryActivityReactionsResponse>> batchQueryActivityReactions({
@Body() required BatchQueryActivityReactionsRequest batchQueryActivityReactionsRequest,
});

@POST('/api/v2/feeds/comments/reactions/query')
Future<Result<BatchQueryCommentReactionsResponse>> batchQueryCommentReactions({
@Body() required BatchQueryCommentReactionsRequest batchQueryCommentReactionsRequest,
});

@POST('/api/v2/users/block')
Future<Result<BlockUsersResponse>> blockUsers({
@Body() required BlockUsersRequest blockUsersRequest,
Expand Down Expand Up @@ -159,6 +169,11 @@ abstract interface class DefaultApi {
@Body() required CreatePollOptionRequest createPollOptionRequest,
});

@POST('/api/v2/moderation/queues')
Future<Result<QueueResponse>> createQueue({
@Body() required CreateQueueRequest createQueueRequest,
});

@POST('/api/v2/usergroups')
Future<Result<CreateUserGroupResponse>> createUserGroup({
@Body() required CreateUserGroupRequest createUserGroupRequest,
Expand Down Expand Up @@ -262,22 +277,24 @@ abstract interface class DefaultApi {
@DELETE('/api/v2/polls/{poll_id}')
Future<Result<DurationResponse>> deletePoll({
@Path('poll_id') required String pollId,
@Query('user_id') String? userId,
});

@DELETE('/api/v2/polls/{poll_id}/options/{option_id}')
Future<Result<DurationResponse>> deletePollOption({
@Path('poll_id') required String pollId,
@Path('option_id') required String optionId,
@Query('user_id') String? userId,
});

@DELETE('/api/v2/feeds/activities/{activity_id}/polls/{poll_id}/vote/{vote_id}')
Future<Result<PollVoteResponse>> deletePollVote({
@Path('activity_id') required String activityId,
@Path('poll_id') required String pollId,
@Path('vote_id') required String voteId,
@Query('user_id') String? userId,
});

@POST('/api/v2/moderation/queues/{id}/delete')
Future<Result<QueueResponse>> deleteQueue({
@Path('id') required String id,
});

@DELETE('/api/v2/usergroups/{id}')
Expand All @@ -287,7 +304,7 @@ abstract interface class DefaultApi {
});

@POST('/api/v2/moderation/flag')
Future<Result<FlagResponse>> flag({
Future<Result<FlagItemResponse>> flag({
@Body() required FlagRequest flagRequest,
});

Expand All @@ -314,6 +331,8 @@ abstract interface class DefaultApi {
@Path('id') required String id,
@Query('comment_sort') String? commentSort,
@Query('comment_limit') int? commentLimit,
@Query('language') String? language,
@Query('translate_text') bool? translateText,
});

@GET('/api/v2/app')
Expand All @@ -330,6 +349,8 @@ abstract interface class DefaultApi {
@GET('/api/v2/feeds/comments/{id}')
Future<Result<GetCommentResponse>> getComment({
@Path('id') required String id,
@Query('language') String? language,
@Query('translate_text') bool? translateText,
});

@GET('/api/v2/feeds/comments/{id}/replies')
Expand All @@ -339,6 +360,8 @@ abstract interface class DefaultApi {
@Query('sort') String? sort,
@Query('replies_limit') int? repliesLimit,
@Query('id_around') String? idAround,
@Query('language') String? language,
@Query('translate_text') bool? translateText,
@Query('limit') int? limit,
@Query('prev') String? prev,
@Query('next') String? next,
Expand All @@ -352,6 +375,8 @@ abstract interface class DefaultApi {
@Query('sort') String? sort,
@Query('replies_limit') int? repliesLimit,
@Query('id_around') String? idAround,
@Query('language') String? language,
@Query('translate_text') bool? translateText,
@Query('limit') int? limit,
@Query('prev') String? prev,
@Query('next') String? next,
Expand All @@ -363,6 +388,12 @@ abstract interface class DefaultApi {
@Query('team') String? team,
});

@GET('/api/v2/feeds/feed_groups/{feed_group_id}/feeds/{feed_id}/counts')
Future<Result<GetFeedCountsResponse>> getFeedCounts({
@Path('feed_group_id') required String feedGroupId,
@Path('feed_id') required String feedId,
});

@GET('/api/v2/feeds/feed_groups/{feed_group_id}/follow_suggestions')
Future<Result<GetFollowSuggestionsResponse>> getFollowSuggestions({
@Path('feed_group_id') required String feedGroupId,
Expand All @@ -378,6 +409,8 @@ abstract interface class DefaultApi {
Future<Result<GetOrCreateFeedResponse>> getOrCreateFeed({
@Path('feed_group_id') required String feedGroupId,
@Path('feed_id') required String feedId,
@Query('language') String? language,
@Query('translate_text') bool? translateText,
@Body() GetOrCreateFeedRequest? getOrCreateFeedRequest,
});

Expand All @@ -404,14 +437,17 @@ abstract interface class DefaultApi {
@GET('/api/v2/polls/{poll_id}')
Future<Result<PollResponse>> getPoll({
@Path('poll_id') required String pollId,
@Query('user_id') String? userId,
});

@GET('/api/v2/polls/{poll_id}/options/{option_id}')
Future<Result<PollOptionResponse>> getPollOption({
@Path('poll_id') required String pollId,
@Path('option_id') required String optionId,
@Query('user_id') String? userId,
});

@GET('/api/v2/moderation/queues/{id}')
Future<Result<QueueResponse>> getQueue({
@Path('id') required String id,
});

@GET('/api/v2/usergroups/{id}')
Expand All @@ -429,14 +465,25 @@ abstract interface class DefaultApi {
@GET('/api/v2/users/live_locations')
Future<Result<SharedLocationsResponse>> getUserLiveLocations();

@POST('/api/v2/blocklists/{id}/import')
Future<Result<ImportBlockListResponse>> importBlockList({
@Path('id') required String id,
@Body() required ImportBlockListRequest importBlockListRequest,
});

@GET('/api/v2/blocklists')
Future<Result<ListBlockListResponse>> listBlockLists({
@Query('team') String? team,
@Query('cursor') String? cursor,
@Query('limit') int? limit,
});

@GET('/api/v2/devices')
Future<Result<ListDevicesResponse>> listDevices();

@GET('/api/v2/moderation/queues')
Future<Result<ListQueuesResponse>> listQueues();

@GET('/api/v2/usergroups')
Future<Result<ListUserGroupsResponse>> listUserGroups({
@Query('limit') int? limit,
Expand Down Expand Up @@ -477,6 +524,8 @@ abstract interface class DefaultApi {

@POST('/api/v2/feeds/activities/query')
Future<Result<QueryActivitiesResponse>> queryActivities({
@Query('language') String? language,
@Query('translate_text') bool? translateText,
@Body() QueryActivitiesRequest? queryActivitiesRequest,
});

Expand All @@ -486,6 +535,14 @@ abstract interface class DefaultApi {
@Body() QueryActivityReactionsRequest? queryActivityReactionsRequest,
});

@GET('/api/v2/feeds/activities/{activity_id}/shares')
Future<Result<QueryActivitySharesResponse>> queryActivityShares({
@Path('activity_id') required String activityId,
@Query('limit') int? limit,
@Query('prev') String? prev,
@Query('next') String? next,
});

@POST('/api/v2/moderation/appeals')
Future<Result<QueryAppealsResponse>> queryAppeals({
@Body() QueryAppealsRequest? queryAppealsRequest,
Expand All @@ -498,6 +555,8 @@ abstract interface class DefaultApi {

@POST('/api/v2/feeds/bookmarks/query')
Future<Result<QueryBookmarksResponse>> queryBookmarks({
@Query('language') String? language,
@Query('translate_text') bool? translateText,
@Body() QueryBookmarksRequest? queryBookmarksRequest,
});

Expand All @@ -514,6 +573,8 @@ abstract interface class DefaultApi {

@POST('/api/v2/feeds/comments/query')
Future<Result<QueryCommentsResponse>> queryComments({
@Query('language') String? language,
@Query('translate_text') bool? translateText,
@Body() required QueryCommentsRequest queryCommentsRequest,
});

Expand Down Expand Up @@ -543,19 +604,19 @@ abstract interface class DefaultApi {
Future<Result<QueryPinnedActivitiesResponse>> queryPinnedActivities({
@Path('feed_group_id') required String feedGroupId,
@Path('feed_id') required String feedId,
@Query('language') String? language,
@Query('translate_text') bool? translateText,
@Body() QueryPinnedActivitiesRequest? queryPinnedActivitiesRequest,
});

@POST('/api/v2/polls/{poll_id}/votes')
Future<Result<PollVotesResponse>> queryPollVotes({
@Path('poll_id') required String pollId,
@Query('user_id') String? userId,
@Body() QueryPollVotesRequest? queryPollVotesRequest,
});

@POST('/api/v2/polls/query')
Future<Result<QueryPollsResponse>> queryPolls({
@Query('user_id') String? userId,
@Body() QueryPollsRequest? queryPollsRequest,
});

Expand Down Expand Up @@ -636,6 +697,18 @@ abstract interface class DefaultApi {
@Body() required TrackActivityMetricsRequest trackActivityMetricsRequest,
});

@POST('/api/v2/feeds/activities/{id}/translate')
Future<Result<TranslateActivityResponse>> translateActivity({
@Path('id') required String id,
@Body() required TranslateActivityRequest translateActivityRequest,
});

@POST('/api/v2/feeds/comments/{id}/translate')
Future<Result<TranslateCommentResponse>> translateComment({
@Path('id') required String id,
@Body() required TranslateCommentRequest translateCommentRequest,
});

@POST('/api/v2/users/unblock')
Future<Result<UnblockUsersResponse>> unblockUsers({
@Body() required UnblockUsersRequest unblockUsersRequest,
Expand All @@ -650,6 +723,11 @@ abstract interface class DefaultApi {
@Query('enrich_own_fields') bool? enrichOwnFields,
});

@POST('/api/v2/moderation/unmute')
Future<Result<UnmuteResponse>> unmute({
@Body() required UnmuteRequest unmuteRequest,
});

@DELETE('/api/v2/feeds/feed_groups/{feed_group_id}/feeds/{feed_id}/activities/{activity_id}/pin')
Future<Result<UnpinActivityResponse>> unpinActivity({
@Path('feed_group_id') required String feedGroupId,
Expand Down Expand Up @@ -757,6 +835,12 @@ abstract interface class DefaultApi {
@Body() required UpsertPushPreferencesRequest upsertPushPreferencesRequest,
});

@PATCH('/api/v2/moderation/queues/{id}')
Future<Result<QueueResponse>> updateQueue({
@Path('id') required String id,
@Body() UpdateQueueRequest? updateQueueRequest,
});

@PUT('/api/v2/usergroups/{id}')
Future<Result<UpdateUserGroupResponse>> updateUserGroup({
@Path('id') required String id,
Expand Down
Loading
Loading