Skip to content

Conversation

@xsahil03x
Copy link
Member

@xsahil03x xsahil03x commented Sep 2, 2025

This pull request adds comprehensive support for file and image uploads and deletions via a new CDN client abstraction, and improves how attachments are handled when adding comments. The changes introduce a new CdnClient interface and implementation, update the configuration and client initialization to support custom CDN clients, and enhance the ActivityAddCommentRequest model to better support attachment uploads.

CDN Client Integration and File Management:

  • Introduced a new CdnClient interface and a default FeedsCdnClient implementation for handling file and image uploads and deletions, with progress tracking and cancellation support. This includes new methods for uploadFile, uploadImage, deleteFile, and deleteImage in both the API and client layers. [1] [2] [3] [4]
  • Updated the main client (StreamFeedsClientImpl) to initialize and expose the CDN client and attachment uploader, and to delegate file/image deletion to the CDN client. [1] [2] [3] [4]

Configuration and Extensibility:

  • Enhanced the FeedsConfig model to allow injection of a custom CdnClient, enabling flexible CDN integration. [1] [2]

Attachment Uploads in Comments:

  • Expanded the ActivityAddCommentRequest model to include fields for activityId, activityType, and a list of StreamAttachment uploads, improving support for adding comments with file/image attachments. The corresponding mapper and generated code were updated accordingly. [1] [2] [3] [4] [5] [6] [7] [8]

API and Internal Refactoring:

  • Refactored the internal API and generated code to rename and adapt upload methods (e.g., sendFileuploadFile), add progress/cancellation parameters, and support new response types for image uploads and deletions. [1] [2] [3] [4] [5] [6]

These changes collectively make file and image management more robust and extensible, and improve the developer experience when working with attachments in comments.

This commit introduces the `StreamAttachmentUploader` for handling file and image uploads to the Stream CDN.

Key changes:
- Added `CdnApi` with methods for uploading and deleting files and images.
- Implemented `FeedsCdnClient` to wrap `CdnApi` and conform to the `CdnClient` interface from `stream_core`.
- Integrated `StreamAttachmentUploader` into `FeedsClientImpl`.
- Modified `ActivitiesRepository` and `CommentsRepository` to use `StreamAttachmentUploader` for uploading attachments before creating activities or comments.
- Updated `FeedAddActivityRequest` and `ActivityAddCommentRequest` to include `attachmentUploads` (list of `StreamAttachment`).
- Renamed `onSendProgress` to `onUploadProgress` in `CdnApi` for clarity.
- Updated relevant public API surfaces and internal logic to support attachment uploads.
- Regenerated `cdn_api.g.dart`.
@xsahil03x xsahil03x requested a review from a team as a code owner September 2, 2025 02:10
Comment on lines 36 to 52
final uploadedAttachments = await _uploadStreamAttachments(
request.attachmentUploads,
);

final currentAttachments = request.attachments ?? [];
final updatedAttachments = currentAttachments.merge(
uploadedAttachments,
key: (it) => (it.type, it.assetUrl, it.imageUrl),
);

final updatedRequest = request.copyWith(
attachments: updatedAttachments.takeIf((it) => it.isNotEmpty),
);

final result = await _api.addActivity(
addActivityRequest: request,
addActivityRequest: updatedRequest.toRequest(),
);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we just create the activities without attachments if the uploads fail? Shouldn't we return an error in that case?

//
// Processes the provided attachments by uploading them via the uploader
// and converting successful uploads to API attachment objects.
Future<List<api.Attachment>> _uploadStreamAttachments(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have the feeling this is the exact same code in multiple places, maybe this can also go to the helper class?

This commit introduces a new `StreamAttachmentUploader` extension with methods to handle attachment uploads for requests that implement the `HasAttachments` interface.

The key changes include:

- **`HasAttachments` interface:** Defines a contract for requests that support attachment uploads, ensuring they provide a way to access and update attachments.
- **`processRequest` method:** Uploads `StreamAttachment` items from a single request, merges them with existing attachments, and returns an updated request.
- **`processRequestsBatch` method:** Processes multiple requests with attachment uploads in parallel, leveraging `processRequest`.
- **Integration with `FeedAddActivityRequest` and `ActivityAddCommentRequest`:** These request models now implement `HasAttachments` and utilize the new uploader utility in `ActivitiesRepository` and `CommentsRepository` respectively. This simplifies attachment handling in these repositories.
- **Nullable `attachmentUploads`:** The `attachmentUploads` field in `FeedAddActivityRequest` and `ActivityAddCommentRequest` is now nullable to align with the uploader's behavior, where an empty or null list signifies no new uploads.

This change streamlines the process of uploading attachments before sending API requests, making the code more robust and easier to maintain.
Comment on lines 26 to 40
late final feed = context.client.feedFromQuery(
FeedQuery(
fid: FeedId(group: 'user', id: widget.currentUser.id),
fid: FeedId(group: 'user', id: context.currentUser.id),
data: FeedInputData(
visibility: FeedVisibility.public,
members: [FeedMemberRequestData(userId: widget.currentUser.id)],
members: [FeedMemberRequestData(userId: context.currentUser.id)],
),
),
);

@override
void initState() {
super.initState();
void didChangeDependencies() {
super.didChangeDependencies();
feed.getOrCreate();
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

context.client might be different here, so we also have to (re)create the feed object in didChangeDependencies.

return SessionScope(
user: user,
client: client,
child: const AutoRouter(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you use AutoRouter here? That's already done by the material app right?

Comment on lines 36 to 40
@override
void initState() {
super.initState();
void didChangeDependencies() {
super.didChangeDependencies();
feed.getOrCreate();
}
Copy link
Collaborator

@renefloor renefloor Sep 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another issue here is that this is being called every time you resize the window, as the MediaQuery changes.

In this video you see 429 responses in the logs on the background.

screen.resizing.mov

I think what you want to do here is to only getOrCreate the feed when the current user changes?
So something like this:

  Feed? feed;
  String? currentUserId;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    if (currentUserId != context.currentUser.id) {
      currentUserId = context.currentUser.id;
      feed?.dispose();
      feed = context.client.feedFromQuery(
        FeedQuery(
          fid: FeedId(group: 'user', id: context.currentUser.id),
          data: FeedInputData(
            visibility: FeedVisibility.public,
            members: [FeedMemberRequestData(userId: context.currentUser.id)],
          ),
        ),
      )..getOrCreate();
    }
  }

This commit introduces a dedicated session scope for dependency injection using `injectable`. This replaces the previous `SessionScope` InheritedWidget.

Key changes:
- **`SessionModule`:** A new module is added to provide `StreamFeedsClient` within the session scope.
- **`HomeScreen`:** Now manages the session scope lifecycle. It initializes the scope in `initState` and disposes of it in `dispose`.
- **Removed `SessionScope` widget:** The `SessionScope` InheritedWidget and its associated extension have been removed.
- **Updated `UserFeedScreen`:** Now retrieves `StreamFeedsClient` and the current user directly from the DI container within the session scope.
- **`AuthGuard`:** Simplified logging.

This change centralizes session-specific dependency management and aligns with standard DI practices.
This commit renames `AuthModule` to `SessionModule` to better reflect its purpose.

Additionally, the method `authenticatedFeeds` within the module has been renamed to `authenticatedFeedsClient` for clarity. This change has been propagated to the dependency injection configuration.
# Conflicts:
#	melos.yaml
#	packages/stream_feeds/lib/stream_feeds.dart
#	packages/stream_feeds/pubspec.yaml
#	sample_app/lib/screens/user_feed/user_feed_screen.dart
This commit updates the documentation snippets for file uploads and activities to reflect recent changes and best practices.

**Key changes:**

- **`03_03_file_uploads.dart`:**
    - Added comprehensive examples for uploading files and images, including:
        - Step-by-step instructions for uploading individual files.
        - How to include `StreamAttachment` objects directly in `FeedAddActivityRequest` and `ActivityAddCommentRequest` for automatic uploading.
        - Guidance on implementing a custom `CdnClient` for users who want to use their own CDN.
- **`01_01_quickstart.dart` and `sample_app`:**
    - Updated `addComment` calls to use the named `request` parameter and `ActivityAddCommentRequest`.
- **`04_01_feeds.dart`:**
    - Improved formatting and added comments for clarity in feed reading and filtering examples.
- **`03_01_activities.dart`:**
    - Added `custom` data examples to image and video `Attachment` objects.
    - Simplified the delete activity example.
- **`sample_app` UI:**
    - Updated icons in `activity_content.dart` for comments, hearts, and reposts to use outlined variants for inactive states and filled variants for active states.
    - Changed the `type` of new activities created in `create_activity_bottom_sheet.dart` from 'activity' to 'post'.
- **`models.dart`:**
    - Exported `FeedsConfig`.
- **`pubspec.lock`:**
    - Updated various dependency versions.
This commit updates the `deleteFile` and `deleteImage` methods in `FeedsClient` and `FeedsClientImpl` to accept the `url` parameter as a named, required argument.

Additionally, it adds a new code snippet to the documentation demonstrating how to use these methods to delete files and images from the CDN.
@xsahil03x xsahil03x merged commit e2b1ad5 into main Sep 5, 2025
2 of 6 checks passed
@xsahil03x xsahil03x deleted the feat/attachment-uploader branch September 5, 2025 09:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants