Skip to content

chore: File attachment readability improvement#7051

Open
divyanshu-patil wants to merge 11 commits intoRocketChat:developfrom
divyanshu-patil:chore/pdf_message
Open

chore: File attachment readability improvement#7051
divyanshu-patil wants to merge 11 commits intoRocketChat:developfrom
divyanshu-patil:chore/pdf_message

Conversation

@divyanshu-patil
Copy link

@divyanshu-patil divyanshu-patil commented Mar 12, 2026

Proposed changes

this pr adds the pdf icon beside the attachment title if the file format is PDF to improve readability

Issue(s)

How to test or reproduce

  • go to any room
  • upload a pdf
  • view the pdf

Screenshots

Before After
image image

Light

image

Dark Dark

image

Dark Black

image

Types of changes

  • Bugfix (non-breaking change which fixes an issue)
  • Improvement (non-breaking change which improves a current function)
  • New feature (non-breaking change which adds functionality)
  • Documentation update (if none of the other choices apply)

Checklist

  • I have read the CONTRIBUTING doc
  • I have signed the CLA
  • Lint and unit tests pass locally with my changes
  • I have added tests that prove my fix is effective or that my feature works (if applicable)
  • I have added necessary documentation (if applicable)
  • Any dependent changes have been merged and published in downstream modules

Further comments

if want to add icon support for more document format but not sure how to edit custom.ttf

Summary by CodeRabbit

  • New Features
    • Document/file attachments show a file-type icon next to the title and display a human-readable file size when no description is present.
  • Style
    • Attachment rows now have improved spacing, alignment, and dedicated icon/size styling.
  • Performance
    • Reduced unnecessary re-renders for attachment descriptions for smoother UI.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 12, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds three utility functions (formatSize, getFileIcon, isDocumentFile) and updates the Reply attachment UI to detect document attachments, render a file icon, show formatted file size for document-only attachments, adjust attachment row layout/styling, and refine memoization.

Changes

Cohort / File(s) Summary
New utilities
app/lib/methods/formatSize.ts, app/lib/methods/getFileIcon.ts, app/lib/methods/isDocumentFile.ts
Added formatSize(bytes, decimals) to produce human-readable sizes (Bytes → TB, 1024 base), getFileIcon(format) mapping common document formats to icon names, and isDocumentFile(attachment) that returns true for file attachments with formats: pdf, xlsx, xls, csv, pptx, ppt, docx, doc.
Attachment Display UI
app/containers/message/Components/Attachments/Reply.tsx
Updated layout styles (alignItems: 'center', gap: 8), added fileIconContainer and fileSizeText styles, detect document files via isDocumentFile, render inline document icon (via CustomIcon + getFileIcon) before title when present, show formatted size (formatSize) instead of MarkdownPreview for file-only documents, use theme colors, and tighten memoization checks to include attachment.format and attachment.size.
Minor
app/containers/message/Components/Attachments/Reply.tsx
Inserted a debug console.log for attachment during rendering.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'chore: File attachment readability improvement' accurately describes the main objective of the PR, which adds file icons and size formatting to document attachments.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

📝 Coding Plan
  • Generate coding plan for human review comments

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: 1

🧹 Nitpick comments (4)
app/containers/message/Components/Attachments/Reply.tsx (3)

164-167: Simplify redundant ternary expression.

On line 165, the isDocument ? formatSize(attachment.size ?? 0, 2) : text ternary is inside a block that already guarantees isDocument is true (from the condition on line 164). The : text branch is unreachable.

♻️ Proposed simplification
 	if (isFileName && isDocument) {
-		const newText = isDocument ? formatSize(attachment.size ?? 0, 2) : text;
-		return <Text style={[styles.fileSizeText, { color: colors.fontSecondaryInfo }]}>{newText}</Text>;
+		return <Text style={[styles.fileSizeText, { color: colors.fontSecondaryInfo }]}>{formatSize(attachment.size ?? 0, 2)}</Text>;
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/containers/message/Components/Attachments/Reply.tsx` around lines 164 -
167, The ternary inside the block guarded by if (isFileName && isDocument) is
redundant; in the Reply component replace the unreachable conditional expression
in the newText assignment (isDocument ? formatSize(attachment.size ?? 0, 2) :
text) with a direct call to formatSize(attachment.size ?? 0, 2) so newText is
assigned the formatted size and the returned <Text> (styles.fileSizeText)
displays that value; update the code using the existing identifiers newText,
formatSize, and attachment accordingly.

174-185: Memo comparison may be incomplete for new dependencies.

The Description component now depends on attachment.format and attachment.size (via isDocumentFile and formatSize), but the custom comparison function doesn't check these fields. If these values change, the component won't re-render.

This is likely fine in practice since format and size are typically immutable after attachment creation, but worth noting for correctness.

💡 Proposed update to memo comparison
 	(prevProps, nextProps) => {
 		if (prevProps.attachment.text !== nextProps.attachment.text) {
 			return false;
 		}
 		if (prevProps.attachment.title !== nextProps.attachment.title) {
 			return false;
 		}
 		if (prevProps.attachment.type !== nextProps.attachment.type) {
 			return false;
 		}
+		if (prevProps.attachment.format !== nextProps.attachment.format) {
+			return false;
+		}
+		if (prevProps.attachment.size !== nextProps.attachment.size) {
+			return false;
+		}
 		return true;
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/containers/message/Components/Attachments/Reply.tsx` around lines 174 -
185, The memo comparator for the Description/Reply attachment component only
compares attachment.text, title, and type, so changes to attachment.format or
attachment.size (used by isDocumentFile/formatSize) won't trigger re-renders;
update the comparator (the anonymous function with signature (prevProps,
nextProps) => { ... }) to also compare attachment.format and attachment.size (or
replace the three-field checks with a shallow comparison of the attachment
object) so updates to those fields cause the component to rerender.

241-243: Consolidate duplicate useTheme() calls.

useTheme() is called twice: line 241 for colors and line 243 for theme. These can be combined into a single destructuring.

♻️ Proposed consolidation
-	const { colors } = useTheme();
 	const [loading, setLoading] = useState(false);
-	const { theme } = useTheme();
+	const { theme, colors } = useTheme();
 	const { baseUrl, user, id, e2e, isEncrypted } = useContext(MessageContext);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/containers/message/Components/Attachments/Reply.tsx` around lines 241 -
243, Consolidate the duplicate useTheme() calls by replacing the two separate
calls with a single destructure: call useTheme() once and extract both colors
and theme (e.g., const { colors, theme } = useTheme()); remove the extra
useTheme() invocation and update any references to colors and theme in the Reply
component so they use the values from the single destructured object (look for
useTheme, colors, and theme in Reply.tsx).
app/lib/methods/getFileIcon.ts (1)

3-8: Consider case-insensitive extension matching.

The .endsWith('.pdf') check is case-sensitive, so files with uppercase extensions like test.PDF won't match. This is a minor edge case but could be worth handling for robustness.

💡 Suggested improvement
 export const getFileIcon = (filename: string): TIconsName => {
-	if (filename.endsWith('.pdf')) return 'adobe-reader-monochromatic';
+	const lowerFilename = filename.toLowerCase();
+	if (lowerFilename.endsWith('.pdf')) return 'adobe-reader-monochromatic';
 	// if (filename.endsWith('.doc') || filename.endsWith('.docx')) return 'another';
 	// if (filename.endsWith('.xls') || filename.endsWith('.xlsx')) return 'another';
 	// if (filename.endsWith('.zip')) return 'another';
 	return 'pin';
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/lib/methods/getFileIcon.ts` around lines 3 - 8, getFileIcon currently
uses a case-sensitive filename.endsWith('.pdf') check which misses uppercase
extensions like "TEST.PDF"; update the function (getFileIcon, parameter
filename) to normalize the filename (e.g., convert to lowercase or use a
case-insensitive comparison) before performing endsWith checks so '.pdf' matches
regardless of case and apply the same normalization approach when you add other
extension checks.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/lib/methods/formatSize.ts`:
- Around line 1-11: The formatSize function can return NaN/undefined for
negative bytes; update the guard at the top of formatSize to handle non-positive
or invalid inputs (e.g., if bytes == null || bytes <= 0 return '0 Bytes') and
then clamp the computed index i to the valid range of sizes (use
Math.min(Math.max(i, 0), sizes.length - 1)) before using sizes[i]; this ensures
bytes, i, and sizes are safe and avoids NaN/undefined results.

---

Nitpick comments:
In `@app/containers/message/Components/Attachments/Reply.tsx`:
- Around line 164-167: The ternary inside the block guarded by if (isFileName &&
isDocument) is redundant; in the Reply component replace the unreachable
conditional expression in the newText assignment (isDocument ?
formatSize(attachment.size ?? 0, 2) : text) with a direct call to
formatSize(attachment.size ?? 0, 2) so newText is assigned the formatted size
and the returned <Text> (styles.fileSizeText) displays that value; update the
code using the existing identifiers newText, formatSize, and attachment
accordingly.
- Around line 174-185: The memo comparator for the Description/Reply attachment
component only compares attachment.text, title, and type, so changes to
attachment.format or attachment.size (used by isDocumentFile/formatSize) won't
trigger re-renders; update the comparator (the anonymous function with signature
(prevProps, nextProps) => { ... }) to also compare attachment.format and
attachment.size (or replace the three-field checks with a shallow comparison of
the attachment object) so updates to those fields cause the component to
rerender.
- Around line 241-243: Consolidate the duplicate useTheme() calls by replacing
the two separate calls with a single destructure: call useTheme() once and
extract both colors and theme (e.g., const { colors, theme } = useTheme());
remove the extra useTheme() invocation and update any references to colors and
theme in the Reply component so they use the values from the single destructured
object (look for useTheme, colors, and theme in Reply.tsx).

In `@app/lib/methods/getFileIcon.ts`:
- Around line 3-8: getFileIcon currently uses a case-sensitive
filename.endsWith('.pdf') check which misses uppercase extensions like
"TEST.PDF"; update the function (getFileIcon, parameter filename) to normalize
the filename (e.g., convert to lowercase or use a case-insensitive comparison)
before performing endsWith checks so '.pdf' matches regardless of case and apply
the same normalization approach when you add other extension checks.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f1889740-9c81-41c8-8209-c4d30e84367f

📥 Commits

Reviewing files that changed from the base of the PR and between 2e46ce6 and ea478f1.

⛔ Files ignored due to path filters (1)
  • app/containers/message/__snapshots__/Message.test.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (3)
  • app/containers/message/Components/Attachments/Reply.tsx
  • app/lib/methods/formatSize.ts
  • app/lib/methods/getFileIcon.ts
📜 Review details
🔇 Additional comments (2)
app/containers/message/Components/Attachments/Reply.tsx (2)

116-124: LGTM!

The isDocumentFile helper is well-structured using a switch statement that makes it easy to extend for additional document formats in the future.


283-287: LGTM!

The PDF icon rendering is well-implemented with proper conditional checks and theme-aware styling. The icon display integrates cleanly with the existing attachment layout.

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: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/containers/message/Components/Attachments/Reply.tsx`:
- Line 247: Remove the render-time console.log that prints the attachment object
in the Reply component: locate the console.log(attachment,
'============================') inside the Reply component (or the render
function/method that uses the attachment prop) and delete it so attachments are
no longer logged on every render; if debugging info is still needed, replace
with a debug-only guard (e.g., conditional on a dev flag) or use a
non-production logger instead.
- Around line 282-285: The icon is chosen from attachment.title but document
detection uses attachment.format, so change the icon source to use
attachment.format (not title) when calling getFileIcon inside the conditional
that checks isDocumentFile(attachment) — e.g., pass attachment.format (or a
normalized extension derived from it) to getFileIcon in the JSX where
attachment.type === 'file' && isDocumentFile(attachment). Also update
app/lib/methods/getFileIcon.ts to accept plain format values like "pdf" or "csv"
(and fallback from dotted filenames) so getFileIcon(format) returns the correct
icon for formats as well as filenames.
- Around line 155-158: The reply preview currently shows "0 B" when
attachment.size is missing because formatSize is called with 0; update the
rendering in Reply.tsx (the component handling isFileName && isDocument) to only
call and render formatSize(attachment.size, 2) when attachment.size is not
null/undefined (e.g., attachment.size != null); if size is absent, render
nothing (omit the Text node) or an empty/placeholder string so unknown sizes are
not shown as "0 B".
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: abc2765a-abf3-4175-b10e-09a04f6696f6

📥 Commits

Reviewing files that changed from the base of the PR and between c892c17 and d846e80.

📒 Files selected for processing (3)
  • app/containers/message/Components/Attachments/Reply.tsx
  • app/lib/methods/getFileIcon.ts
  • app/lib/methods/isDocumentFile.ts

@divyanshu-patil divyanshu-patil changed the title chore: PDF attachment readability improvement chore: File attachment readability improvement Mar 12, 2026
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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/containers/message/Components/Attachments/Reply.tsx`:
- Around line 153-156: isDocumentFile(attachment) currently ignores the
filename-suffix fallback used for attachmentFormat, so attachments with no
IAttachment.format (e.g., report.pdf) miss the document icon and size label;
update the code to resolve the effective format first (use attachmentFormat
which falls back to the title/file suffix) and then check that resolved format
in isDocumentFile (or modify isDocumentFile to accept/compute the same
fallback). Apply the same change for the other occurrence that checks documents
so both icon and size label use the unified fallback.
- Around line 155-156: The current conditional in Reply.tsx (around isFileName
and isDocument checks) uses truthiness for attachment.size which hides valid
zero-length files; change the guard to explicitly check for null/undefined
(e.g., attachment.size != null or typeof attachment.size === 'number') so that
size === 0 is rendered by the Text component using formatSize; update the
condition that includes isFileName and isDocument to use this explicit null
check.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3e50e277-40aa-46cc-bf64-86dadd306de5

📥 Commits

Reviewing files that changed from the base of the PR and between d846e80 and 8d97961.

📒 Files selected for processing (2)
  • app/containers/message/Components/Attachments/Reply.tsx
  • app/lib/methods/getFileIcon.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/lib/methods/getFileIcon.ts
📜 Review details
🔇 Additional comments (1)
app/containers/message/Components/Attachments/Reply.tsx (1)

175-180: Nice memoization update.

Including format and size here prevents the memoized description from getting stuck with stale document metadata when attachment details hydrate after the first render.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant