Skip to content

feat: render timestamp markdown#6266

Merged
diegolmello merged 36 commits intoRocketChat:developfrom
Rohit3523:timestamp-markdown
Jan 7, 2026
Merged

feat: render timestamp markdown#6266
diegolmello merged 36 commits intoRocketChat:developfrom
Rohit3523:timestamp-markdown

Conversation

@Rohit3523
Copy link
Copy Markdown
Contributor

@Rohit3523 Rohit3523 commented Mar 25, 2025

Proposed changes

This PR adds a function to render the timestamp message in the mobile app, aligning it with the web version. Previously, the timestamp was missing on mobile causing it to render blank message. This update ensures that users see the timestamp message correctly across both web and mobile.

Issue(s)

https://rocketchat.atlassian.net/browse/CORE-1637

How to test or reproduce

Screenshots

Before After
Screenshot 2025-11-05 at 12 33 20 AM Screenshot 2025-11-05 at 2 41 10 AM

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 Comment

I am using the date and time format given in https://github.com/RocketChat/google-summer-of-code/blob/main/google-summer-of-code-2025.md#-message-timestamp-date-picker-components
Screenshot 2025-03-25 at 8 55 57 PM

Summary by CodeRabbit

  • New Features
    • Added timestamp rendering support with multiple format options (7 different styles).
    • Timestamps are now interactive and provide feedback when pressed.
    • Improved spacing alignment in markdown layouts when timestamps are present.

@Rohit3523 Rohit3523 marked this pull request as ready for review March 25, 2025 15:24
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Nov 4, 2025

Walkthrough

This PR introduces a new Timestamp component for rendering timestamps in markdown with support for seven format options (t, T, d, D, f, F, R). The component is integrated into Inline and Paragraph markdown containers with appropriate styling adjustments. It uses moment.js for date formatting and emits events on user interactions.

Changes

Cohort / File(s) Summary
New Timestamp Component
app/containers/markdown/components/Timestamp.tsx
New React Native component that renders formatted timestamps with memoized moment.js formatting logic. Supports seven format specifiers and emits EventEmitter events on press interactions. Includes theme-driven styling and invalid date fallback.
Markdown Container Integration
app/containers/markdown/components/Inline.tsx, app/containers/markdown/components/Paragraph.tsx
Added TIMESTAMP block type handling in both containers with Timestamp component imports. Paragraph component includes conditional marginTop spacing adjustment based on TIMESTAMP type presence.

Sequence Diagram

sequenceDiagram
    participant Markdown as Markdown Parser
    participant Inline as Inline.tsx
    participant Paragraph as Paragraph.tsx
    participant Timestamp as Timestamp Component
    participant Theme as Theme/Styling
    participant EventEmitter as EventEmitter

    Markdown->>Inline: Render TIMESTAMP block
    Inline->>Timestamp: value={block.value}
    activate Timestamp
    Timestamp->>Timestamp: Compute formatted date (memoized)
    Timestamp->>Theme: Get theme colors
    Theme-->>Timestamp: bgColor, fontColor
    Timestamp->>Timestamp: Render Text with formatting
    deactivate Timestamp
    Timestamp-->>Inline: Rendered timestamp UI
    
    Note over Timestamp: On user press
    Timestamp->>EventEmitter: emit(LISTENER, {message})
    EventEmitter->>EventEmitter: Trigger feedback
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Timestamp.tsx: Verify moment.js format specifiers are correctly mapped; validate the memoized formatting logic and event emission pattern; ensure theme color integration is consistent
  • Paragraph.tsx: Confirm marginTop conditional logic doesn't introduce unexpected spacing in edge cases; validate TIMESTAMP detection logic doesn't conflict with existing LINK/PLAIN_TEXT handling
  • Inline.tsx: Simple integration; verify block.value structure matches Timestamp component expectations

Poem

🐰 Hop, hop! The timestamps now shine bright,
Seven formats, moment-crafted just right,
From t to R, all the clock-shows align,
No AM/PM confusion this time!
Press them, they speak—events defined!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR adds timestamp rendering components but does not address the core coding requirement from issue #5501: fixing the incorrect 12-hour AM/PM time format to respect device 24-hour locale settings. Implement locale-aware time formatting that respects system 24-hour settings. Currently the code doesn't validate if moment.js is configured to use device locale settings for the time format.
✅ Passed checks (3 passed)
Check name Status Explanation
Out of Scope Changes check ✅ Passed Changes are limited to adding Timestamp component and integration into Inline and Paragraph components, all directly supporting the timestamp rendering feature.
Title check ✅ Passed The title 'feat: render timestamp markdown' directly and clearly summarizes the main change—adding timestamp rendering support to the markdown parser.
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

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
Copy Markdown
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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/containers/markdown/components/Inline.tsx (1)

25-79: Add key props to all components returned from the map.

The map iteration starting at line 25 returns components from the switch statement without key props, causing React to be unable to efficiently track changes. Biome correctly flagged this for the new TIMESTAMP case (line 75), but the issue affects all switch cases.

Each component returned from the switch should include a key prop. Apply this pattern to all cases:

 		<Text style={styles.inline}>
 			{value.map((block, index) => {
 				// ... forceTrim logic ...
 
 				switch (block.type) {
 					case 'IMAGE':
-						return <Image value={block.value} />;
+						return <Image key={index} value={block.value} />;
 					case 'PLAIN_TEXT':
-						return <Plain value={block.value} />;
+						return <Plain key={index} value={block.value} />;
 					case 'BOLD':
-						return <Bold value={block.value} />;
+						return <Bold key={index} value={block.value} />;
 					case 'STRIKE':
-						return <Strike value={block.value} />;
+						return <Strike key={index} value={block.value} />;
 					case 'ITALIC':
-						return <Italic value={block.value} />;
+						return <Italic key={index} value={block.value} />;
 					case 'LINK':
-						return <Link value={block.value} />;
+						return <Link key={index} value={block.value} />;
 					case 'MENTION_USER':
 						return (
-							<AtMention
+							<AtMention key={index}
 								mention={block.value.value}
 								useRealName={useRealName}
 								username={username}
 								navToRoomInfo={navToRoomInfo}
 								mentions={mentions}
 							/>
 						);
 					case 'EMOJI':
-						return <Emoji block={block} />;
+						return <Emoji key={index} block={block} />;
 					case 'MENTION_CHANNEL':
-						return <Hashtag hashtag={block.value.value} navToRoomInfo={navToRoomInfo} channels={channels} />;
+						return <Hashtag key={index} hashtag={block.value.value} navToRoomInfo={navToRoomInfo} channels={channels} />;
 					case 'INLINE_CODE':
-						return <InlineCode value={block.value} />;
+						return <InlineCode key={index} value={block.value} />;
 					case 'INLINE_KATEX':
-						return <Text>{block.value}</Text>;
+						return <Text key={index}>{block.value}</Text>;
 					case 'TIMESTAMP':
-						return <Timestamp value={block.value} />;
+						return <Timestamp key={index} value={block.value} />;
 					default:
 						return null;
 				}
 			})}
 		</Text>

Note: While using index as a key is acceptable here since the order of inline markdown elements is stable and won't be reordered, if these elements could be reordered or filtered, consider using a more stable identifier.

♻️ Duplicate comments (4)
app/containers/markdown/components/Paragraph.tsx (2)

36-42: Clarify the use case and add stories for this implementation.

This early return handles timestamps at the paragraph start, but the use case and test coverage remain unclear.

As noted in the previous review: What's the specific use case that requires this early return? Please add Storybook stories to demonstrate the TIMESTAMP rendering behavior.


45-45: Optimize the type check and clarify the margin logic.

The inline map operation is inefficient and the marginTop logic needs clarification:

  1. value?.map(x=> x.type).includes('TIMESTAMP') creates an unnecessary intermediate array. Use some instead: value?.some(x => x.type === 'TIMESTAMP')
  2. Missing space in arrow function: x=> should be x =>
  3. The logic is confusing: lines 36-42 early return when value[0] is TIMESTAMP, so this marginTop only applies when TIMESTAMP appears at a non-zero index. Please clarify why mixed content with TIMESTAMP requires different spacing.

Apply this diff to optimize the type check:

-		<Text style={[styles.text, { color: themes[theme].fontDefault, marginTop: value?.map(x=> x.type).includes('TIMESTAMP') ? 2 : 0 }]}>
+		<Text style={[styles.text, { color: themes[theme].fontDefault, marginTop: value?.some(x => x.type === 'TIMESTAMP') ? 2 : 0 }]}>

Note: The previous reviewer asked "Why do you have to add this margin top?" - please provide justification for this spacing adjustment.

app/containers/markdown/components/Timestamp.tsx (2)

56-60: Add font styling to match the web version.

The component is missing font styling (family, weight) that should align with the web implementation for consistency across platforms.

As noted in the previous review: Please verify the web version's font styling and apply the same here. Consider extracting these styles to a StyleSheet for better performance instead of using inline styles.

#!/bin/bash
# Description: Find timestamp styling in web codebase to ensure mobile matches

# Search for timestamp-related styles in the web codebase
rg -n -C3 "timestamp.*style|Timestamp.*css" --type-add 'web:*.{tsx,ts,css,scss}' -t web

51-54: Extract the duplicate timestamp calculation.

The timestamp calculation parseInt(value.timestamp) * 1000 is duplicated on lines 18 and 52, violating DRY principles.

As noted in the previous review, extract this to a memoized constant:

     const formatDate = React.useMemo(()=>{
         const timestamp = parseInt(value.timestamp) * 1000;
         // ... formatting logic
     }, [value]);
