Skip to content

Conversation

@josefinalliende
Copy link
Contributor

@josefinalliende josefinalliende commented Nov 1, 2025

Description

This PR shows first image in the reply preview of message bubble and in the reply preview of chat input.

https://www.figma.com/design/dGJqv6m6pchdcwxJEwxxzB/White-Noise---Design-System?node-id=3536-43262&t=ZyUC6TcDAvCeNV3t-0

Chat input reply preview with media Message reply box with media
input

Commit by commit:

  • 🛠️ Fixed spacings of MediaFilePreview in the chat input, to match the Figma
  • ✨🧪Shows first media in the reply preview of the chat input
  • ✨ Shows the first media in the reply box of the message bubble
  • 🧪 Moves the reply box of the message bubble to its own file and adds tests

Type of Change

  • ✨ New feature (non-breaking change which adds functionality)
  • 🛠️ Bug fix (non-breaking change which fixes an issue)
  • ❌ Breaking change (fix or feature that would cause existing functionality to change)
  • 🧹 Code refactor
  • ✅ Build configuration change
  • 📝 Documentation
  • 🗑️ Chore
  • 🧪 Tests

Checklist

  • Run just precommit to ensure that formatting and linting are correct
  • Run just check-flutter-coverage to ensure that flutter coverage rules are passing
  • Updated the CHANGELOG.md file with your changes (if they affect the user experience)

Summary by CodeRabbit

  • Style

    • Redesigned message reply preview layout for improved visual hierarchy and readability.
    • Enhanced media attachment display within reply previews.
    • Optimized padding and spacing for better visual consistency.
  • Tests

    • Added comprehensive test coverage for reply preview functionality with various media attachment scenarios.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 1, 2025

Walkthrough

This PR refactors the chat reply preview UI by extracting a new MessageReplyBox component from message_widget.dart, restructures chat_input_reply_preview.dart to use a Row-based layout with conditional media rendering, adjusts padding in media_preview.dart, and adds comprehensive test coverage for both new and modified components.

Changes

Cohort / File(s) Change Summary
Reply preview component refactoring
lib/ui/chat/widgets/chat_input_reply_preview.dart, lib/ui/chat/widgets/message_reply_box.dart, lib/ui/chat/widgets/message_widget.dart
Extracts new MessageReplyBox ConsumerWidget from existing ReplyBox, refactors chat_input_reply_preview.dart from Column to Row-based layout with horizontal media/text arrangement, imports MessageMediaTile for conditional rendering of first media attachment, moves close control to right side with GestureDetector, maintains single-line text truncation and styling consistency.
Media preview adjustments
lib/ui/chat/widgets/media_preview.dart
Reduces horizontal padding from 14.w to 6.w; adjusts vertical padding from 16.h to 14.h (non-reply) and 8.h to 4.h (reply mode).
Test coverage
test/ui/chat/widgets/chat_input_reply_preview_test.dart, test/ui/chat/widgets/message_reply_box_test.dart
Adds comprehensive widget test suites for both components covering three scenarios each: no media attachments, single media attachment, and multiple media attachments; validates user name display, content rendering, and correct media tile representation.

Sequence Diagram(s)

sequenceDiagram
    participant MW as MessageWidget
    participant MRB as MessageReplyBox
    participant CIRP as ChatInputReplyPreview
    participant MMT as MessageMediaTile

    MW->>MRB: render(replyingTo)
    activate MRB
    MRB->>MRB: check replyingTo.mediaAttachments
    alt has media attachments
        MRB->>MMT: render first attachment
        activate MMT
        MMT-->>MRB: media tile widget
        deactivate MMT
    end
    MRB->>MRB: display sender name & content
    MRB-->>MW: reply preview widget
    deactivate MRB

    Note over CIRP: Row layout (new)<br/>Media | Text | Close
    CIRP->>MMT: conditional render
    alt media exists
        MMT-->>CIRP: first attachment tile
    end
    CIRP->>CIRP: Expanded column for text
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Layout restructuring in chat_input_reply_preview.dart: Verify Row/Expanded/Column nesting correctly handles media tiles and text overflow with single-line truncation
  • New MessageReplyBox component: Review logic for media attachment detection, localization provider usage, and theming extension application
  • Test coverage validation: Ensure test utilities (createTestMediaFile, testUser) and assertions comprehensively cover all three scenarios (no media, single, multiple attachments)
  • Padding adjustments in media_preview.dart: Confirm values align with overall UI design intent

