Skip to content

Conversation

@xsahil03x
Copy link
Member

@xsahil03x xsahil03x commented Sep 22, 2025

Submit a pull request

Fixes: FLU-262

Description of the pull request

This commit modifies the toDraftMessage method in the Message class to ensure that only attachments that have been successfully uploaded are included in the resulting DraftMessage.

Previously, all attachments, regardless of their upload status, were included. This change filters the attachments, keeping only those where uploadState.isSuccess is true.

Summary by CodeRabbit

  • Bug Fixes

    • Draft messages now include only attachments that were successfully uploaded, preventing pending or failed files from appearing in drafts.
  • Documentation

    • Updated changelog with details of the draft attachment handling fix.

This commit modifies the `toDraftMessage` method in the `Message` class to ensure that only attachments that have been successfully uploaded are included in the resulting `DraftMessage`.

Previously, all attachments, regardless of their upload status, were included. This change filters the attachments, keeping only those where `uploadState.isSuccess` is true.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 22, 2025

Walkthrough

Restricts Message.toDraftMessage to include only attachments with a successful uploadState when creating DraftMessage. Updates tests to set explicit uploadState and adds a new test verifying the filtering. Adds a CHANGELOG entry documenting the change.

Changes

Cohort / File(s) Summary of Changes
Changelog update
packages/stream_chat/CHANGELOG.md
Added Upcoming/Fixed note: Draft messages now include only successfully uploaded attachments from toDraftMessage.
DraftMessage conversion logic
packages/stream_chat/lib/src/core/models/draft_message.dart
Modified toDraftMessage to filter attachments by UploadState.success before inclusion; introduced local variable and comment.
Tests for draft attachment filtering
packages/stream_chat/test/src/core/models/draft_message_test.dart
Added import; set explicit uploadState in fixtures; added test asserting only successful attachments are kept; adjusted existing tests accordingly.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Composer as Message Composer
  participant Model as Message.toDraftMessage()
  participant Draft as DraftMessage

  User->>Composer: Compose message with attachments
  Composer->>Model: Convert Message to Draft
  rect rgb(230,245,255)
    note over Model: New behavior
    Model->>Model: Filter attachments where uploadState == success
  end
  Model->>Draft: Create DraftMessage(attachments: filtered)
  Draft-->>Composer: Return DraftMessage
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested reviewers

  • renefloor

Poem

I nibbled the drafts, attachments in tow,
Only the uploads that truly glow.
No ghostly files, no broken preview,
A tidy burrow for messages new.
Thump-thump! The channel feels just right—
Carrots, code, and clean compose tonight. 🥕✨

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title concisely describes the main change: restricting attachments included in drafts to only uploaded ones, which directly reflects the edit in Message.toDraftMessage and the accompanying tests and changelog; it is short, clear, and focused for a reviewer scanning history. The phrasing is developer-friendly and correctly highlights the behavioral fix rather than implementation details. The "llc" prefix appears to be an internal scope marker and does not reduce clarity for the primary change.
Linked Issues Check ✅ Passed The code change filters attachments in Message.toDraftMessage to only include those with a successful uploadState and adds tests validating this behavior, which directly addresses the symptom reported in [FLU-262] where drafts retained invalid/unuploaded media previews after navigating away and returning to a channel. The update prevents non-successful attachments from being persisted in drafts and the changelog documents the fix, aligning the implementation with the linked issue's objective to avoid broken media in composer drafts. Based on the provided summaries, the change targets the coding-related root cause described in the issue.
Out of Scope Changes Check ✅ Passed All modifications are scoped to packages/stream_chat/lib/src/core/models/draft_message.dart, its tests, and the package CHANGELOG.md, which are directly relevant to the draft-attachment behavior described in the linked issue, and there are no API signature changes or unrelated file edits included in the summary. I detect no out-of-scope or unrelated changes in the provided diffs.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/draft-attachments

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 21411ee and c5edb31.

📒 Files selected for processing (1)
  • packages/stream_chat/CHANGELOG.md (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/stream_chat/CHANGELOG.md
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: build (android)
  • GitHub Check: test
  • GitHub Check: stream_chat_flutter
  • GitHub Check: stream_chat_localizations
  • GitHub Check: stream_chat_flutter_core
  • GitHub Check: analyze_legacy_versions

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (5)
packages/stream_chat/CHANGELOG.md (1)

10-10: Tighten wording and reference the scope (method + state).

Consider clarifying the entry and referencing the ticket explicitly.

-- Fixed `toDraftMessage` to only include successfully uploaded attachments in draft messages.
+- Fixed `Message.toDraftMessage` to include only attachments with `UploadState.success` in draft messages (FLU-262).
packages/stream_chat/lib/src/core/models/draft_message.dart (2)

187-191: Docstring mismatch (extension and method direction).

This converts a Message to a DraftMessage, not the other way around.

-  /// Converts this [DraftMessage] to a [Message].
+  /// Converts this [Message] to a [DraftMessage].

192-196: Optional: inline filter to reduce temp allocation.

No behavioral change; simplifies the method.

-    final uploadedAttachments = attachments
-        .where((it) => it.uploadState?.isSuccess ?? true)
-        .toList();
-
     return DraftMessage(
       id: id,
       text: text,
       type: type,
-      attachments: uploadedAttachments,
+      attachments:
+          attachments.where((it) => it.uploadState?.isSuccess ?? true).toList(),

Also applies to: 201-201

packages/stream_chat/test/src/core/models/draft_message_test.dart (2)

4-4: Remove unused import.

attachment_file.dart isn’t referenced in this test suite.

-import 'package:stream_chat/src/core/models/attachment_file.dart';

359-399: Add a case for attachments with no uploadState (e.g., OG/link embeds).

To prevent regressions, cover attachments where uploadState is null—they should remain included.

Suggested test (add alongside this group):

test('includes attachments with null uploadState (remote/OG)', () {
  final remoteImage = Attachment(id: 'og-1', type: 'image'); // no uploadState
  final msg = Message(id: 'm1', text: 'link', attachments: [remoteImage]);

  final draft = msg.toDraftMessage();

  expect(draft.attachments, hasLength(1));
  expect(draft.attachments.first.id, 'og-1');
});
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ee78069 and e14a61f.

📒 Files selected for processing (3)
  • packages/stream_chat/CHANGELOG.md (1 hunks)
  • packages/stream_chat/lib/src/core/models/draft_message.dart (1 hunks)
  • packages/stream_chat/test/src/core/models/draft_message_test.dart (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: stream_chat
  • GitHub Check: stream_chat_flutter_core
  • GitHub Check: stream_chat_flutter
  • GitHub Check: stream_chat_localizations
  • GitHub Check: stream_chat_persistence
  • GitHub Check: analyze_legacy_versions
  • GitHub Check: build (ios)
  • GitHub Check: build (android)
  • GitHub Check: test
  • GitHub Check: analyze
🔇 Additional comments (3)
packages/stream_chat/test/src/core/models/draft_message_test.dart (2)

307-309: LGTM: align fixtures with new filter.

Marking attachments as UploadState.success() keeps parity with the updated conversion behavior.


402-404: LGTM: round‑trip consistency.

Success-state fixtures ensure symmetry for DraftMessage → Message.

packages/stream_chat/lib/src/core/models/draft_message.dart (1)

192-196: No NPE — uploadState is defaulted to success; change not needed

Generated deserialization and the OG-constructor set missing upload_state to const UploadState.success (packages/stream_chat/lib/src/core/models/attachment.g.dart:38–40; packages/stream_chat/lib/src/core/models/attachment.dart:81–84), so uploadState is effectively non-null and calling .isSuccess is safe; the proposed null-guard is unnecessary and would alter semantics.

Likely an incorrect or invalid review comment.

@codecov
Copy link

codecov bot commented Sep 22, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 63.78%. Comparing base (ee78069) to head (c5edb31).
⚠️ Report is 3 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2388      +/-   ##
==========================================
+ Coverage   63.76%   63.78%   +0.01%     
==========================================
  Files         412      412              
  Lines       25821    25823       +2     
==========================================
+ Hits        16466    16470       +4     
+ Misses       9355     9353       -2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@xsahil03x xsahil03x force-pushed the fix/draft-attachments branch from 21411ee to c5edb31 Compare September 22, 2025 13:35
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (6)
packages/stream_chat/lib/src/client/channel.dart (1)

728-736: Block empty final payloads after uploads; also pre‑validate to avoid UI flicker

The post-upload guard and hard delete are correct and align with the tests. Two small nits:

  • Prefer !_isMessageValidForUpload(message) over == false.
  • Optional: add an early guard (before state!.updateMessage(message)) when there are no attachments to upload, to avoid briefly emitting a “sending” message that is immediately deleted.

Example tweak:

@@
-    state!.updateMessage(message);
+    // If there are no attachments to upload, we can pre-validate to avoid flicker.
+    if (message.attachments.isEmpty && !_isMessageValidForUpload(message)) {
+      throw const StreamChatError('Message is not valid for sending');
+    }
+    state!.updateMessage(message);
@@
-      if (_isMessageValidForUpload(message) == false) {
+      if (!_isMessageValidForUpload(message)) {
         client.logger.warning('Message is not valid for sending, removing it');
         state!.deleteMessage(message, hardDelete: true);
         throw const StreamChatError('Message is not valid for sending');
       }
packages/stream_chat/test/src/client/channel_test.dart (5)

464-475: Test: reject invalid empty message

Covers the new guard and ensures the client API isn’t called. Consider also asserting the message is removed from state to fully validate the hard-delete behavior.


478-517: Test: all attachments cancelled → reject

Great coverage of the cancellation path. Same optional enhancement: assert the message is removed from messagesStream after the error.


519-578: Test: cancelled attachment but text exists → send succeeds

Nice. To strengthen it, verify the payload sent to client.sendMessage has attachments filtered out (empty), not just that the call occurred.


580-644: Test: cancelled attachment but quoted message exists → send succeeds

Looks good. Same suggestion: assert sent message’s attachments are empty.


646-705: Test: cancelled attachment but poll exists → send succeeds

Good scenario coverage. Consider verifying that attachments are removed in the sent payload.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between e14a61f and 21411ee.

📒 Files selected for processing (3)
  • packages/stream_chat/CHANGELOG.md (1 hunks)
  • packages/stream_chat/lib/src/client/channel.dart (2 hunks)
  • packages/stream_chat/test/src/client/channel_test.dart (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/stream_chat/CHANGELOG.md
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: test
  • GitHub Check: analyze
  • GitHub Check: build (android)
  • GitHub Check: build (ios)
  • GitHub Check: stream_chat_flutter
  • GitHub Check: stream_chat
  • GitHub Check: stream_chat_persistence
  • GitHub Check: stream_chat_localizations
  • GitHub Check: stream_chat_flutter_core
🔇 Additional comments (2)
packages/stream_chat/lib/src/client/channel.dart (1)

661-668: Solid pre-send validity check helper

Clear, fast check for non-empty text, attachments, quoted message, or poll. Reads well and matches product intent.

packages/stream_chat/test/src/client/channel_test.dart (1)

248-248: Add text to baseline sendMessage test

Good adjustment to keep the message valid under the new guard.

@xsahil03x xsahil03x merged commit e83a790 into master Sep 23, 2025
19 checks passed
@xsahil03x xsahil03x deleted the fix/draft-attachments branch September 23, 2025 12:43
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