+
+    const timestampMs = React.useMemo(() => parseInt(value.timestamp) * 1000, [value.timestamp]);

     const handlePress = () => {
-        const message = moment(parseInt(value.timestamp) * 1000).format('dddd, MMM DD, YYYY hh:mm A');
+        const message = moment(timestampMs).format('LLL');
         EventEmitter.emit(LISTENER, { message });
     }

Note: Also updated the format string to 'LLL' to use locale-aware formatting (see previous comment).

🧹 Nitpick comments (1)
app/containers/markdown/components/Timestamp.tsx (1)

20-46: Consider using a switch statement for better readability.

The sequence of seven if statements would be more readable and maintainable as a switch statement.

Apply this diff:

     const formatDate = React.useMemo(()=>{
         const timestamp = parseInt(value.timestamp) * 1000;
 
-        if(value.format === "t"){
-            return moment(timestamp).format('LT');
-        }
-
-        if(value.format === "T"){
-            return moment(timestamp).format('LTS');
-        }
-
-        if(value.format === "d"){
-            return moment(timestamp).format('L');
-        }
-
-        if(value.format === "D"){
-            return moment(timestamp).format('LL');
-        }
-
-        if(value.format === "f"){
-            return moment(timestamp).format('LLL');
-        }
-
-        if(value.format === "F"){
-            return moment(timestamp).format('LLLL');
-        }
-
-        if(value.format === "R"){
-            return moment(timestamp).fromNow();
-        }
-
-        return "Invalid Date";
+        switch(value.format) {
+            case "t":
+                return moment(timestamp).format('LT');
+            case "T":
+                return moment(timestamp).format('LTS');
+            case "d":
+                return moment(timestamp).format('L');
+            case "D":
+                return moment(timestamp).format('LL');
+            case "f":
+                return moment(timestamp).format('LLL');
+            case "F":
+                return moment(timestamp).format('LLLL');
+            case "R":
+                return moment(timestamp).fromNow();
+            default:
+                return "Invalid Date";
+        }
     }, [value]);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira 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 a4875e1 and 473c231.

📒 Files selected for processing (3)
  • app/containers/markdown/components/Inline.tsx (2 hunks)
  • app/containers/markdown/components/Paragraph.tsx (2 hunks)
  • app/containers/markdown/components/Timestamp.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
app/containers/markdown/components/Timestamp.tsx (2)
app/theme.tsx (1)
  • useTheme (29-29)
app/containers/Toast.tsx (1)
  • LISTENER (21-21)
🪛 Biome (2.1.2)
app/containers/markdown/components/Inline.tsx

[error] 75-75: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.

(lint/correctness/useJsxKeyInIterable)

🪛 ESLint
app/containers/markdown/components/Timestamp.tsx

[error] 5-5: 'styles' is defined but never used.

(@typescript-eslint/no-unused-vars)


[error] 7-7: Useless path segments for "../../../containers/Toast", should be "../../Toast"

(import/no-useless-path-segments)

🔇 Additional comments (1)
app/containers/markdown/components/Inline.tsx (1)

13-13: LGTM!

The Timestamp import is correctly placed and follows the existing import pattern.

@Rohit3523 Rohit3523 requested a review from diegolmello November 4, 2025 16:10
@Rohit3523 Rohit3523 self-assigned this Nov 4, 2025
@Rohit3523 Rohit3523 marked this pull request as draft November 4, 2025 16:33
@Rohit3523
Copy link
Copy Markdown
Contributor Author

Requesting a review :)

@Rohit3523 Rohit3523 had a problem deploying to experimental_ios_build December 11, 2025 17:29 — with GitHub Actions Error
@Rohit3523 Rohit3523 had a problem deploying to experimental_android_build December 11, 2025 17:29 — with GitHub Actions Error
@Rohit3523 Rohit3523 had a problem deploying to official_android_build December 11, 2025 17:29 — with GitHub Actions Error
@diegolmello diegolmello changed the title feature: render timestamp markdown feat: render timestamp markdown Dec 12, 2025
@Rohit3523 Rohit3523 had a problem deploying to approve_e2e_testing January 7, 2026 15:08 — with GitHub Actions Failure
@Rohit3523 Rohit3523 had a problem deploying to official_android_build January 7, 2026 15:11 — with GitHub Actions Failure
@Rohit3523 Rohit3523 requested a deployment to experimental_android_build January 7, 2026 15:11 — with GitHub Actions Waiting
@Rohit3523 Rohit3523 had a problem deploying to experimental_ios_build January 7, 2026 15:11 — with GitHub Actions Failure
@diegolmello diegolmello merged commit b990e60 into RocketChat:develop Jan 7, 2026
4 of 9 checks passed
diegolmello pushed a commit that referenced this pull request Jan 8, 2026
@diegolmello diegolmello mentioned this pull request Jan 8, 2026
10 tasks
diegolmello pushed a commit that referenced this pull request Jan 8, 2026
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.

2 participants