Skip to content

poc: Virtua message list#40105

Draft
MartinSchoeler wants to merge 23 commits intodevelopfrom
chore/poc-virtua
Draft

poc: Virtua message list#40105
MartinSchoeler wants to merge 23 commits intodevelopfrom
chore/poc-virtua

Conversation

@MartinSchoeler
Copy link
Copy Markdown
Member

@MartinSchoeler MartinSchoeler commented Apr 9, 2026

Proposed changes (including videos or screenshots)

Migrates the main message list to a virtualized solution to improve performance

I'm using this storybook as an example/guide https://inokawa.github.io/virtua/?path=/story/advanced-chat--default

TODO:

  • Virtualize Message List
  • Adjust Load more
  • Adjust Jump to Bottom on message sent
  • Adjust Send/Receive Message
  • Adjust Detect is at bottom
  • Adjust Date Bubbles
  • Adjust Unread handling
  • Adjust Restore Scroll Position (Loaded Messages)
  • (wontfix, this does not work with existing implementation) Adjust Restore Scroll Position (unLoaded Messages)
  • Adjust Jump to message
  • Adjust Has new message
  • Adjust Keyboard Navigation
  • Implement 🔝 in threads

Possible next steps:

  • Move all scroll eventlisteners to virtua's onscroll

Existing issues (that will not be fixed on this PR):

  • If you jump to an older message, and someone sends a new message, the message will be inserted into the list and you wont be able to scroll to load more

Issue(s)

Summary by CodeRabbit

  • New Features

    • Virtualized message list rendering for better performance
    • Deep-link handling for ?msg= (channel and thread targets)
    • New custom virtual scrollbars component
  • Bug Fixes

    • More reliable new-message / scroll-to-bottom behavior and jump handling
    • Improved unread-divider detection and date-scroll handling
    • Persistent scroll state saved via a debounced store updater
  • Tests

    • Added MessageList scroll behavior tests

@dionisio-bot
Copy link
Copy Markdown
Contributor

dionisio-bot Bot commented Apr 9, 2026

Looks like this PR is not ready to merge, because of the following issues:

  • This PR is missing the 'stat: QA assured' label
  • This PR is missing the required milestone or project
  • This PR has an invalid title

Please fix the issues and try again

If you have any trouble, please check the PR guidelines

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 9, 2026

⚠️ No Changeset found

Latest commit: ea7d275

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 9, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2591a40f-9596-4a5c-b425-d5121b3e1530

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

Replaces DOM-based message list scrolling with a virtualized VList, removes ref-based jump-to-message plumbing, centralizes deep-link handling into new hooks that fetch/scroll/highlight messages, and shifts scroll/load state management to ref-driven and virtualizer-driven APIs across room and thread UIs.

Changes

Cohort / File(s) Summary
Virtualization Core
apps/meteor/client/views/room/MessageList/MessageList.tsx, apps/meteor/client/views/room/MessageList/MessageListItem.tsx, packages/ui-client/src/components/CustomScrollbars/CustomVirtuaScrollbars.tsx, packages/ui-client/src/components/CustomScrollbars/index.ts
Introduce virtua-based VList rendering, add CustomVirtuaScrollbars, reorder imports; migrate message-list rendering, loading indicators, and foreword/retention UI into the virtualized list.
Deep-link / Jump-to Hooks
apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts, apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToThreadMessage.ts, apps/meteor/client/views/room/MessageList/hooks/useLoadSurroundingMessages.ts
Add hooks to handle ?msg= deep links for channel and thread targets, driving React Query fetches and virtualizer scrolls; remove returned jumpToRef from surrounding-message loader.
Removed Legacy Jump & Scroll Hooks
apps/meteor/client/views/room/MessageList/hooks/useJumpToMessage.ts, apps/meteor/client/views/room/body/hooks/useListIsAtBottom.ts, apps/meteor/client/views/room/body/hooks/useRestoreScrollPosition.ts, apps/meteor/client/views/room/contextualBar/Threads/hooks/useLegacyThreadMessageListScrolling.ts
Delete legacy imperative jump and "at-bottom"/restore-scroll hooks; replace with virtualizer/ref-driven patterns.
Room Body & Thread List Integration
apps/meteor/client/views/room/body/RoomBody.tsx, apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageList.tsx
Wire in virtualizer refs and jump/at-bottom state refs, remove older imperative scroll hooks, and integrate new jump-to-thread/message behavior and UI placement changes.
Scroll / New-message APIs & Hooks
apps/meteor/client/views/room/body/hooks/useGetMore.ts, apps/meteor/client/views/room/body/hooks/useHasNewMessages.ts, apps/meteor/client/views/room/body/hooks/useStoreScrollPosition.ts, apps/meteor/client/views/room/body/hooks/useHasNewMessages.spec.ts, apps/meteor/client/views/room/body/hooks/useGetMore.spec.ts
Switch get-more/has-new-messages APIs to use ref-driven shouldJumpToBottom/isAtBottom, remove DOM scroll listeners, add useStoreScrollPosition to persist virtualizer offsets, and update tests accordingly.
Unread / Read-state Changes
apps/meteor/client/lib/chats/readStateManager.ts, apps/meteor/client/views/room/body/hooks/useUnreadMessages.ts
Replace DOM-based unread-divider visibility checks with injectable visibility callback; expose setters for unread count/last message date instead of internal refs.
Message Ref & Context Cleanup
apps/meteor/client/components/message/list/MessageListContext.tsx, apps/meteor/client/views/room/MessageList/providers/MessageListProvider.tsx, apps/meteor/client/components/message/variants/RoomMessage.tsx, apps/meteor/client/components/message/variants/ThreadMessage.tsx, apps/meteor/client/views/room/MessageList/hooks/useJumpToMessage.ts
Remove messageListRef from context/provider and message components; delete obsolete useJumpToMessage hook and usages.
Date Scroll & Retention API
apps/meteor/client/views/room/hooks/useDateScroll.ts, apps/meteor/client/views/room/hooks/useRetentionPolicy.ts
Refactor date-scroll to external debounced handler (handleDateScroll) and export explicit RetentionPolicy type with unchanged runtime behavior.
RoomHistoryManager Signature
apps/meteor/app/ui-utils/client/lib/RoomHistoryManager.ts, apps/meteor/client/views/room/body/hooks/useGetMore.spec.tsx
Change getMoreNext to accept only rid (remove atBottomRef param); adjust tests to expect single-arg call.
Tests & Specs
apps/meteor/client/views/room/MessageList/MessageList.spec.tsx, various removed/updated spec files (useRestoreScrollPosition.spec.tsx, useLoadSurroundingMessages.spec.ts, useHasNewMessages.spec.ts)
Add MessageList virtualization tests; remove or update tests for deleted hooks and changed behaviors.
Package deps
apps/meteor/package.json
Add runtime dependency virtua (^0.49.0).

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Router
    participant TryJumpHook as useTryToJumpToMessage
    participant ReactQuery
    participant RoomHistory
    participant Virtualizer as MessageList(VList)
    participant HighlightMgr as HighlightManager

    User->>Router: Navigate with ?msg=messageId
    Router->>TryJumpHook: effect triggered (msg param present)
    TryJumpHook->>ReactQuery: GET /v1/chat.getMessage(messageId)
    ReactQuery-->>TryJumpHook: API message payload
    alt Message in messages[]
        TryJumpHook->>Virtualizer: scrollToIndex(foundIndex, center)
    else Message not present
        TryJumpHook->>RoomHistory: loadSurroundingMessages(messageId)
        RoomHistory-->>Virtualizer: messages loaded into list
        TryJumpHook->>Virtualizer: scrollToIndex(foundIndex, center)
    end
    TryJumpHook->>HighlightMgr: setHighlightMessage(messageId)
    Note over HighlightMgr: Highlight cleared after timeout
    TryJumpHook->>Router: replace URL without ?msg
Loading
sequenceDiagram
    participant User
    participant Virtualizer as VList
    participant MessageList
    participant useGetMore
    participant RoomHistoryManager

    User->>Virtualizer: Scroll near start (top)
    Virtualizer->>MessageList: onScroll/topIndex changed
    MessageList->>useGetMore: request previous page
    useGetMore->>RoomHistoryManager: getMorePrevious(rid)
    RoomHistoryManager-->>useGetMore: messages loaded
    useGetMore->>MessageList: update internal ref/viewport
    MessageList->>Virtualizer: re-render (prepend shift)

    User->>Virtualizer: Scroll near end (bottom)
    Virtualizer->>MessageList: onScroll + isAtBottom check
    MessageList->>useGetMore: request next page
    useGetMore->>RoomHistoryManager: getMoreNext(rid)
    Note over RoomHistoryManager: no longer mutates external atBottomRef
    RoomHistoryManager-->>useGetMore: messages loaded
    useGetMore->>MessageList: update internal ref/viewport
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ 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%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'poc: Virtua message list' clearly describes the main change—migrating the message list to use Virtua virtualization—but uses the term 'poc' (proof of concept) which may be vague for permanent codebase history.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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


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.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 9, 2026

Codecov Report

❌ Patch coverage is 85.95318% with 42 lines in your changes missing coverage. Please review.
✅ Project coverage is 69.85%. Comparing base (9498359) to head (ea7d275).
⚠️ Report is 3 commits behind head on develop.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop   #40105      +/-   ##
===========================================
+ Coverage    69.83%   69.85%   +0.01%     
===========================================
  Files         3296     3298       +2     
  Lines       119173   119228      +55     
  Branches     21475    21468       -7     