Possibly related PRs

Suggested reviewers

  • untreu2
  • Quwaysim
  • codeswot

Poem

🐰 A row of replies, now neat and aligned,
Media tiles tucked in, with text intertwined,
No stacks anymore—just a compact display,
The preview looks sharper in every new way!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title "Feat/media reply preview" is fully related to and accurately summarizes the main change in this pull request. The PR's core objective is to add display of the first image/media attachment in reply previews for both the chat input and message bubble, which is precisely what the title conveys. The title is specific and descriptive—it clearly identifies both the feature type ("media") and the component being enhanced ("reply preview")—without being vague or generic. All the file changes, including restructuring of ChatInputReplyPreview, creation of MessageReplyBox, and associated tests, directly support this single cohesive feature objective.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/media-reply-preview

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.

@josefinalliende josefinalliende linked an issue Nov 1, 2025 that may be closed by this pull request
@josefinalliende josefinalliende force-pushed the feat/media-reply-preview branch 2 times, most recently from 2b994ce to c71342d Compare November 1, 2025 16:22
Base automatically changed from feat/images-modal to master November 2, 2025 14:04
@josefinalliende josefinalliende force-pushed the feat/media-reply-preview branch from c71342d to 2c77d05 Compare November 2, 2025 16:06
@josefinalliende josefinalliende marked this pull request as ready for review November 2, 2025 16:08
@josefinalliende josefinalliende force-pushed the feat/media-reply-preview branch from 2c77d05 to a131973 Compare November 2, 2025 16:45
@josefinalliende josefinalliende marked this pull request as draft November 2, 2025 16:45
@josefinalliende josefinalliende marked this pull request as ready for review November 2, 2025 16:48
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 (1)
test/ui/chat/widgets/message_reply_box_test.dart (1)

10-33: LGTM! Consistent test setup.

The test user and media file helper follow the same pattern as chat_input_reply_preview_test.dart, ensuring consistent testing approach across components.

The createTestMediaFile helper is duplicated between this file and chat_input_reply_preview_test.dart. Consider extracting it to a shared test utilities file for reusability:

// test/ui/chat/widgets/test_helpers.dart
MediaFile createTestMediaFile({required String id}) {
  return MediaFile(
    id: id,
    mlsGroupId: 'group-id',
    accountPubkey: 'pubkey',
    filePath: '/test/path/$id.jpg',
    fileHash: 'hash-$id',
    mimeType: 'image/jpeg',
    mediaType: 'image',
    blossomUrl: 'https://example.com/$id.jpg',
    nostrKey: 'key-$id',
    fileMetadata: const FileMetadata(),
    createdAt: DateTime.now(),
  );
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9af0cf5 and a131973.

📒 Files selected for processing (6)
  • lib/ui/chat/widgets/chat_input_reply_preview.dart (2 hunks)
  • lib/ui/chat/widgets/media_preview.dart (1 hunks)
  • lib/ui/chat/widgets/message_reply_box.dart (1 hunks)
  • lib/ui/chat/widgets/message_widget.dart (2 hunks)
  • test/ui/chat/widgets/chat_input_reply_preview_test.dart (1 hunks)
  • test/ui/chat/widgets/message_reply_box_test.dart (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.dart

📄 CodeRabbit inference engine (.cursor/rules/flutter.mdc)

**/*.dart: Always declare the type of each variable and function (parameters and return value)
Avoid using dynamic and Object without justification
Create necessary types instead of overusing primitives or dynamic
One export per file
Use PascalCase for classes
Use camelCase for variables, functions, and methods
Avoid magic numbers and define constants
Start each function name with a verb
Use verbs for boolean variables (e.g., isLoading, hasError, canDelete)
Write short functions with a single purpose (under ~20 instructions)
Name functions with a verb plus context; for booleans use isX/hasX/canX; for void use executeX/saveX
Avoid deep nesting via early returns and extraction to utility functions
Use higher-order functions (map, where/filter, reduce) to avoid nesting
Use arrow functions for simple functions (under ~3 statements); use named functions otherwise
Use default parameter values instead of null checks
Reduce function parameters using RO-RO: pass/return parameter objects with declared types
Maintain a single level of abstraction within functions
Encapsulate data in composite types; avoid overusing primitives
Prefer validating data within classes rather than in functions
Prefer immutability; use final for runtime constants and const for compile-time constants
Use const constructors and const literals where possible
Follow SOLID principles
Prefer composition over inheritance
Declare interfaces (abstract classes) to define contracts
Write small classes with a single purpose (under ~200 instructions, <10 public methods, <10 properties)
Use exceptions for unexpected errors
Only catch exceptions to fix expected problems or add context; otherwise use a global handler

Files:

  • lib/ui/chat/widgets/chat_input_reply_preview.dart
  • lib/ui/chat/widgets/message_reply_box.dart
  • lib/ui/chat/widgets/message_widget.dart
  • lib/ui/chat/widgets/media_preview.dart
  • test/ui/chat/widgets/message_reply_box_test.dart
  • test/ui/chat/widgets/chat_input_reply_preview_test.dart
lib/**/*.dart

📄 CodeRabbit inference engine (.cursor/rules/flutter.mdc)

lib/**/*.dart: Use flutter_rust_bridge to access core app functionality
Use Riverpod for state management; prefer StreamProviders for Rust API streams; use keepAlive if needed
Use freezed to model/manage UI states
Controllers should expose methods as inputs and update UI state that drives the UI
Use AutoRoute for navigation and use extras to pass data between pages
Use Dart extensions to manage reusable code
Use ThemeData to manage themes
Use AppLocalizations for translations
Use constants to manage constant values
Avoid deeply nested widget trees; aim for a flatter widget structure for performance and readability
Break down large widgets into smaller, focused, reusable components
Keep the widget tree shallow to simplify state management and data flow
Utilize const constructors and const widgets wherever possible to reduce rebuilds

Files:

  • lib/ui/chat/widgets/chat_input_reply_preview.dart
  • lib/ui/chat/widgets/message_reply_box.dart
  • lib/ui/chat/widgets/message_widget.dart
  • lib/ui/chat/widgets/media_preview.dart
test/**/*.dart

📄 CodeRabbit inference engine (.cursor/rules/flutter.mdc)

test/**/*.dart: Follow Arrange-Act-Assert in tests
Name test variables clearly using inputX, mockX, actualX, expectedX
Write unit tests for each public function; use test doubles for dependencies (except cheap third-party)
Use standard Flutter widget testing

Files:

  • test/ui/chat/widgets/message_reply_box_test.dart
  • test/ui/chat/widgets/chat_input_reply_preview_test.dart
🧠 Learnings (12)
📓 Common learnings
Learnt from: josefinalliende
Repo: parres-hq/whitenoise_flutter PR: 721
File: lib/ui/chat/widgets/chat_input.dart:80-87
Timestamp: 2025-10-15T02:08:41.843Z
Learning: In `lib/ui/chat/widgets/chat_input.dart`, the `_toggleMediaSelector()` method correctly reads `chatInputState` before toggling and checks `!chatInputState.showMediaSelector` to unfocus when opening the selector. This matches the design requirement where the input should be unfocused when the media selector is shown.
📚 Learning: 2025-10-15T02:08:41.843Z
Learnt from: josefinalliende
Repo: parres-hq/whitenoise_flutter PR: 721
File: lib/ui/chat/widgets/chat_input.dart:80-87
Timestamp: 2025-10-15T02:08:41.843Z
Learning: In `lib/ui/chat/widgets/chat_input.dart`, the `_toggleMediaSelector()` method correctly reads `chatInputState` before toggling and checks `!chatInputState.showMediaSelector` to unfocus when opening the selector. This matches the design requirement where the input should be unfocused when the media selector is shown.

Applied to files:

  • lib/ui/chat/widgets/chat_input_reply_preview.dart
  • lib/ui/chat/widgets/media_preview.dart
  • test/ui/chat/widgets/chat_input_reply_preview_test.dart
📚 Learning: 2025-08-23T11:02:28.308Z
Learnt from: Quwaysim
Repo: parres-hq/whitenoise_flutter PR: 527
File: lib/ui/core/ui/wn_avatar.dart:1-6
Timestamp: 2025-08-23T11:02:28.308Z
Learning: In the whitenoise_flutter codebase, flutter_screenutil is re-exported through lib/ui/core/themes/src/app_theme.dart, so files that import app_theme.dart do not need to directly import flutter_screenutil to use .w, .h, .sp extensions.

Applied to files:

  • lib/ui/chat/widgets/chat_input_reply_preview.dart
  • lib/ui/chat/widgets/message_widget.dart
📚 Learning: 2025-09-01T14:56:50.988Z
Learnt from: josefinalliende
Repo: parres-hq/whitenoise_flutter PR: 568
File: lib/ui/core/ui/wn_image.dart:1-2
Timestamp: 2025-09-01T14:56:50.988Z
Learning: The whitenoise_flutter project does not target web platforms and any future web version would be in a separate repository, so dart:io imports and file system operations are acceptable in this codebase.

Applied to files:

  • lib/ui/chat/widgets/chat_input_reply_preview.dart
📚 Learning: 2025-10-12T22:44:49.606Z
Learnt from: CR
Repo: parres-hq/whitenoise_flutter PR: 0
File: .cursor/rules/flutter.mdc:0-0
Timestamp: 2025-10-12T22:44:49.606Z
Learning: Applies to test/**/*.dart : Use standard Flutter widget testing

Applied to files:

  • lib/ui/chat/widgets/chat_input_reply_preview.dart
  • test/ui/chat/widgets/message_reply_box_test.dart
  • test/ui/chat/widgets/chat_input_reply_preview_test.dart
📚 Learning: 2025-10-12T22:44:49.606Z
Learnt from: CR
Repo: parres-hq/whitenoise_flutter PR: 0
File: .cursor/rules/flutter.mdc:0-0
Timestamp: 2025-10-12T22:44:49.606Z
Learning: Applies to lib/**/*.dart : Use Dart extensions to manage reusable code

Applied to files:

  • lib/ui/chat/widgets/chat_input_reply_preview.dart
📚 Learning: 2025-10-12T22:44:49.606Z
Learnt from: CR
Repo: parres-hq/whitenoise_flutter PR: 0
File: .cursor/rules/flutter.mdc:0-0
Timestamp: 2025-10-12T22:44:49.606Z
Learning: Applies to lib/**/*.dart : Use freezed to model/manage UI states

Applied to files:

  • lib/ui/chat/widgets/chat_input_reply_preview.dart
📚 Learning: 2025-09-24T17:59:53.850Z
Learnt from: josefinalliende
Repo: parres-hq/whitenoise_flutter PR: 688
File: lib/ui/contact_list/chat_list_screen.dart:284-293
Timestamp: 2025-09-24T17:59:53.850Z
Learning: In Flutter apps using Riverpod, complex list processing logic (sorting, filtering, separating) should be extracted to dedicated provider classes rather than embedded in widget build methods. This improves testability, maintainability, and separation of concerns. Create providers like ChatListProcessor to handle business logic and keep widgets focused on UI rendering.

Applied to files:

  • lib/ui/chat/widgets/message_widget.dart
📚 Learning: 2025-10-28T07:11:05.836Z
Learnt from: Quwaysim
Repo: parres-hq/whitenoise_flutter PR: 759
File: lib/ui/settings/profile/edit_profile_screen.dart:126-145
Timestamp: 2025-10-28T07:11:05.836Z
Learning: In lib/ui/settings/profile/edit_profile_screen.dart, the edit icon on the profile avatar is intentionally positioned with `left: 1.sw * 0.5` rather than being centered. This positioning is by design and should not be flagged as a centering issue.

Applied to files:

  • lib/ui/chat/widgets/media_preview.dart
📚 Learning: 2025-10-12T22:44:49.606Z
Learnt from: CR
Repo: parres-hq/whitenoise_flutter PR: 0
File: .cursor/rules/flutter.mdc:0-0
Timestamp: 2025-10-12T22:44:49.606Z
Learning: Applies to test/**/*.dart : Write unit tests for each public function; use test doubles for dependencies (except cheap third-party)

Applied to files:

  • test/ui/chat/widgets/message_reply_box_test.dart
  • test/ui/chat/widgets/chat_input_reply_preview_test.dart
📚 Learning: 2025-10-12T22:44:49.606Z
Learnt from: CR
Repo: parres-hq/whitenoise_flutter PR: 0
File: .cursor/rules/flutter.mdc:0-0
Timestamp: 2025-10-12T22:44:49.606Z
Learning: Applies to test/**/*.dart : Follow Arrange-Act-Assert in tests

Applied to files:

  • test/ui/chat/widgets/message_reply_box_test.dart
  • test/ui/chat/widgets/chat_input_reply_preview_test.dart
📚 Learning: 2025-10-12T22:44:49.606Z
Learnt from: CR
Repo: parres-hq/whitenoise_flutter PR: 0
File: .cursor/rules/flutter.mdc:0-0
Timestamp: 2025-10-12T22:44:49.606Z
Learning: Applies to integration_test/**/*.dart : Use integration tests for each API module

Applied to files:

  • test/ui/chat/widgets/chat_input_reply_preview_test.dart
🔇 Additional comments (19)
lib/ui/chat/widgets/media_preview.dart (1)

93-93: LGTM! Padding adjustments align with design.

The reduced horizontal and vertical padding values improve the visual alignment for media previews in reply contexts.

test/ui/chat/widgets/chat_input_reply_preview_test.dart (4)

10-33: LGTM! Well-structured test setup.

The test user and media file helper are properly defined with clear naming and all required fields.


35-88: LGTM! Comprehensive coverage for messages without media.

The test group properly validates that reply previews without media display user name and content without showing a media tile.


90-145: LGTM! Proper validation for single media attachment.

The test group correctly verifies that reply previews with one media attachment display both text content and the media tile.


147-220: LGTM! Excellent validation for multiple media attachments.

The test group properly verifies that when multiple media attachments exist, exactly one media tile is shown and it's specifically the first attachment (lines 205-219). This directly validates the PR objective.

lib/ui/chat/widgets/message_widget.dart (2)

12-12: LGTM! Clean import of extracted component.

The import correctly references the new MessageReplyBox widget from its dedicated file.


132-136: LGTM! Proper usage of extracted MessageReplyBox.

The component replacement maintains the same API contract with replyingTo and onTap parameters.

lib/ui/chat/widgets/chat_input_reply_preview.dart (4)

5-5: LGTM! Import supports media tile rendering.

The MessageMediaTile import is necessary for displaying media attachments in the reply preview.


40-50: LGTM! Media tile properly renders first attachment.

The horizontal layout correctly displays the first media attachment when available. The conditional check ensures mediaAttachments.isNotEmpty before accessing .first (line 46), preventing runtime errors.


51-82: LGTM! Well-structured text content layout.

The nested Expanded/Row/Column structure provides proper flexible sizing, and text content correctly handles null cases with localized fallbacks. The single-line truncation with ellipsis ensures the preview doesn't overflow.


83-96: LGTM! Cancel button properly implemented.

The close icon is correctly wired to the onCancel callback with proper sizing and theming.

lib/ui/chat/widgets/message_reply_box.dart (5)

11-14: LGTM! Clean widget API.

The MessageReplyBox properly extends ConsumerWidget with well-typed optional parameters for flexibility.


17-22: LGTM! Proper localization handling and early return.

The widget correctly watches locale changes for dynamic updates and efficiently returns an empty widget when there's no reply context.


23-38: LGTM! Proper Material Design hierarchy.

The widget structure (Material > InkWell > Container) correctly enables tap feedback while maintaining proper styling with theme colors and responsive sizing.


39-49: LGTM! Consistent media tile implementation.

The media rendering correctly displays the first attachment when available, matching the implementation in chat_input_reply_preview.dart with consistent sizing (32.w).


50-77: LGTM! Well-structured text content display.

The text layout properly uses MessageUtils.getDisplayName for consistent name display and handles null content gracefully. Single-line truncation prevents overflow.

test/ui/chat/widgets/message_reply_box_test.dart (3)

35-88: LGTM! Thorough coverage for messages without media.

The test group properly validates MessageReplyBox behavior when no media attachments are present, ensuring user name and content display correctly without a media tile.


90-145: LGTM! Proper validation for single media attachment.

The test group correctly verifies that MessageReplyBox displays both text content and the media tile when one attachment is present.


147-220: LGTM! Excellent validation for multiple media attachments.

The test group thoroughly validates that MessageReplyBox shows exactly one media tile for the first attachment when multiple exist (lines 205-219), directly confirming the PR objective.

Copy link
Contributor

@untreu2 untreu2 left a comment

Choose a reason for hiding this comment

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

LGTM

@josefinalliende josefinalliende merged commit b39a6a4 into master Nov 2, 2025
2 checks passed
@josefinalliende josefinalliende deleted the feat/media-reply-preview branch November 2, 2025 21:39
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.

Show first image in replies

3 participants