===========================================
+ Hits         83221    83282      +61     
- Misses       32645    32651       +6     
+ Partials      3307     3295      -12     
Flag Coverage Δ
unit 70.64% <85.95%> (+0.05%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@MartinSchoeler
Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 27, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot added the type: feature Pull requests that introduces new feature label Apr 27, 2026
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: 9

Caution

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

⚠️ Outside diff range comments (1)
apps/meteor/client/views/room/body/hooks/useHasNewMessages.spec.ts (1)

22-31: ⚠️ Potential issue | 🟡 Minor

Reset shared refs between tests to avoid order-dependent behavior.

Line 22 and Line 23 define mutable refs once for the whole suite, but they are never reset in beforeEach. Because tests mutate these refs (e.g., Line 191), later tests can inherit stale state.

Proposed fix
 beforeEach(() => {
 	jest.clearAllMocks();
+	isAtBottom.current = true;
+	shouldJumpToBottom.current = false;
 	clientCallbacks.remove('streamNewMessage', rid);
 	clientCallbacks.remove('afterSaveMessage', rid);
 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/meteor/client/views/room/body/hooks/useHasNewMessages.spec.ts` around
lines 22 - 31, Tests define shared mutable refs isAtBottom and
shouldJumpToBottom at suite scope but never reinitialize them between tests;
update the beforeEach in useHasNewMessages.spec.ts to reset isAtBottom.current =
true and shouldJumpToBottom.current = false (or recreate the objects) so each
test starts with fresh refs, ensuring tests that mutate these refs (e.g., later
assertions around shouldJumpToBottom) are isolated.
🧹 Nitpick comments (3)
packages/ui-client/src/components/CustomScrollbars/CustomVirtuaScrollbars.tsx (1)

18-21: Remove the inline implementation comment.

As per coding guidelines, keep implementation comments out of the component; the overflow-style assignments are already self-explanatory.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/ui-client/src/components/CustomScrollbars/CustomVirtuaScrollbars.tsx`
around lines 18 - 21, Remove the unnecessary implementation comment in
CustomVirtuaScrollbars.tsx: delete the line containing "// force overflow
styles" directly above the block that accesses osInstance.elements() and assigns
viewport.style.overflowX and viewport.style.overflowY so the code remains
self-explanatory while leaving the osInstance.elements() call and viewport.style
assignments intact.
apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts (1)

36-39: Remove inline REVIEW/TODO comments from hook implementation.

Move these notes to a tracked issue/PR note and keep runtime code comment-free.

As per coding guidelines "**/*.{ts,tsx,js}: ... Avoid code comments in the implementation".

Also applies to: 82-82

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts`
around lines 36 - 39, Remove the inline REVIEW/TODO comments inside the
useTryToJumpToMessage hook implementation (e.g., the block mentioning onScroll
and context about jump-to-message behavior) and relocate that text into a
tracked issue or PR note; update the hook file to contain no runtime-facing
REVIEW/TODO comments while preserving any necessary context in the external
issue/PR so the behavior is documented outside the code.
apps/meteor/client/views/room/MessageList/MessageList.tsx (1)

65-67: Remove inline TODO/REVIEW comments from runtime implementation.

Please track these as issues/PR notes instead of embedding them in component code.

As per coding guidelines "**/*.{ts,tsx,js}: ... Avoid code comments in the implementation".

Also applies to: 80-83, 99-100, 134-135

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/meteor/client/views/room/MessageList/MessageList.tsx` around lines 65 -
67, Remove inline TODO/REVIEW comments from the runtime component code in
MessageList.tsx: delete the comment blocks that precede/annotate the isPrepend
useRef declaration and any other inline review/TODO comments in this file (the
ones near the isPrepend declaration and the other comment blocks around the
message list logic), and instead track these items as issues or PR notes in your
tracker; keep the isPrepend = useRef<boolean>(false) statement unchanged but
remove any explanatory review comments from the implementation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/meteor/client/views/room/body/hooks/useHasNewMessages.spec.ts`:
- Around line 57-59: The test "should NOT show new messages button when user
sends own message during race condition (not at bottom)" sets isAtBottom true,
so it never exercises the "not at bottom" branch; update the test that calls
renderHook(() => useHasNewMessages(rid, uid, shouldJumpToBottom, isAtBottom), {
wrapper: ... }) to pass isAtBottom as false (or use the existing variable set to
false) so the hook runs the not-at-bottom race-condition path for self-sent
messages; ensure the variables referenced (rid, uid, shouldJumpToBottom,
isAtBottom) and the renderHook call are adjusted accordingly.

In `@apps/meteor/client/views/room/body/hooks/useStoreScrollPosition.ts`:
- Around line 13-21: The debounced callback in useStoreScrollPosition can
overwrite the last valid scroll with 0 if virtualizerRef.current is gone when
the timer fires; change the callback in useStoreScrollPosition to bail out if
virtualizerRef.current is null/undefined instead of using the "?? 0" fallback,
and only call RoomManager.getStore(rid)?.update(...) when virtualizerRef.current
exists (use its scrollOffset) and preserve using isAtBottom.current for the
atBottom field; this prevents detaching the viewport from writing a bogus
top-of-list position.

In
`@apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageList.tsx`:
- Around line 126-131: The current call to router.navigate with only pathname
wipes all query params; instead, preserve existing query params and remove only
the msg key before navigating. Read the current search/query (via
router.getLocationSearch or the router/location object available in
ThreadMessageList), create a URLSearchParams from it, delete('msg'), then call
router.navigate with pathname: router.getLocationPathname() and search:
`?${params.toString()}` (or omit search if empty), keeping { replace: true } to
avoid replacing unrelated query state.

In `@apps/meteor/client/views/room/hooks/useDateScroll.ts`:
- Around line 97-107: The fallback date-bubble style in useDateScroll.ts sets an
invalid CSS key "to" which prevents the position from applying; find the code
path where matched is assigned when (matched.length === 0 && topMessage) and
replace the "to" property in the style object with "top" (keeping the same value
`${margin}px`) so the absolute positioning for the fallback bubble works as
intended (refer to the matched assignment and topMessage usage).
- Around line 113-118: The state update in useDateScroll.ts clears date to ''
every run, which causes flicker; instead only overwrite date when the new date
value is present. Update the setBubbleDate(...) call (the setter used in
useDateScroll.ts) to avoid setting date: '' unconditionally—keep the existing
current.date when date is falsy and only spread in { date } when date is truthy,
while still updating show using Boolean(date).

In `@apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts`:
- Around line 54-67: The isJumpingToMessage.current flag is set too early
causing it to remain true on early returns; move the assignment or ensure it's
cleared on all aborted paths: either set isJumpingToMessage.current = true only
after the RoomHistoryManager.isLoading(rid) check and after confirming
loadedMessage exists, or add explicit resets (isJumpingToMessage.current =
false) before each early return in the current block (references:
isJumpingToMessage.current, RoomHistoryManager.isLoading(rid), messages.find(...
_id === messageJumpParam),
RoomHistoryManager.getSurroundingChannelMessages(message)).

In `@apps/meteor/client/views/room/MessageList/MessageList.tsx`:
- Around line 46-47: lastViewportSize is defined as a constant (const
lastViewportSize = 0) so the subsequent comparison never tracks previous
viewport height; replace it with a persistent mutable value (e.g., a React ref
via useRef<number>(0) or component state) inside the MessageList component,
update references in the resize/viewport-checking logic to read/write
lastViewportSize.current (or the state setter) instead of the constant, and set
it after you perform the comparison so future renders see the previous viewport
size (update usages where the code compares and assigns around the existing
comparison block that references lastViewportSize).
- Around line 155-175: Reset unreadMarkIndex.current to null at the start of
each render so stale index values can't be used by visibility checks: add
unreadMarkIndex.current = null near the top of the MessageList component before
any logic that searches for the divider (the code paths that set unreadMarkIndex
when a divider is found), and do the same reset in the other block that mirrors
this logic (the block around the second occurrence at lines 227-235). This
ensures isUnreadMarkVisible (which reads unreadMarkIndex.current), the
divider-finding code that updates unreadMarkIndex, and uses of virtualizerRef
and canPreview always operate on a fresh value rather than a stale ref.

In
`@packages/ui-client/src/components/CustomScrollbars/CustomVirtuaScrollbars.tsx`:
- Around line 23-27: The forwarded ref assigned in CustomVirtuaScrollbars (the
block that sets ref(viewport) or ref.current = viewport) must be cleared on
unmount to avoid leaving a dangling reference; update the component's effect or
cleanup where the viewport is assigned so the cleanup function resets the
forwarded ref back to null (call ref(null) for function refs or set ref.current
= null for RefObjects) and ensure this logic runs when the overlay/component
unmounts or viewport changes.

---

Outside diff comments:
In `@apps/meteor/client/views/room/body/hooks/useHasNewMessages.spec.ts`:
- Around line 22-31: Tests define shared mutable refs isAtBottom and
shouldJumpToBottom at suite scope but never reinitialize them between tests;
update the beforeEach in useHasNewMessages.spec.ts to reset isAtBottom.current =
true and shouldJumpToBottom.current = false (or recreate the objects) so each
test starts with fresh refs, ensuring tests that mutate these refs (e.g., later
assertions around shouldJumpToBottom) are isolated.

---

Nitpick comments:
In `@apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts`:
- Around line 36-39: Remove the inline REVIEW/TODO comments inside the
useTryToJumpToMessage hook implementation (e.g., the block mentioning onScroll
and context about jump-to-message behavior) and relocate that text into a
tracked issue or PR note; update the hook file to contain no runtime-facing
REVIEW/TODO comments while preserving any necessary context in the external
issue/PR so the behavior is documented outside the code.

In `@apps/meteor/client/views/room/MessageList/MessageList.tsx`:
- Around line 65-67: Remove inline TODO/REVIEW comments from the runtime
component code in MessageList.tsx: delete the comment blocks that
precede/annotate the isPrepend useRef declaration and any other inline
review/TODO comments in this file (the ones near the isPrepend declaration and
the other comment blocks around the message list logic), and instead track these
items as issues or PR notes in your tracker; keep the isPrepend =
useRef<boolean>(false) statement unchanged but remove any explanatory review
comments from the implementation.

In
`@packages/ui-client/src/components/CustomScrollbars/CustomVirtuaScrollbars.tsx`:
- Around line 18-21: Remove the unnecessary implementation comment in
CustomVirtuaScrollbars.tsx: delete the line containing "// force overflow
styles" directly above the block that accesses osInstance.elements() and assigns
viewport.style.overflowX and viewport.style.overflowY so the code remains
self-explanatory while leaving the osInstance.elements() call and viewport.style
assignments intact.
🪄 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: e0052fd9-bbed-423c-b36b-ec29e2545d7d

📥 Commits

Reviewing files that changed from the base of the PR and between 9498359 and bdcc7e4.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (30)
  • apps/meteor/app/ui-utils/client/lib/RoomHistoryManager.ts
  • apps/meteor/client/components/message/list/MessageListContext.tsx
  • apps/meteor/client/components/message/variants/RoomMessage.tsx
  • apps/meteor/client/components/message/variants/ThreadMessage.tsx
  • apps/meteor/client/lib/chats/readStateManager.ts
  • apps/meteor/client/views/room/MessageList/MessageList.spec.tsx
  • apps/meteor/client/views/room/MessageList/MessageList.tsx
  • apps/meteor/client/views/room/MessageList/MessageListItem.tsx
  • apps/meteor/client/views/room/MessageList/hooks/useJumpToMessage.ts
  • apps/meteor/client/views/room/MessageList/hooks/useLoadSurroundingMessages.spec.ts
  • apps/meteor/client/views/room/MessageList/hooks/useLoadSurroundingMessages.ts
  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts
  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToThreadMessage.ts
  • apps/meteor/client/views/room/MessageList/providers/MessageListProvider.tsx
  • apps/meteor/client/views/room/body/RoomBody.tsx
  • apps/meteor/client/views/room/body/hooks/useGetMore.ts
  • apps/meteor/client/views/room/body/hooks/useHasNewMessages.spec.ts
  • apps/meteor/client/views/room/body/hooks/useHasNewMessages.ts
  • apps/meteor/client/views/room/body/hooks/useListIsAtBottom.ts
  • apps/meteor/client/views/room/body/hooks/useRestoreScrollPosition.spec.tsx
  • apps/meteor/client/views/room/body/hooks/useRestoreScrollPosition.ts
  • apps/meteor/client/views/room/body/hooks/useStoreScrollPosition.ts
  • apps/meteor/client/views/room/body/hooks/useUnreadMessages.ts
  • apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageList.tsx
  • apps/meteor/client/views/room/contextualBar/Threads/hooks/useLegacyThreadMessageListScrolling.ts
  • apps/meteor/client/views/room/hooks/useDateScroll.ts
  • apps/meteor/client/views/room/hooks/useRetentionPolicy.ts
  • apps/meteor/package.json
  • packages/ui-client/src/components/CustomScrollbars/CustomVirtuaScrollbars.tsx
  • packages/ui-client/src/components/CustomScrollbars/index.ts
💤 Files with no reviewable changes (8)
  • apps/meteor/client/views/room/MessageList/hooks/useLoadSurroundingMessages.spec.ts
  • apps/meteor/client/components/message/variants/ThreadMessage.tsx
  • apps/meteor/client/components/message/variants/RoomMessage.tsx
  • apps/meteor/client/views/room/body/hooks/useRestoreScrollPosition.spec.tsx
  • apps/meteor/client/views/room/contextualBar/Threads/hooks/useLegacyThreadMessageListScrolling.ts
  • apps/meteor/client/views/room/MessageList/hooks/useJumpToMessage.ts
  • apps/meteor/client/views/room/body/hooks/useRestoreScrollPosition.ts
  • apps/meteor/client/views/room/body/hooks/useListIsAtBottom.ts
📜 Review details
⏰ 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). (2)
  • GitHub Check: 📦 Build Packages
  • GitHub Check: CodeQL-Build
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js}

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

**/*.{ts,tsx,js}: Write concise, technical TypeScript/JavaScript with accurate typing in Playwright tests
Avoid code comments in the implementation

Files:

  • packages/ui-client/src/components/CustomScrollbars/index.ts
  • apps/meteor/client/views/room/MessageList/MessageListItem.tsx
  • apps/meteor/client/views/room/body/hooks/useStoreScrollPosition.ts
  • apps/meteor/client/views/room/MessageList/providers/MessageListProvider.tsx
  • apps/meteor/client/views/room/MessageList/hooks/useLoadSurroundingMessages.ts
  • apps/meteor/client/components/message/list/MessageListContext.tsx
  • apps/meteor/client/views/room/hooks/useRetentionPolicy.ts
  • apps/meteor/app/ui-utils/client/lib/RoomHistoryManager.ts
  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToThreadMessage.ts
  • apps/meteor/client/views/room/MessageList/MessageList.spec.tsx
  • apps/meteor/client/lib/chats/readStateManager.ts
  • packages/ui-client/src/components/CustomScrollbars/CustomVirtuaScrollbars.tsx
  • apps/meteor/client/views/room/body/hooks/useGetMore.ts
  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts
  • apps/meteor/client/views/room/body/hooks/useUnreadMessages.ts
  • apps/meteor/client/views/room/body/hooks/useHasNewMessages.spec.ts
  • apps/meteor/client/views/room/hooks/useDateScroll.ts
  • apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageList.tsx
  • apps/meteor/client/views/room/body/RoomBody.tsx
  • apps/meteor/client/views/room/MessageList/MessageList.tsx
  • apps/meteor/client/views/room/body/hooks/useHasNewMessages.ts
**/*.spec.ts

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

**/*.spec.ts: Use descriptive test names that clearly communicate expected behavior in Playwright tests
Use .spec.ts extension for test files (e.g., login.spec.ts)

Files:

  • apps/meteor/client/views/room/body/hooks/useHasNewMessages.spec.ts
🧠 Learnings (32)
📓 Common learnings
Learnt from: rodrigok
Repo: RocketChat/Rocket.Chat PR: 38623
File: apps/meteor/app/lib/server/functions/cleanRoomHistory.ts:146-149
Timestamp: 2026-04-18T12:32:53.425Z
Learning: In `apps/meteor/app/lib/server/functions/cleanRoomHistory.ts` (PR `#38623`), the read-receipt cleanup (both `ReadReceipts.removeByMessageIds` and `ReadReceiptsArchive.removeByMessageIds`) is intentionally only performed in the limited prune path (`limit && selectedMessageIds`). The unlimited/delete-all path (`limit === 0`) deliberately skips cleaning up orphaned read receipts in both hot and cold storage — this is by design. Do not flag this as a bug or missing cleanup in future reviews.
📚 Learning: 2026-04-14T23:23:18.990Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 40159
File: packages/apps-engine/turbo.json:6-6
Timestamp: 2026-04-14T23:23:18.990Z
Learning: In RocketChat/Rocket.Chat's Turborepo setup, the workspace-root `package.json` is part of Turbo's global hash and does not need to be explicitly listed in a package-level turbo.json task's `inputs` array. Changes to the root `package.json` already bust the cache for all tasks via the global hash. Do not flag the absence of `$TURBO_ROOT$/package.json` in task-level inputs as a cache-invalidation issue.

Applied to files:

  • apps/meteor/package.json
📚 Learning: 2026-03-17T16:08:37.572Z
Learnt from: cardoso
Repo: RocketChat/Rocket.Chat PR: 39690
File: packages/ui-voip/package.json:11-11
Timestamp: 2026-03-17T16:08:37.572Z
Learning: In `packages/ui-voip/package.json` (RocketChat/Rocket.Chat), the team deliberately chose to use `rm -rf dist` directly in the `"build"` script instead of `rimraf`, as they decided against introducing the `rimraf` dependency for this package. Do not flag `rm -rf dist` in the ui-voip build script as a cross-platform issue requiring rimraf.

Applied to files:

  • apps/meteor/package.json
📚 Learning: 2026-02-25T20:10:16.987Z
Learnt from: ahmed-n-abdeltwab
Repo: RocketChat/Rocket.Chat PR: 38913
File: packages/ddp-client/src/legacy/types/SDKLegacy.ts:34-34
Timestamp: 2026-02-25T20:10:16.987Z
Learning: In the RocketChat/Rocket.Chat monorepo, packages/ddp-client and apps/meteor do not use TypeScript project references. Module augmentations in apps/meteor (e.g., declare module 'rocket.chat/rest-typings') are not visible when compiling packages/ddp-client in isolation, which is why legacy SDK methods that depend on OperationResult types for OpenAPI-migrated endpoints must remain commented out.

Applied to files:

  • apps/meteor/package.json
  • apps/meteor/client/views/room/MessageList/providers/MessageListProvider.tsx
  • apps/meteor/client/views/room/MessageList/hooks/useLoadSurroundingMessages.ts
  • apps/meteor/client/views/room/body/hooks/useUnreadMessages.ts
  • apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageList.tsx
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In the Rocket.Chat repository, do not reference Biome lint rules in code review feedback. Biome is not used even if biome.json exists; only reference Biome rules if there is explicit, project-wide usage documented. For TypeScript files, review lint implications without Biome guidance unless the project enables Biome rules.

Applied to files:

  • packages/ui-client/src/components/CustomScrollbars/index.ts
  • apps/meteor/client/views/room/body/hooks/useStoreScrollPosition.ts
  • apps/meteor/client/views/room/MessageList/hooks/useLoadSurroundingMessages.ts
  • apps/meteor/client/views/room/hooks/useRetentionPolicy.ts
  • apps/meteor/app/ui-utils/client/lib/RoomHistoryManager.ts
  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToThreadMessage.ts
  • apps/meteor/client/lib/chats/readStateManager.ts
  • apps/meteor/client/views/room/body/hooks/useGetMore.ts
  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts
  • apps/meteor/client/views/room/body/hooks/useUnreadMessages.ts
  • apps/meteor/client/views/room/body/hooks/useHasNewMessages.spec.ts
  • apps/meteor/client/views/room/hooks/useDateScroll.ts
  • apps/meteor/client/views/room/body/hooks/useHasNewMessages.ts
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In this repository (RocketChat/Rocket.Chat), Biome lint rules are not used even if a biome.json exists. When reviewing TypeScript files (e.g., packages/ui-voip/src/providers/useMediaSession.ts), ensure lint suggestions do not reference Biome-specific rules. Rely on general ESLint/TypeScript lint rules and project conventions instead.

Applied to files:

  • packages/ui-client/src/components/CustomScrollbars/index.ts
  • apps/meteor/client/views/room/body/hooks/useStoreScrollPosition.ts
  • apps/meteor/client/views/room/MessageList/hooks/useLoadSurroundingMessages.ts
  • apps/meteor/client/views/room/hooks/useRetentionPolicy.ts
  • apps/meteor/app/ui-utils/client/lib/RoomHistoryManager.ts
  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToThreadMessage.ts
  • apps/meteor/client/lib/chats/readStateManager.ts
  • apps/meteor/client/views/room/body/hooks/useGetMore.ts
  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts
  • apps/meteor/client/views/room/body/hooks/useUnreadMessages.ts
  • apps/meteor/client/views/room/body/hooks/useHasNewMessages.spec.ts
  • apps/meteor/client/views/room/hooks/useDateScroll.ts
  • apps/meteor/client/views/room/body/hooks/useHasNewMessages.ts
📚 Learning: 2026-03-11T22:04:20.529Z
Learnt from: juliajforesti
Repo: RocketChat/Rocket.Chat PR: 39545
File: apps/meteor/client/views/room/body/hooks/useHasNewMessages.ts:59-61
Timestamp: 2026-03-11T22:04:20.529Z
Learning: In `apps/meteor/client/views/room/body/hooks/useHasNewMessages.ts`, the `msg.u._id === uid` early-return in the `streamNewMessage` handler is intentional: the "New messages" indicator is designed to notify about messages from other users only. Self-sent messages — including those sent from a different session/device — are always skipped, by design. Do not flag this as a multi-session regression.

Applied to files:

  • apps/meteor/client/views/room/MessageList/MessageListItem.tsx
  • apps/meteor/client/views/room/body/hooks/useStoreScrollPosition.ts
  • apps/meteor/client/views/room/MessageList/providers/MessageListProvider.tsx
  • apps/meteor/client/views/room/MessageList/hooks/useLoadSurroundingMessages.ts
  • apps/meteor/client/views/room/hooks/useRetentionPolicy.ts
  • apps/meteor/app/ui-utils/client/lib/RoomHistoryManager.ts
  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToThreadMessage.ts
  • apps/meteor/client/views/room/MessageList/MessageList.spec.tsx
  • apps/meteor/client/lib/chats/readStateManager.ts
  • apps/meteor/client/views/room/body/hooks/useGetMore.ts
  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts
  • apps/meteor/client/views/room/body/hooks/useUnreadMessages.ts
  • apps/meteor/client/views/room/body/hooks/useHasNewMessages.spec.ts
  • apps/meteor/client/views/room/hooks/useDateScroll.ts
  • apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageList.tsx
  • apps/meteor/client/views/room/body/RoomBody.tsx
  • apps/meteor/client/views/room/MessageList/MessageList.tsx
📚 Learning: 2026-02-24T19:36:55.089Z
Learnt from: juliajforesti
Repo: RocketChat/Rocket.Chat PR: 38493
File: apps/meteor/tests/e2e/page-objects/fragments/home-content.ts:60-82
Timestamp: 2026-02-24T19:36:55.089Z
Learning: In RocketChat/Rocket.Chat e2e tests (apps/meteor/tests/e2e/page-objects/fragments/home-content.ts), thread message preview listitems do not have aria-roledescription="message", so lastThreadMessagePreview locator cannot be scoped to messageListItems (which filters for aria-roledescription="message"). It should remain scoped to page.getByRole('listitem') or mainMessageList.getByRole('listitem').

Applied to files:

  • apps/meteor/client/views/room/MessageList/MessageListItem.tsx
  • apps/meteor/client/views/room/MessageList/providers/MessageListProvider.tsx
  • apps/meteor/client/views/room/MessageList/hooks/useLoadSurroundingMessages.ts
  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToThreadMessage.ts
  • apps/meteor/client/views/room/MessageList/MessageList.spec.tsx
  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts
  • apps/meteor/client/views/room/body/hooks/useHasNewMessages.spec.ts
  • apps/meteor/client/views/room/hooks/useDateScroll.ts
  • apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageList.tsx
  • apps/meteor/client/views/room/body/RoomBody.tsx
  • apps/meteor/client/views/room/MessageList/MessageList.tsx
  • apps/meteor/client/views/room/body/hooks/useHasNewMessages.ts
📚 Learning: 2026-03-27T14:52:56.865Z
Learnt from: dougfabris
Repo: RocketChat/Rocket.Chat PR: 39892
File: apps/meteor/client/views/room/contextualBar/Threads/Thread.tsx:150-155
Timestamp: 2026-03-27T14:52:56.865Z
Learning: In Rocket.Chat, there are two different `ModalBackdrop` components with different prop APIs. During review, confirm the import source: (1) `rocket.chat/fuselage` `ModalBackdrop` uses `ModalBackdropProps` based on `BoxProps` (so it supports `onClick` and other Box/DOM props) and does not have an `onDismiss` prop; (2) `rocket.chat/ui-client` `ModalBackdrop` uses a narrower props interface like `{ children?: ReactNode; onDismiss?: () => void }` and handles Escape keypress and outside mouse-up, and it does not forward arbitrary DOM props such as `onClick`. Flag mismatched props (e.g., `onDismiss` passed to the fuselage component or `onClick` passed to the ui-client component) and ensure the usage matches the correct component being imported.

Applied to files:

  • apps/meteor/client/views/room/MessageList/MessageListItem.tsx
  • apps/meteor/client/views/room/MessageList/providers/MessageListProvider.tsx
  • apps/meteor/client/components/message/list/MessageListContext.tsx
  • apps/meteor/client/views/room/MessageList/MessageList.spec.tsx
  • packages/ui-client/src/components/CustomScrollbars/CustomVirtuaScrollbars.tsx
  • apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageList.tsx
  • apps/meteor/client/views/room/body/RoomBody.tsx
  • apps/meteor/client/views/room/MessageList/MessageList.tsx
📚 Learning: 2026-04-13T16:40:55.864Z
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 40100
File: apps/meteor/client/views/root/MainLayout/EmbeddedPreload.tsx:18-35
Timestamp: 2026-04-13T16:40:55.864Z
Learning: In Rocket.Chat's embedded mode (`?layout=embedded`), route changes are driven by the parent frame reloading the iframe URL, which causes a full remount of the React tree (including `EmbeddedPreload` at `apps/meteor/client/views/root/MainLayout/EmbeddedPreload.tsx`). The component never survives a navigation, so `roomParams` (computed via `useMemo`) is always fresh on mount. A `[router]` singleton as the sole `useMemo` dependency is therefore correct and intentional — do not flag it as a stale-dependency bug in this component.

Applied to files:

  • apps/meteor/client/views/room/body/hooks/useStoreScrollPosition.ts
  • apps/meteor/client/views/room/MessageList/providers/MessageListProvider.tsx
  • apps/meteor/client/views/room/MessageList/hooks/useLoadSurroundingMessages.ts
  • apps/meteor/app/ui-utils/client/lib/RoomHistoryManager.ts
  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToThreadMessage.ts
  • apps/meteor/client/views/room/body/hooks/useGetMore.ts
  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts
  • apps/meteor/client/views/room/body/hooks/useUnreadMessages.ts
  • apps/meteor/client/views/room/body/RoomBody.tsx
📚 Learning: 2026-02-10T16:32:42.586Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 38528
File: apps/meteor/client/startup/roles.ts:14-14
Timestamp: 2026-02-10T16:32:42.586Z
Learning: In Rocket.Chat's Meteor client code, DDP streams use EJSON and Date fields arrive as Date objects; do not manually construct new Date() in stream handlers (for example, in sdk.stream()). Only REST API responses return plain JSON where dates are strings, so implement explicit conversion there if needed. Apply this guidance to all TypeScript files under apps/meteor/client to ensure consistent date handling in DDP streams and REST responses.

Applied to files:

  • apps/meteor/client/views/room/body/hooks/useStoreScrollPosition.ts
  • apps/meteor/client/views/room/MessageList/hooks/useLoadSurroundingMessages.ts
  • apps/meteor/client/views/room/hooks/useRetentionPolicy.ts
  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToThreadMessage.ts
  • apps/meteor/client/lib/chats/readStateManager.ts
  • apps/meteor/client/views/room/body/hooks/useGetMore.ts
  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts
  • apps/meteor/client/views/room/body/hooks/useUnreadMessages.ts
  • apps/meteor/client/views/room/body/hooks/useHasNewMessages.spec.ts
  • apps/meteor/client/views/room/hooks/useDateScroll.ts
  • apps/meteor/client/views/room/body/hooks/useHasNewMessages.ts
📚 Learning: 2026-04-14T21:10:36.233Z
Learnt from: dougfabris
Repo: RocketChat/Rocket.Chat PR: 36292
File: apps/meteor/client/hooks/useHasValidLocationHash.ts:7-12
Timestamp: 2026-04-14T21:10:36.233Z
Learning: In the RocketChat/Rocket.Chat repository, adding JSDoc-style comments to React hooks (e.g., files under apps/meteor/client/hooks/) is considered a good pattern and should not be flagged as a violation of the "avoid code comments in the implementation" guideline. The guideline against code comments does not apply to JSDoc documentation on exported hooks.

Applied to files:

  • apps/meteor/client/views/room/MessageList/providers/MessageListProvider.tsx
  • apps/meteor/client/components/message/list/MessageListContext.tsx
  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToThreadMessage.ts
  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts
  • apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageList.tsx
📚 Learning: 2025-11-19T18:20:07.720Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 37419
File: packages/i18n/src/locales/en.i18n.json:918-921
Timestamp: 2025-11-19T18:20:07.720Z
Learning: Repo: RocketChat/Rocket.Chat — i18n/formatting
Learning: This repository uses a custom message formatting parser in UI blocks/messages; do not assume standard Markdown rules. For keys like Call_ended_bold, Call_not_answered_bold, Call_failed_bold, and Call_transferred_bold in packages/i18n/src/locales/en.i18n.json, retain the existing single-asterisk emphasis unless maintainers request otherwise.

Applied to files:

  • apps/meteor/client/views/room/MessageList/providers/MessageListProvider.tsx
📚 Learning: 2026-04-18T12:32:53.425Z
Learnt from: rodrigok
Repo: RocketChat/Rocket.Chat PR: 38623
File: apps/meteor/app/lib/server/functions/cleanRoomHistory.ts:146-149
Timestamp: 2026-04-18T12:32:53.425Z
Learning: In `apps/meteor/app/lib/server/functions/cleanRoomHistory.ts` (PR `#38623`), the read-receipt cleanup (both `ReadReceipts.removeByMessageIds` and `ReadReceiptsArchive.removeByMessageIds`) is intentionally only performed in the limited prune path (`limit && selectedMessageIds`). The unlimited/delete-all path (`limit === 0`) deliberately skips cleaning up orphaned read receipts in both hot and cold storage — this is by design. Do not flag this as a bug or missing cleanup in future reviews.

Applied to files:

  • apps/meteor/client/views/room/MessageList/hooks/useLoadSurroundingMessages.ts
  • apps/meteor/client/views/room/hooks/useRetentionPolicy.ts
  • apps/meteor/app/ui-utils/client/lib/RoomHistoryManager.ts
  • apps/meteor/client/lib/chats/readStateManager.ts
  • apps/meteor/client/views/room/body/hooks/useGetMore.ts
  • apps/meteor/client/views/room/body/hooks/useUnreadMessages.ts
  • apps/meteor/client/views/room/hooks/useDateScroll.ts
  • apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageList.tsx
  • apps/meteor/client/views/room/body/RoomBody.tsx
  • apps/meteor/client/views/room/MessageList/MessageList.tsx
  • apps/meteor/client/views/room/body/hooks/useHasNewMessages.ts
📚 Learning: 2026-01-19T18:08:13.423Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38169
File: packages/ui-client/src/hooks/useGoToDirectMessage.ts:20-27
Timestamp: 2026-01-19T18:08:13.423Z
Learning: In React hooks, including custom hooks like useUserSubscriptionByName, must always be called unconditionally and in the same order on every render, per the Rules of Hooks. Passing empty strings or fallback values to hooks is the correct approach when dealing with potentially undefined values, rather than conditionally calling the hook based on whether the value exists.

Applied to files:

  • apps/meteor/client/views/room/MessageList/hooks/useLoadSurroundingMessages.ts
📚 Learning: 2026-03-04T14:16:49.202Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 39304
File: packages/ui-contexts/src/ActionManagerContext.ts:26-26
Timestamp: 2026-03-04T14:16:49.202Z
Learning: In `packages/ui-contexts/src/ActionManagerContext.ts` (TypeScript, RocketChat/Rocket.Chat), the `disposeView` method in `IActionManager` uses an intentionally explicit union `UiKit.ModalView['id'] | UiKit.BannerView['viewId'] | UiKit.ContextualBarView['id']` to document which view types are accepted, even though all constituents resolve to the same primitive. The inline `// eslint-disable-next-line typescript-eslint/no-duplicate-type-constituents` comment is intentional and should not be flagged or removed.

Applied to files:

  • apps/meteor/client/components/message/list/MessageListContext.tsx
📚 Learning: 2026-04-17T17:38:15.994Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 39858
File: packages/ui-kit/src/interactions/UserInteraction.ts:33-33
Timestamp: 2026-04-17T17:38:15.994Z
Learning: In RocketChat/Rocket.Chat (`packages/ui-kit/src/interactions/UserInteraction.ts`), `ViewSubmitUserInteraction` and `ViewClosedUserInteraction` intentionally do NOT include `rid` when the interaction originates from a **modal** surface. Modals are not scoped to a room, so no room id is available in that context. The `rid?: string` field is optional to support the contextual bar surface (where room context exists) while remaining absent for modals. Do not flag the absence of `rid` in modal viewSubmit/viewClosed interactions as a missing-context bug.

Applied to files:

  • apps/meteor/client/views/room/hooks/useRetentionPolicy.ts
  • apps/meteor/app/ui-utils/client/lib/RoomHistoryManager.ts
  • apps/meteor/client/views/room/body/hooks/useGetMore.ts
  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts
  • apps/meteor/client/views/room/body/hooks/useUnreadMessages.ts
  • apps/meteor/client/views/room/MessageList/MessageList.tsx
  • apps/meteor/client/views/room/body/hooks/useHasNewMessages.ts
📚 Learning: 2026-04-17T18:33:27.211Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 39858
File: apps/meteor/tests/e2e/apps/uikit-interactions.spec.ts:123-151
Timestamp: 2026-04-17T18:33:27.211Z
Learning: In RocketChat/Rocket.Chat (`apps/meteor/tests/e2e/apps/uikit-interactions.spec.ts`), `executeBlockActionHandler` invocations originating from a **modal** surface intentionally do NOT include a `block_action_room` (room property) in the interaction payload. Modals are not scoped to a room, so no room id is available in that context. Do not flag the absence of a room assertion in the modal block-action test as a missing coverage bug; instead, document it explicitly with a `test.step` asserting the room entry is `undefined`.

Applied to files:

  • apps/meteor/client/views/room/MessageList/MessageList.spec.tsx
  • apps/meteor/client/views/room/body/hooks/useHasNewMessages.spec.ts
📚 Learning: 2025-12-10T21:00:54.909Z
Learnt from: KevLehman
Repo: RocketChat/Rocket.Chat PR: 37091
File: ee/packages/abac/jest.config.ts:4-7
Timestamp: 2025-12-10T21:00:54.909Z
Learning: Rocket.Chat monorepo: Jest testMatch pattern '<rootDir>/src/**/*.spec.(ts|js|mjs)' is valid in this repo and used across multiple packages (e.g., packages/tools, ee/packages/omnichannel-services). Do not flag it as invalid in future reviews.

Applied to files:

  • apps/meteor/client/views/room/MessageList/MessageList.spec.tsx
  • apps/meteor/client/views/room/body/hooks/useHasNewMessages.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Utilize Playwright fixtures (`test`, `page`, `expect`) for consistency in test files

Applied to files:

  • apps/meteor/client/views/room/MessageList/MessageList.spec.tsx
  • apps/meteor/client/views/room/body/hooks/useHasNewMessages.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Prefer web-first assertions (`toBeVisible`, `toHaveText`, etc.) in Playwright tests

Applied to files:

  • apps/meteor/client/views/room/MessageList/MessageList.spec.tsx
  • apps/meteor/client/views/room/body/hooks/useHasNewMessages.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Group related tests in the same file

Applied to files:

  • apps/meteor/client/views/room/MessageList/MessageList.spec.tsx
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.{ts,spec.ts} : Follow Page Object Model pattern consistently in Playwright tests

Applied to files:

  • apps/meteor/client/views/room/MessageList/MessageList.spec.tsx
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Ensure tests run reliably in parallel without shared state conflicts

Applied to files:

  • apps/meteor/client/views/room/MessageList/MessageList.spec.tsx
  • apps/meteor/client/views/room/body/hooks/useHasNewMessages.spec.ts
📚 Learning: 2026-03-06T18:10:15.268Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 39397
File: packages/gazzodown/src/code/CodeBlock.spec.tsx:47-68
Timestamp: 2026-03-06T18:10:15.268Z
Learning: In tests (especially those using testing-library/dom/jsdom) for Rocket.Chat components, the HTML <code> element has an implicit ARIA role of 'code'. Therefore, screen.getByRole('code') or screen.findByRole('code') will locate <code> elements even without a role attribute. Do not flag findByRole('code') as invalid in reviews; prefer using the implicit role instead of adding role="code" unless necessary for accessibility.

Applied to files:

  • apps/meteor/client/views/room/MessageList/MessageList.spec.tsx
  • apps/meteor/client/views/room/body/hooks/useHasNewMessages.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Use `expect` matchers for assertions (`toEqual`, `toContain`, `toBeTruthy`, `toHaveLength`, etc.) instead of `assert` statements in Playwright tests

Applied to files:

  • apps/meteor/client/views/room/body/hooks/useHasNewMessages.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Maintain test isolation between test cases in Playwright tests

Applied to files:

  • apps/meteor/client/views/room/body/hooks/useHasNewMessages.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Use `test.step()` for complex test scenarios to improve organization in Playwright tests

Applied to files:

  • apps/meteor/client/views/room/body/hooks/useHasNewMessages.spec.ts
📚 Learning: 2026-02-24T19:22:48.358Z
Learnt from: juliajforesti
Repo: RocketChat/Rocket.Chat PR: 38493
File: apps/meteor/tests/e2e/omnichannel/omnichannel-send-pdf-transcript.spec.ts:66-67
Timestamp: 2026-02-24T19:22:48.358Z
Learning: In Playwright end-to-end tests (e.g., under apps/meteor/tests/e2e/...), prefer locating elements by translated text (getByText) and ARIA roles (getByRole) over data-qa attributes. If translation values change, update the corresponding test locators accordingly. Never use data-qa locators. This guideline applies to all Playwright e2e test specs in the repository and helps keep tests robust to UI text changes and accessible semantics.

Applied to files:

  • apps/meteor/client/views/room/body/hooks/useHasNewMessages.spec.ts
📚 Learning: 2026-03-11T18:17:53.972Z
Learnt from: dougfabris
Repo: RocketChat/Rocket.Chat PR: 39425
File: apps/meteor/client/lib/chats/flows/processMessageUploads.ts:112-119
Timestamp: 2026-03-11T18:17:53.972Z
Learning: In `apps/meteor/client/lib/chats/flows/processMessageUploads.ts`, when sending multiple file uploads, each file is confirmed via its own `/rooms.mediaConfirm/${rid}/${fileId}` call and produces a separate message. Only the first file's confirm payload carries the composed message text (`msg`); all subsequent files receive `msg: ''`. This one-message-per-file behavior is intentional by design — do not flag it as a bug or suggest batching into a single message.

Applied to files:

  • apps/meteor/client/views/room/MessageList/MessageList.tsx
📚 Learning: 2026-01-17T01:51:47.764Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 38219
File: packages/core-typings/src/cloud/Announcement.ts:5-6
Timestamp: 2026-01-17T01:51:47.764Z
Learning: In packages/core-typings/src/cloud/Announcement.ts, the AnnouncementSchema.createdBy field intentionally overrides IBannerSchema.createdBy (object with _id and optional username) with a string enum ['cloud', 'system'] to match existing runtime behavior. This is documented as technical debt with a FIXME comment at apps/meteor/app/cloud/server/functions/syncWorkspace/handleCommsSync.ts:53 and should not be flagged as an error until the runtime behavior is corrected.

Applied to files:

  • apps/meteor/client/views/room/MessageList/MessageList.tsx
📚 Learning: 2026-03-11T22:04:16.136Z
Learnt from: juliajforesti
Repo: RocketChat/Rocket.Chat PR: 39545
File: apps/meteor/client/views/room/body/hooks/useHasNewMessages.ts:59-61
Timestamp: 2026-03-11T22:04:16.136Z
Learning: In apps/meteor/client/views/room/body/hooks/useHasNewMessages.ts, the early-return condition that checks msg.u._id === uid in the streamNewMessage handler is intentional. The New messages indicator should notify only about messages from other users; self-sent messages (including those from a different session/device) are intentionally skipped. Do not flag this as a multi-session regression for this file.

Applied to files:

  • apps/meteor/client/views/room/body/hooks/useHasNewMessages.ts
🔇 Additional comments (11)
apps/meteor/client/views/room/MessageList/MessageListItem.tsx (1)

5-6: Import cleanup only.

This reorder doesn't change behavior; it just groups the message-list helpers together.

apps/meteor/client/components/message/list/MessageListContext.tsx (1)

2-2: Type import matches the hook signature.

KeyboardEvent | MouseEvent is the right shape for useOpenEmojiPicker, so this update looks good.

apps/meteor/client/views/room/MessageList/providers/MessageListProvider.tsx (1)

26-26: Provider API cleanup looks consistent.

Dropping the provider-level messageListRef passthrough matches the new useMessageListNavigation/Virtua wiring.

apps/meteor/client/views/room/hooks/useRetentionPolicy.ts (1)

87-122: Type alias makes the hook contract clearer.

The exported RetentionPolicy type matches the existing returned shape, and the undefined return for a missing room is consistent with the hook's usage.

packages/ui-client/src/components/CustomScrollbars/index.ts (1)

6-6: Barrel export looks good.

Re-exporting CustomVirtuaScrollbars here keeps the new scroll wrapper available through the existing public entrypoint.

apps/meteor/package.json (1)

307-307: Dependency addition matches the virtualization rollout.

Adding virtua here is consistent with the new VList-based message list and thread list components.

apps/meteor/app/ui-utils/client/lib/RoomHistoryManager.ts (1)

218-218: All getMoreNext usages have been correctly updated to the new one-argument signature. The function definition at line 218 accepts only rid, and the single call site in useGetMore.ts:61 passes exactly one argument. No unmigrated two-argument call patterns exist in the codebase.

apps/meteor/client/views/room/MessageList/MessageList.spec.tsx (1)

140-201: Good targeted coverage for virtualizer scroll state behavior.

These cases validate the critical restore/update paths introduced by the virtualization refactor.

apps/meteor/client/views/room/body/hooks/useGetMore.ts (1)

27-62: Jump-aware short-circuiting and cleanup look solid here.

The fetch/restore flow is correctly guarded during jump-to-message and keeps observer/listener teardown intact.

apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToThreadMessage.ts (1)

35-64: Thread deep-link routing flow looks consistent.

The guard clauses and navigation branching are clean and match the intended thread-only handling.

apps/meteor/client/views/room/body/hooks/useHasNewMessages.ts (1)

83-95: Ref-based debounced clearing integrates well with the new scroll model.

The returned debouncedClearNewMessagesOnScroll cleanly supports virtualizer-driven scroll updates.

Comment on lines 57 to 59
it('should NOT show new messages button when user sends own message during race condition (not at bottom)', () => {
const scrollBehavior = {
...mockScrollBehavior,
isAtBottom: jest.fn(() => false),
};

const { result } = renderHook(() => useHasNewMessages(rid, uid, atBottomRef, scrollBehavior), { wrapper: mockAppRoot().build() });
const { result } = renderHook(() => useHasNewMessages(rid, uid, shouldJumpToBottom, isAtBottom), { wrapper: mockAppRoot().build() });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

The "not at bottom" precondition is not actually set in this test.

Line 57 says this covers the "not at bottom" race, but Line 58 passes isAtBottom (initialized true). This test currently validates only the self-message branch, not the intended scroll-state branch.

Proposed fix
-const { result } = renderHook(() => useHasNewMessages(rid, uid, shouldJumpToBottom, isAtBottom), { wrapper: mockAppRoot().build() });
+const { result } = renderHook(() => useHasNewMessages(rid, uid, shouldJumpToBottom, { current: false }), {
+	wrapper: mockAppRoot().build(),
+});
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('should NOT show new messages button when user sends own message during race condition (not at bottom)', () => {
const scrollBehavior = {
...mockScrollBehavior,
isAtBottom: jest.fn(() => false),
};
const { result } = renderHook(() => useHasNewMessages(rid, uid, atBottomRef, scrollBehavior), { wrapper: mockAppRoot().build() });
const { result } = renderHook(() => useHasNewMessages(rid, uid, shouldJumpToBottom, isAtBottom), { wrapper: mockAppRoot().build() });
it('should NOT show new messages button when user sends own message during race condition (not at bottom)', () => {
const { result } = renderHook(() => useHasNewMessages(rid, uid, shouldJumpToBottom, { current: false }), {
wrapper: mockAppRoot().build(),
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/meteor/client/views/room/body/hooks/useHasNewMessages.spec.ts` around
lines 57 - 59, The test "should NOT show new messages button when user sends own
message during race condition (not at bottom)" sets isAtBottom true, so it never
exercises the "not at bottom" branch; update the test that calls renderHook(()
=> useHasNewMessages(rid, uid, shouldJumpToBottom, isAtBottom), { wrapper: ...
}) to pass isAtBottom as false (or use the existing variable set to false) so
the hook runs the not-at-bottom race-condition path for self-sent messages;
ensure the variables referenced (rid, uid, shouldJumpToBottom, isAtBottom) and
the renderHook call are adjusted accordingly.

Comment thread apps/meteor/client/views/room/body/hooks/useStoreScrollPosition.ts
Comment thread apps/meteor/client/views/room/hooks/useDateScroll.ts
Comment thread apps/meteor/client/views/room/hooks/useDateScroll.ts
Comment thread apps/meteor/client/views/room/MessageList/MessageList.tsx Outdated
Comment thread apps/meteor/client/views/room/MessageList/MessageList.tsx
@MartinSchoeler
Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 28, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot removed the type: feature Pull requests that introduces new feature label Apr 28, 2026
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

♻️ Duplicate comments (1)
apps/meteor/client/views/room/MessageList/MessageList.tsx (1)

46-47: ⚠️ Potential issue | 🟠 Major

Module-level mutable state causes cross-instance pollution.

lastViewportSize is declared at module scope, meaning it's shared across all MessageList instances. If multiple rooms are open (e.g., in different tabs or panels), they'll interfere with each other's viewport tracking.

🔧 Proposed fix: Convert to ref inside component
-let lastViewportSize = 0;
-
 export const MessageList = function MessageList({
   // ...props
 }: MessageListProps) {
+  const lastViewportSizeRef = useRef(0);
   // ...
   
-  if (isAtBottom.current && lastViewportSize !== handle?.viewportSize) {
-    lastViewportSize = handle?.viewportSize ?? 0;
+  if (isAtBottom.current && lastViewportSizeRef.current !== handle?.viewportSize) {
+    lastViewportSizeRef.current = handle?.viewportSize ?? 0;
     handle?.scrollToIndex(lastItemIndex + 1, {
       align: 'end',
     });
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/meteor/client/views/room/MessageList/MessageList.tsx` around lines 46 -
47, lastViewportSize is a module-level mutable variable shared across all
MessageList instances causing cross-instance interference; move this state into
the MessageList component as an instance-scoped ref (e.g., useRef) so each
component keeps its own viewport value, update any reads/writes to reference the
ref (current) inside the MessageList render/effects, and remove the module-level
declaration to prevent global pollution.
🧹 Nitpick comments (1)
apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts (1)

89-89: Unused router in dependency array.

router is included in the useEffect dependency array but is not used in the effect body. The query parameter is already cleared via setMessageJumpQueryStringParameter(null) at line 88.

🔧 Proposed fix
-	}, [messageJumpParam, virtualizerRef, isJumpingToMessage, rid, messages, router, message]);
+	}, [messageJumpParam, virtualizerRef, isJumpingToMessage, rid, messages, message]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts` at
line 89, In the useEffect inside useTryToJumpToMessage (the effect with
dependencies [messageJumpParam, virtualizerRef, isJumpingToMessage, rid,
messages, router, message]), remove the unused router from the dependency array
(or alternatively reference router in the effect if you intended to use it);
update the dependency list to [messageJumpParam, virtualizerRef,
isJumpingToMessage, rid, messages, message] so it only contains variables
actually used in the effect body (ensure ESLint/React hooks rules remain
satisfied).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/meteor/client/views/room/MessageList/MessageList.tsx`:
- Around line 234-239: The unread mark is being nulled inside the per-message
mapping which causes later messages to overwrite the found index; before
iterating, reset unreadMarkIndex.current = null once, and inside the loop only
set unreadMarkIndex.current = index when showUnreadDivider is true (remove the
else branch that clears it). Update the logic around the map that computes
showUnreadDivider/unreadMarkIndex (referencing unreadMarkIndex,
showUnreadDivider and isUnreadMarkVisible) so the index is initialized once
before the loop and only assigned when the divider is encountered.
- Line 147: The effect's dependency array incorrectly uses ref `.current` values
(shouldJumpToBottom.current, isJumpingToMessage.current) which don't trigger
reruns; update the dependency array to either remove those `.current` entries
entirely if the effect doesn't need to rerun on ref mutations, or replace them
with the ref objects (shouldJumpToBottom, isJumpingToMessage) so React sees
stable refs (but still won't rerun on .current changes) and the effect can read
latest values inside its body; adjust the useEffect dependency list where it's
declared to reference shouldJumpToBottom and isJumpingToMessage (or omit them)
along with the existing messages, rid, isAtBottom, firstUnreadMessageId.

---

Duplicate comments:
In `@apps/meteor/client/views/room/MessageList/MessageList.tsx`:
- Around line 46-47: lastViewportSize is a module-level mutable variable shared
across all MessageList instances causing cross-instance interference; move this
state into the MessageList component as an instance-scoped ref (e.g., useRef) so
each component keeps its own viewport value, update any reads/writes to
reference the ref (current) inside the MessageList render/effects, and remove
the module-level declaration to prevent global pollution.

---

Nitpick comments:
In `@apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts`:
- Line 89: In the useEffect inside useTryToJumpToMessage (the effect with
dependencies [messageJumpParam, virtualizerRef, isJumpingToMessage, rid,
messages, router, message]), remove the unused router from the dependency array
(or alternatively reference router in the effect if you intended to use it);
update the dependency list to [messageJumpParam, virtualizerRef,
isJumpingToMessage, rid, messages, message] so it only contains variables
actually used in the effect body (ensure ESLint/React hooks rules remain
satisfied).
🪄 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: c1f2746e-014d-4308-8301-056f4cb86635

📥 Commits

Reviewing files that changed from the base of the PR and between bdcc7e4 and 0658a90.

📒 Files selected for processing (6)
  • apps/meteor/client/views/room/MessageList/MessageList.tsx
  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts
  • apps/meteor/client/views/room/body/hooks/useGetMore.spec.tsx
  • apps/meteor/client/views/room/body/hooks/useStoreScrollPosition.ts
  • apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageList.tsx
  • apps/meteor/client/views/room/hooks/useDateScroll.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/meteor/client/views/room/body/hooks/useStoreScrollPosition.ts
  • apps/meteor/client/views/room/hooks/useDateScroll.ts
  • apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageList.tsx
📜 Review details
⏰ 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). (2)
  • GitHub Check: 📦 Build Packages
  • GitHub Check: CodeQL-Build
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js}

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

**/*.{ts,tsx,js}: Write concise, technical TypeScript/JavaScript with accurate typing in Playwright tests
Avoid code comments in the implementation

Files:

  • apps/meteor/client/views/room/body/hooks/useGetMore.spec.tsx
  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts
  • apps/meteor/client/views/room/MessageList/MessageList.tsx
🧠 Learnings (33)
📓 Common learnings
Learnt from: ggazzo
Repo: RocketChat/Rocket.Chat PR: 35995
File: apps/meteor/app/api/server/v1/rooms.ts:1107-1112
Timestamp: 2026-02-23T17:53:18.785Z
Learning: In Rocket.Chat PR reviews, maintain strict scope boundaries—when a PR is focused on a specific endpoint (e.g., rooms.favorite), avoid reviewing or suggesting changes to other endpoints that were incidentally refactored (e.g., rooms.invite) unless explicitly requested by maintainers.
Learnt from: smirk-dev
Repo: RocketChat/Rocket.Chat PR: 39625
File: apps/meteor/app/api/server/v1/push.ts:85-97
Timestamp: 2026-03-14T14:58:58.834Z
Learning: In RocketChat/Rocket.Chat, the `push.token` POST/DELETE endpoints in `apps/meteor/app/api/server/v1/push.ts` were already migrated to the chained router API pattern on `develop` prior to PR `#39625`. `cleanTokenResult` (which strips `authToken` and returns `PushTokenResult`) and `isPushTokenPOSTProps`/`isPushTokenDELETEProps` validators already exist on `develop`. PR `#39625` only migrates `push.get` and `push.info` to the chained pattern. Do not flag `cleanTokenResult` or `PushTokenResult` as newly introduced behavior-breaking changes when reviewing this PR.
Learnt from: juliajforesti
Repo: RocketChat/Rocket.Chat PR: 38493
File: apps/meteor/tests/e2e/page-objects/fragments/home-content.ts:60-82
Timestamp: 2026-02-24T19:36:55.089Z
Learning: In RocketChat/Rocket.Chat e2e tests (apps/meteor/tests/e2e/page-objects/fragments/home-content.ts), thread message preview listitems do not have aria-roledescription="message", so lastThreadMessagePreview locator cannot be scoped to messageListItems (which filters for aria-roledescription="message"). It should remain scoped to page.getByRole('listitem') or mainMessageList.getByRole('listitem').
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Use `expect` matchers for assertions (`toEqual`, `toContain`, `toBeTruthy`, `toHaveLength`, etc.) instead of `assert` statements in Playwright tests

Applied to files:

  • apps/meteor/client/views/room/body/hooks/useGetMore.spec.tsx
📚 Learning: 2026-04-17T18:33:27.211Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 39858
File: apps/meteor/tests/e2e/apps/uikit-interactions.spec.ts:123-151
Timestamp: 2026-04-17T18:33:27.211Z
Learning: In RocketChat/Rocket.Chat (`apps/meteor/tests/e2e/apps/uikit-interactions.spec.ts`), `executeBlockActionHandler` invocations originating from a **modal** surface intentionally do NOT include a `block_action_room` (room property) in the interaction payload. Modals are not scoped to a room, so no room id is available in that context. Do not flag the absence of a room assertion in the modal block-action test as a missing coverage bug; instead, document it explicitly with a `test.step` asserting the room entry is `undefined`.

Applied to files:

  • apps/meteor/client/views/room/body/hooks/useGetMore.spec.tsx
📚 Learning: 2026-03-11T22:04:20.529Z
Learnt from: juliajforesti
Repo: RocketChat/Rocket.Chat PR: 39545
File: apps/meteor/client/views/room/body/hooks/useHasNewMessages.ts:59-61
Timestamp: 2026-03-11T22:04:20.529Z
Learning: In `apps/meteor/client/views/room/body/hooks/useHasNewMessages.ts`, the `msg.u._id === uid` early-return in the `streamNewMessage` handler is intentional: the "New messages" indicator is designed to notify about messages from other users only. Self-sent messages — including those sent from a different session/device — are always skipped, by design. Do not flag this as a multi-session regression.

Applied to files:

  • apps/meteor/client/views/room/body/hooks/useGetMore.spec.tsx
  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts
  • apps/meteor/client/views/room/MessageList/MessageList.tsx
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Utilize Playwright fixtures (`test`, `page`, `expect`) for consistency in test files

Applied to files:

  • apps/meteor/client/views/room/body/hooks/useGetMore.spec.tsx
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Prefer web-first assertions (`toBeVisible`, `toHaveText`, etc.) in Playwright tests

Applied to files:

  • apps/meteor/client/views/room/body/hooks/useGetMore.spec.tsx
📚 Learning: 2025-12-10T21:00:54.909Z
Learnt from: KevLehman
Repo: RocketChat/Rocket.Chat PR: 37091
File: ee/packages/abac/jest.config.ts:4-7
Timestamp: 2025-12-10T21:00:54.909Z
Learning: Rocket.Chat monorepo: Jest testMatch pattern '<rootDir>/src/**/*.spec.(ts|js|mjs)' is valid in this repo and used across multiple packages (e.g., packages/tools, ee/packages/omnichannel-services). Do not flag it as invalid in future reviews.

Applied to files:

  • apps/meteor/client/views/room/body/hooks/useGetMore.spec.tsx
📚 Learning: 2025-12-16T17:29:45.163Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 37834
File: apps/meteor/tests/e2e/page-objects/fragments/admin-flextab-emoji.ts:12-22
Timestamp: 2025-12-16T17:29:45.163Z
Learning: In page object files under `apps/meteor/tests/e2e/page-objects/`, always import `expect` from `../../utils/test` (Playwright's async expect), not from Jest. Jest's `expect` has a synchronous signature and will cause TypeScript errors when used with web-first assertions like `toBeVisible()`.

Applied to files:

  • apps/meteor/client/views/room/body/hooks/useGetMore.spec.tsx
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Use `test.step()` for complex test scenarios to improve organization in Playwright tests

Applied to files:

  • apps/meteor/client/views/room/body/hooks/useGetMore.spec.tsx
📚 Learning: 2026-04-18T12:32:53.425Z
Learnt from: rodrigok
Repo: RocketChat/Rocket.Chat PR: 38623
File: apps/meteor/app/lib/server/functions/cleanRoomHistory.ts:146-149
Timestamp: 2026-04-18T12:32:53.425Z
Learning: In `apps/meteor/app/lib/server/functions/cleanRoomHistory.ts` (PR `#38623`), the read-receipt cleanup (both `ReadReceipts.removeByMessageIds` and `ReadReceiptsArchive.removeByMessageIds`) is intentionally only performed in the limited prune path (`limit && selectedMessageIds`). The unlimited/delete-all path (`limit === 0`) deliberately skips cleaning up orphaned read receipts in both hot and cold storage — this is by design. Do not flag this as a bug or missing cleanup in future reviews.

Applied to files:

  • apps/meteor/client/views/room/body/hooks/useGetMore.spec.tsx
  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts
  • apps/meteor/client/views/room/MessageList/MessageList.tsx
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Ensure tests run reliably in parallel without shared state conflicts

Applied to files:

  • apps/meteor/client/views/room/body/hooks/useGetMore.spec.tsx
📚 Learning: 2026-03-06T18:10:15.268Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 39397
File: packages/gazzodown/src/code/CodeBlock.spec.tsx:47-68
Timestamp: 2026-03-06T18:10:15.268Z
Learning: In tests (especially those using testing-library/dom/jsdom) for Rocket.Chat components, the HTML <code> element has an implicit ARIA role of 'code'. Therefore, screen.getByRole('code') or screen.findByRole('code') will locate <code> elements even without a role attribute. Do not flag findByRole('code') as invalid in reviews; prefer using the implicit role instead of adding role="code" unless necessary for accessibility.

Applied to files:

  • apps/meteor/client/views/room/body/hooks/useGetMore.spec.tsx
📚 Learning: 2026-03-27T14:52:56.865Z
Learnt from: dougfabris
Repo: RocketChat/Rocket.Chat PR: 39892
File: apps/meteor/client/views/room/contextualBar/Threads/Thread.tsx:150-155
Timestamp: 2026-03-27T14:52:56.865Z
Learning: In Rocket.Chat, there are two different `ModalBackdrop` components with different prop APIs. During review, confirm the import source: (1) `rocket.chat/fuselage` `ModalBackdrop` uses `ModalBackdropProps` based on `BoxProps` (so it supports `onClick` and other Box/DOM props) and does not have an `onDismiss` prop; (2) `rocket.chat/ui-client` `ModalBackdrop` uses a narrower props interface like `{ children?: ReactNode; onDismiss?: () => void }` and handles Escape keypress and outside mouse-up, and it does not forward arbitrary DOM props such as `onClick`. Flag mismatched props (e.g., `onDismiss` passed to the fuselage component or `onClick` passed to the ui-client component) and ensure the usage matches the correct component being imported.

Applied to files:

  • apps/meteor/client/views/room/body/hooks/useGetMore.spec.tsx
  • apps/meteor/client/views/room/MessageList/MessageList.tsx
📚 Learning: 2026-04-28T14:08:43.140Z
Learnt from: MartinSchoeler
Repo: RocketChat/Rocket.Chat PR: 40105
File: apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts:54-67
Timestamp: 2026-04-28T14:08:43.140Z
Learning: In `apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts`, setting `isJumpingToMessage.current = true` before the guard clauses (RoomHistoryManager.isLoading check, message not found check) is intentional. The flag means "a jump is pending/in progress" and must stay `true` through all intermediate early-return paths (loading, unresolved message, etc.) so that downstream scroll and load behavior is suppressed while waiting for the jump conditions to be satisfied. Do not flag this as a "flag stuck true" bug.

Applied to files:

  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts
  • apps/meteor/client/views/room/MessageList/MessageList.tsx
📚 Learning: 2026-02-24T19:36:55.089Z
Learnt from: juliajforesti
Repo: RocketChat/Rocket.Chat PR: 38493
File: apps/meteor/tests/e2e/page-objects/fragments/home-content.ts:60-82
Timestamp: 2026-02-24T19:36:55.089Z
Learning: In RocketChat/Rocket.Chat e2e tests (apps/meteor/tests/e2e/page-objects/fragments/home-content.ts), thread message preview listitems do not have aria-roledescription="message", so lastThreadMessagePreview locator cannot be scoped to messageListItems (which filters for aria-roledescription="message"). It should remain scoped to page.getByRole('listitem') or mainMessageList.getByRole('listitem').

Applied to files:

  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts
  • apps/meteor/client/views/room/MessageList/MessageList.tsx
📚 Learning: 2026-04-17T17:38:15.994Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 39858
File: packages/ui-kit/src/interactions/UserInteraction.ts:33-33
Timestamp: 2026-04-17T17:38:15.994Z
Learning: In RocketChat/Rocket.Chat (`packages/ui-kit/src/interactions/UserInteraction.ts`), `ViewSubmitUserInteraction` and `ViewClosedUserInteraction` intentionally do NOT include `rid` when the interaction originates from a **modal** surface. Modals are not scoped to a room, so no room id is available in that context. The `rid?: string` field is optional to support the contextual bar surface (where room context exists) while remaining absent for modals. Do not flag the absence of `rid` in modal viewSubmit/viewClosed interactions as a missing-context bug.

Applied to files:

  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts
  • apps/meteor/client/views/room/MessageList/MessageList.tsx
📚 Learning: 2026-04-13T16:40:55.864Z
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 40100
File: apps/meteor/client/views/root/MainLayout/EmbeddedPreload.tsx:18-35
Timestamp: 2026-04-13T16:40:55.864Z
Learning: In Rocket.Chat's embedded mode (`?layout=embedded`), route changes are driven by the parent frame reloading the iframe URL, which causes a full remount of the React tree (including `EmbeddedPreload` at `apps/meteor/client/views/root/MainLayout/EmbeddedPreload.tsx`). The component never survives a navigation, so `roomParams` (computed via `useMemo`) is always fresh on mount. A `[router]` singleton as the sole `useMemo` dependency is therefore correct and intentional — do not flag it as a stale-dependency bug in this component.

Applied to files:

  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts
  • apps/meteor/client/views/room/MessageList/MessageList.tsx
📚 Learning: 2026-04-17T23:32:07.223Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 38357
File: apps/meteor/app/apps/server/converters/messages.ts:348-352
Timestamp: 2026-04-17T23:32:07.223Z
Learning: In `apps/meteor/app/apps/server/converters/messages.ts`, the `timestamp` handler inside `_convertAttachmentsToApp` uses a non-null assertion (`attachment.ts!`) intentionally. The `ts` property on `MessageAttachment` is optional only to accommodate MessageAttachment creation-time scenarios; by the time `_convertAttachmentsToApp` is called, the message has already been stored and the attachment is guaranteed to have a `ts` value. Do not flag this non-null assertion as unsafe during code review.

Applied to files:

  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts
📚 Learning: 2026-02-24T19:16:35.307Z
Learnt from: sampaiodiego
Repo: RocketChat/Rocket.Chat PR: 39003
File: apps/meteor/client/lib/chats/flows/sendMessage.ts:39-45
Timestamp: 2026-02-24T19:16:35.307Z
Learning: In apps/meteor/client/lib/chats/flows/sendMessage.ts, when sdk.call('sendMessage', ...) throws an error, the message is intentionally left with temp: true (not deleted or cleaned up) to support a future retry UI feature. This allows users to retry sending failed messages rather than losing them.

Applied to files:

  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts
📚 Learning: 2026-04-20T17:11:59.452Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 40225
File: apps/meteor/ee/server/apps/communication/endpoints/appLogsHandler.ts:55-71
Timestamp: 2026-04-20T17:11:59.452Z
Learning: In `apps/meteor/ee/server/apps/communication/endpoints/appLogsHandler.ts`, the concern about an empty `?appId=` query param bypassing the truthy check and overriding the path `appId` in the `makeAppLogsQuery` spread is not relevant. The AJV query schema (`isAppLogsProps`) validates and rejects invalid/empty `appId` values before the action handler is reached, making the in-handler guard sufficient as-is. Do not flag this pattern as a vulnerability in future reviews of this file.

Applied to files:

  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts
📚 Learning: 2026-03-16T22:56:54.500Z
Learnt from: KevLehman
Repo: RocketChat/Rocket.Chat PR: 39677
File: packages/models/src/helpers/omnichannel/agentStatus.ts:10-29
Timestamp: 2026-03-16T22:56:54.500Z
Learning: In `packages/models/src/helpers/omnichannel/agentStatus.ts` (PR `#39677`), the `queryStatusAgentOnline` function intentionally omits the `$or` offline-status guard for non-bot agents when `isLivechatEnabledWhenAgentIdle === true`. This is by design: the setting `Livechat_enabled_when_agent_idle` (`accept_chats_when_agent_idle`) means agents should receive chats even when idle/offline, so the offline filter must be removed in that path. Bots are always status-agnostic and are always included regardless of their online/offline status. Do not flag this as a bug.

Applied to files:

  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts
  • apps/meteor/client/views/room/MessageList/MessageList.tsx
📚 Learning: 2026-03-20T13:51:23.302Z
Learnt from: ggazzo
Repo: RocketChat/Rocket.Chat PR: 39553
File: apps/meteor/app/integrations/server/methods/incoming/updateIncomingIntegration.ts:179-181
Timestamp: 2026-03-20T13:51:23.302Z
Learning: In `apps/meteor/app/integrations/server/methods/incoming/updateIncomingIntegration.ts`, the truthiness guards `...(integration.avatar && { avatar })`, `...(integration.emoji && { emoji })`, `...(integration.alias && { alias })`, and `...(integration.script && { script })` in the `$set` payload of `updateIncomingIntegration` are intentional. Empty-string values for these fields should NOT overwrite the stored value — only truthy values are persisted. Do not flag these as bugs preventing explicit clears.

Applied to files:

  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts
  • apps/meteor/client/views/room/MessageList/MessageList.tsx
📚 Learning: 2026-01-17T01:51:47.764Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 38219
File: packages/core-typings/src/cloud/Announcement.ts:5-6
Timestamp: 2026-01-17T01:51:47.764Z
Learning: In packages/core-typings/src/cloud/Announcement.ts, the AnnouncementSchema.createdBy field intentionally overrides IBannerSchema.createdBy (object with _id and optional username) with a string enum ['cloud', 'system'] to match existing runtime behavior. This is documented as technical debt with a FIXME comment at apps/meteor/app/cloud/server/functions/syncWorkspace/handleCommsSync.ts:53 and should not be flagged as an error until the runtime behavior is corrected.

Applied to files:

  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts
📚 Learning: 2026-03-10T08:13:52.153Z
Learnt from: ahmed-n-abdeltwab
Repo: RocketChat/Rocket.Chat PR: 39414
File: apps/meteor/app/api/server/v1/rooms.ts:1241-1297
Timestamp: 2026-03-10T08:13:52.153Z
Learning: In the RocketChat/Rocket.Chat OpenAPI migration PRs for endpoints under apps/meteor/app/api/server/v1/rooms.ts, the pattern `ajv.compile<void>({...})` is intentionally used for the 200 response schema even when the endpoint returns `{ success: true }`. This is an established convention across all migrated endpoints (rooms.leave, rooms.favorite, rooms.delete, rooms.muteUser, rooms.unmuteUser). Do not flag this as a type mismatch during reviews of these migration PRs.

Applied to files:

  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts
📚 Learning: 2026-04-14T21:10:36.233Z
Learnt from: dougfabris
Repo: RocketChat/Rocket.Chat PR: 36292
File: apps/meteor/client/hooks/useHasValidLocationHash.ts:7-12
Timestamp: 2026-04-14T21:10:36.233Z
Learning: In the RocketChat/Rocket.Chat repository, adding JSDoc-style comments to React hooks (e.g., files under apps/meteor/client/hooks/) is considered a good pattern and should not be flagged as a violation of the "avoid code comments in the implementation" guideline. The guideline against code comments does not apply to JSDoc documentation on exported hooks.

Applied to files:

  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts
📚 Learning: 2025-11-10T19:06:20.146Z
Learnt from: MartinSchoeler
Repo: RocketChat/Rocket.Chat PR: 37408
File: apps/meteor/client/views/admin/ABAC/useRoomAttributeOptions.tsx:53-69
Timestamp: 2025-11-10T19:06:20.146Z
Learning: In the Rocket.Chat repository, do not provide suggestions or recommendations about code sections marked with TODO comments. The maintainers have already identified these as future work and external reviewers lack the full context about implementation plans and timing.

Applied to files:

  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts
📚 Learning: 2026-02-23T17:53:18.785Z
Learnt from: ggazzo
Repo: RocketChat/Rocket.Chat PR: 35995
File: apps/meteor/app/api/server/v1/rooms.ts:1107-1112
Timestamp: 2026-02-23T17:53:18.785Z
Learning: In Rocket.Chat PR reviews, maintain strict scope boundaries—when a PR is focused on a specific endpoint (e.g., rooms.favorite), avoid reviewing or suggesting changes to other endpoints that were incidentally refactored (e.g., rooms.invite) unless explicitly requested by maintainers.

Applied to files:

  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts
📚 Learning: 2026-03-06T18:10:23.330Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 39397
File: packages/gazzodown/src/code/CodeBlock.spec.tsx:47-68
Timestamp: 2026-03-06T18:10:23.330Z
Learning: In the RocketChat/Rocket.Chat `packages/gazzodown` package and more broadly, the HTML `<code>` element has an implicit ARIA role of `code` per WAI-ARIA 1.3, and `testing-library/dom` / jsdom supports it. Therefore, `screen.getByRole('code')` / `screen.findByRole('code')` correctly locates `<code>` elements without needing an explicit `role="code"` attribute. Do NOT flag `findByRole('code')` as invalid in future reviews.

Applied to files:

  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts
📚 Learning: 2026-02-10T16:32:42.586Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 38528
File: apps/meteor/client/startup/roles.ts:14-14
Timestamp: 2026-02-10T16:32:42.586Z
Learning: In Rocket.Chat's Meteor client code, DDP streams use EJSON and Date fields arrive as Date objects; do not manually construct new Date() in stream handlers (for example, in sdk.stream()). Only REST API responses return plain JSON where dates are strings, so implement explicit conversion there if needed. Apply this guidance to all TypeScript files under apps/meteor/client to ensure consistent date handling in DDP streams and REST responses.

Applied to files:

  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In the Rocket.Chat repository, do not reference Biome lint rules in code review feedback. Biome is not used even if biome.json exists; only reference Biome rules if there is explicit, project-wide usage documented. For TypeScript files, review lint implications without Biome guidance unless the project enables Biome rules.

Applied to files:

  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In this repository (RocketChat/Rocket.Chat), Biome lint rules are not used even if a biome.json exists. When reviewing TypeScript files (e.g., packages/ui-voip/src/providers/useMediaSession.ts), ensure lint suggestions do not reference Biome-specific rules. Rely on general ESLint/TypeScript lint rules and project conventions instead.

Applied to files:

  • apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts
📚 Learning: 2026-04-10T22:42:05.539Z
Learnt from: dougfabris
Repo: RocketChat/Rocket.Chat PR: 40075
File: apps/meteor/client/views/room/modals/FileUploadModal/FileUploadModal.tsx:69-71
Timestamp: 2026-04-10T22:42:05.539Z
Learning: In `apps/meteor/client/views/room/modals/FileUploadModal/FileUploadModal.tsx`, the submit handler converts an empty/whitespace-only description to `undefined` (`description?.trim() || undefined`) intentionally. All downstream image-rendering components (`AttachmentImage`, `ImagePreview`, `ImageItem`, `ImageGallery`) default `undefined` alt to `''`, so the `<img alt="">` attribute is always present. Do not flag this `undefined` conversion as a bug preventing alt text from being cleared.

Applied to files:

  • apps/meteor/client/views/room/MessageList/MessageList.tsx
📚 Learning: 2026-03-04T14:16:49.202Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 39304
File: packages/ui-contexts/src/ActionManagerContext.ts:26-26
Timestamp: 2026-03-04T14:16:49.202Z
Learning: In `packages/ui-contexts/src/ActionManagerContext.ts` (TypeScript, RocketChat/Rocket.Chat), the `disposeView` method in `IActionManager` uses an intentionally explicit union `UiKit.ModalView['id'] | UiKit.BannerView['viewId'] | UiKit.ContextualBarView['id']` to document which view types are accepted, even though all constituents resolve to the same primitive. The inline `// eslint-disable-next-line typescript-eslint/no-duplicate-type-constituents` comment is intentional and should not be flagged or removed.

Applied to files:

  • apps/meteor/client/views/room/MessageList/MessageList.tsx
🔇 Additional comments (5)
apps/meteor/client/views/room/body/hooks/useGetMore.spec.tsx (1)

94-94: LGTM!

The updated assertion correctly matches the RoomHistoryManager.getMoreNext(rid) signature, which now accepts only a single rid argument. This aligns with the implementation in RoomHistoryManager.ts.

apps/meteor/client/views/room/MessageList/hooks/useTryToJumpToMessage.ts (1)

21-35: LGTM!

The React Query setup correctly fetches the message only when messageJumpParam is present. The mapMessageFromApi transformation properly converts API date fields to Date objects, matching the internal IMessage shape.

apps/meteor/client/views/room/MessageList/MessageList.tsx (3)

115-127: LGTM on scroll restoration logic.

The scroll restoration correctly handles both cases: converting stored scroll offset to an item index via findItemIndex() for index-based scrolling, and falling back to direct scrollTo() for offset-based scrolling when the index lookup fails.


78-93: LGTM on prepend handling and bottom detection.

The handlePrepend callback correctly:

  1. Enables shift mode when scrolled near top (offset < 200) for smooth prepending
  2. Calculates isAtBottom based on scroll position relative to scroll size and viewport
  3. Resets shouldJumpToBottom when the user is at the bottom

159-176: LGTM on unread mark visibility detection.

The isUnreadMarkVisible callback correctly accounts for the leading preview row offset and computes the visible viewport range using findItemIndex(). The min/max comparison handles both scroll directions.

align: 'end',
});
}
}, [isAtBottom, messages, shouldJumpToBottom.current, isJumpingToMessage.current, rid, firstUnreadMessageId]);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Ref .current values in dependency array don't trigger effect re-runs.

shouldJumpToBottom.current and isJumpingToMessage.current are ref values. React's useEffect doesn't track ref mutations, so the effect won't re-run when these values change. The effect only runs when messages or rid change.

If the intent is to read the latest ref values during each effect run (not to trigger re-runs), remove .current from the dependencies and keep only the refs themselves, or remove them entirely since refs are stable:

🔧 Proposed fix
-	}, [isAtBottom, messages, shouldJumpToBottom.current, isJumpingToMessage.current, rid, firstUnreadMessageId]);
+	}, [isAtBottom, messages, shouldJumpToBottom, isJumpingToMessage, rid, firstUnreadMessageId]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
}, [isAtBottom, messages, shouldJumpToBottom.current, isJumpingToMessage.current, rid, firstUnreadMessageId]);
}, [isAtBottom, messages, shouldJumpToBottom, isJumpingToMessage, rid, firstUnreadMessageId]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/meteor/client/views/room/MessageList/MessageList.tsx` at line 147, The
effect's dependency array incorrectly uses ref `.current` values
(shouldJumpToBottom.current, isJumpingToMessage.current) which don't trigger
reruns; update the dependency array to either remove those `.current` entries
entirely if the effect doesn't need to rerun on ref mutations, or replace them
with the ref objects (shouldJumpToBottom, isJumpingToMessage) so React sees
stable refs (but still won't rerun on .current changes) and the effect can read
latest values inside its body; adjust the useEffect dependency list where it's
declared to reference shouldJumpToBottom and isJumpingToMessage (or omit them)
along with the existing messages, rid, isAtBottom, firstUnreadMessageId.

Comment on lines +234 to +239
if (showUnreadDivider) {
unreadMarkIndex.current = index;
} else {
// Manually unsets the unread mark index when the divider is not visible to avoid stale state
unreadMarkIndex.current = null;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Unread mark index is incorrectly reset by subsequent messages.

The else branch (line 238) resets unreadMarkIndex.current = null for every message that doesn't show the divider. Since this runs inside the .map() loop, messages after the unread divider will overwrite the index with null.

For example, if the divider is at message index 5 out of 10, messages 6-9 will each set it back to null, and isUnreadMarkVisible() will always return false.

🔧 Proposed fix: Reset once before the loop, set only when divider found
+	// Reset before rendering messages
+	unreadMarkIndex.current = null;
+
 	{messages.map((message, index, { [index - 1]: previous }) => {
 		const sequential = isMessageSequential(message, previous, messageGroupingPeriod);
 		const showUnreadDivider = firstUnreadMessageId === message._id;
 		const system = MessageTypes.isSystemMessage(message);
 		const visible = !isThreadMessage(message) && !system;

 		if (showUnreadDivider) {
 			unreadMarkIndex.current = index;
-		} else {
-			// Manually unsets the unread mark index when the divider is not visible to avoid stale state
-			unreadMarkIndex.current = null;
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/meteor/client/views/room/MessageList/MessageList.tsx` around lines 234 -
239, The unread mark is being nulled inside the per-message mapping which causes
later messages to overwrite the found index; before iterating, reset
unreadMarkIndex.current = null once, and inside the loop only set
unreadMarkIndex.current = index when showUnreadDivider is true (remove the else
branch that clears it). Update the logic around the map that computes
showUnreadDivider/unreadMarkIndex (referencing unreadMarkIndex,
showUnreadDivider and isUnreadMarkVisible) so the index is initialized once
before the loop and only assigned when the divider is encountered.

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