Skip to content

Conversation

@aleksandernsilva
Copy link
Contributor

@aleksandernsilva aleksandernsilva commented Jan 8, 2026

Proposed changes (including videos or screenshots)

This pull request refactors how query invalidation and navigation are handled after accepting or rejecting a room invite. The logic for invalidating queries is moved out of the useRoomInvitation hook and into the RoomInvite component, making responsibilities clearer and improving separation of concerns. Additionally, some tests related to navigation on invite actions are removed and will be handled by a general hook for user removal.

This is necessary due to recent changes made to the federation backend streamlining the room acceptance flow. Due to asynchronous nature of the room join events, invalidating the room query right away introduces a race condition where fetched data might still be outdated, causing room stream access to be denied, resulting in the room entering an invalid state.

Refactoring and Separation of Concerns:

  • Moved query invalidation logic from the useRoomInvitation hook to the RoomInvite component, now using useEffect and a new invalidateQueries callback to invalidate relevant queries when the component unmounts (invite accepted or rejected). (apps/meteor/client/views/room/RoomInvite.tsx, apps/meteor/client/views/room/hooks/useRoomInvitation.tsx) [1] [2] [3] [4]

Testing Updates:

  • Removed tests that checked for navigation to /home after invite rejection or acceptance, reflecting the fact that navigation is no longer handled in the useRoomInvitation hook. (apps/meteor/client/views/room/hooks/useRoomInvitation.spec.tsx)

Issue(s)

FB-172

Steps to test or reproduce

  1. Element User: Create a room and invite a Rocket.Chat user.
  2. Rocket.Chat User: Navigate to the room and click "Accept".
  3. Observation: Room history populates.
  4. Observation: The message composer remains in a permanent loading state.
  5. Action: Refresh the page.
  6. Result: The composer becomes active immediately.

Further comments

Introduced on #37924

Summary by CodeRabbit

  • Bug Fixes

    • Fixed room invitation data synchronization by ensuring related queries are properly refreshed when leaving the invitation view, preventing stale data from persisting after handling invitations.
  • Refactor

    • Simplified room invitation flows with improved internal state management and reduced external dependencies.

✏️ Tip: You can customize this high-level summary in your review settings.

@dionisio-bot
Copy link
Contributor

dionisio-bot bot commented Jan 8, 2026

Looks like this PR is ready to merge! 🎉
If you have any trouble, please check the PR guidelines

@changeset-bot
Copy link

changeset-bot bot commented Jan 8, 2026

⚠️ No Changeset found

Latest commit: 153f551

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
Contributor

coderabbitai bot commented Jan 8, 2026

Walkthrough

Refactors room invitation acceptance/rejection flow by moving query invalidation logic from the useRoomInvitation hook to the RoomInvite component's unmount lifecycle. Removes side-effect handling from the hook mutation, exposes new public methods, and extends component props to support unmount-based query cache refresh.

Changes

Cohort / File(s) Summary
Hook API redesign
apps/meteor/client/views/room/hooks/useRoomInvitation.tsx
Removes router/user context dependencies and onSuccess side effects; exposes acceptInvite and rejectInvite methods; retains modal-based rejection UX without navigation/invalidation logic
Hook test cleanup
apps/meteor/client/views/room/hooks/useRoomInvitation.spec.tsx
Removes navigation behavior assertions from acceptance and rejection tests; retains endpoint, pending state, and modal interaction validations
Component-level invalidation
apps/meteor/client/views/room/RoomInvite.tsx
Adds query invalidation on unmount for room, subscription, and roomReference queries using federation-aware keys; extends public props with room and subscription fields; introduces useEffectEvent and useQueryClient integration

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

stat: ready to merge, stat: QA assured

Suggested reviewers

  • ggazzo
  • sampaiodiego
  • rodrigok

Poem

🐰 Hops along with query keys,
Moving invalidations with such ease,
From hook to component, the cache runs free,
Federated invites work naturally,
No more stuck composers—hoorah with glee! 🎉

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies a regression fix related to federated room state issues after accepting invitations, directly matching the PR's core objective.
Linked Issues check ✅ Passed The code changes address FB-172 by moving query invalidation from the hook to component unmount, preventing race conditions that caused the composer to remain in loading state after accepting federated invitations.
Out of Scope Changes check ✅ Passed All changes directly support the regression fix: refactoring query invalidation timing, removing stale navigation logic, and updating component props are all within scope of resolving the federated invitation race condition.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch regression/invite-race-condition

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.

@aleksandernsilva
Copy link
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 8, 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.

@aleksandernsilva aleksandernsilva added this to the 8.0.0 milestone Jan 8, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
apps/meteor/client/views/room/hooks/useRoomInvitation.tsx (1)

23-32: Redundant optional chaining on user.

Since lines 14-16 throw if user is falsy, the optional chaining on user?._id and user?.username at line 29 is unnecessary. This inconsistency may cause confusion about whether user can be null.

♻️ Suggested fix
 		queryClient.refetchQueries({
-			queryKey: roomsQueryKeys.roomReference(reference, room.t, user?._id, user?.username),
+			queryKey: roomsQueryKeys.roomReference(reference, room.t, user._id, user.username),
 		}),
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 35a6fdf and 24c427c.

📒 Files selected for processing (2)
  • apps/meteor/client/views/room/hooks/useRoomInvitation.tsx
  • packages/ddp-client/src/types/streams.ts
🧰 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:

  • packages/ddp-client/src/types/streams.ts
  • apps/meteor/client/views/room/hooks/useRoomInvitation.tsx
🧠 Learnings (6)
📓 Common learnings
Learnt from: sampaiodiego
Repo: RocketChat/Rocket.Chat PR: 37532
File: ee/packages/federation-matrix/src/FederationMatrix.ts:920-927
Timestamp: 2025-12-09T20:01:07.355Z
Learning: In Rocket.Chat's federation invite handling (ee/packages/federation-matrix/src/FederationMatrix.ts), when a user rejects an invite via federationSDK.rejectInvite(), the subscription cleanup happens automatically through an event-driven flow: Matrix emits a leave event back, which is processed by handleLeave() in ee/packages/federation-matrix/src/events/member.ts, and that function calls Room.performUserRemoval() to clean up the subscription. No explicit cleanup is needed in the reject branch of handleInvite() because the leave event handler takes care of it.
<!-- </add_learning>
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37377
File: apps/meteor/ee/server/hooks/federation/index.ts:86-88
Timestamp: 2025-11-04T16:49:19.107Z
Learning: In Rocket.Chat's federation system (apps/meteor/ee/server/hooks/federation/), permission checks follow two distinct patterns: (1) User-initiated federation actions (creating rooms, adding users to federated rooms, joining from invites) should throw MeteorError to inform users they lack 'access-federation' permission. (2) Remote server-initiated federation events should silently skip/ignore when users lack permission. The beforeAddUserToRoom hook only executes for local user-initiated actions, so throwing an error there is correct. Remote federation events are handled separately by the federation Matrix package with silent skipping logic.
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37205
File: ee/packages/federation-matrix/src/FederationMatrix.ts:296-301
Timestamp: 2025-10-28T16:53:42.761Z
Learning: In the Rocket.Chat federation-matrix integration (ee/packages/federation-matrix/), the createRoom method from rocket.chat/federation-sdk will support a 4-argument signature (userId, roomName, visibility, displayName) in newer versions. Code using this 4-argument call is forward-compatible with planned library updates and should not be flagged as an error.
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37205
File: ee/packages/federation-matrix/src/FederationMatrix.ts:296-301
Timestamp: 2025-10-28T16:53:42.761Z
Learning: In the Rocket.Chat federation-matrix integration (ee/packages/federation-matrix/), the createRoom method from rocket.chat/federation-sdk will support a 4-argument signature (userId, roomName, visibility, displayName) in newer versions. Code using this 4-argument call is forward-compatible with planned library updates and should not be flagged as an error.
📚 Learning: 2025-12-09T20:01:07.355Z
Learnt from: sampaiodiego
Repo: RocketChat/Rocket.Chat PR: 37532
File: ee/packages/federation-matrix/src/FederationMatrix.ts:920-927
Timestamp: 2025-12-09T20:01:07.355Z
Learning: In Rocket.Chat's federation invite handling (ee/packages/federation-matrix/src/FederationMatrix.ts), when a user rejects an invite via federationSDK.rejectInvite(), the subscription cleanup happens automatically through an event-driven flow: Matrix emits a leave event back, which is processed by handleLeave() in ee/packages/federation-matrix/src/events/member.ts, and that function calls Room.performUserRemoval() to clean up the subscription. No explicit cleanup is needed in the reject branch of handleInvite() because the leave event handler takes care of it.
<!-- </add_learning>

Applied to files:

  • apps/meteor/client/views/room/hooks/useRoomInvitation.tsx
📚 Learning: 2025-10-28T16:53:42.761Z
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37205
File: ee/packages/federation-matrix/src/FederationMatrix.ts:296-301
Timestamp: 2025-10-28T16:53:42.761Z
Learning: In the Rocket.Chat federation-matrix integration (ee/packages/federation-matrix/), the createRoom method from rocket.chat/federation-sdk will support a 4-argument signature (userId, roomName, visibility, displayName) in newer versions. Code using this 4-argument call is forward-compatible with planned library updates and should not be flagged as an error.

Applied to files:

  • apps/meteor/client/views/room/hooks/useRoomInvitation.tsx
📚 Learning: 2025-11-04T16:49:19.107Z
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37377
File: apps/meteor/ee/server/hooks/federation/index.ts:86-88
Timestamp: 2025-11-04T16:49:19.107Z
Learning: In Rocket.Chat's federation system (apps/meteor/ee/server/hooks/federation/), permission checks follow two distinct patterns: (1) User-initiated federation actions (creating rooms, adding users to federated rooms, joining from invites) should throw MeteorError to inform users they lack 'access-federation' permission. (2) Remote server-initiated federation events should silently skip/ignore when users lack permission. The beforeAddUserToRoom hook only executes for local user-initiated actions, so throwing an error there is correct. Remote federation events are handled separately by the federation Matrix package with silent skipping logic.

Applied to files:

  • apps/meteor/client/views/room/hooks/useRoomInvitation.tsx
📚 Learning: 2025-09-25T09:59:26.461Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 37057
File: packages/apps-engine/src/definition/accessors/IUserRead.ts:23-27
Timestamp: 2025-09-25T09:59:26.461Z
Learning: AppUserBridge.getUserRoomIds in apps/meteor/app/apps/server/bridges/users.ts always returns an array of strings (mapping subscription documents to room IDs), never undefined, even when user has no room subscriptions.

Applied to files:

  • apps/meteor/client/views/room/hooks/useRoomInvitation.tsx
📚 Learning: 2025-09-25T09:59:26.461Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 37057
File: packages/apps-engine/src/definition/accessors/IUserRead.ts:23-27
Timestamp: 2025-09-25T09:59:26.461Z
Learning: AppUserBridge.getUserRoomIds in apps/meteor/app/apps/server/bridges/users.ts always returns an array of strings by mapping subscription documents to room IDs, never undefined, even when user has no room subscriptions.

Applied to files:

  • apps/meteor/client/views/room/hooks/useRoomInvitation.tsx
⏰ 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). (1)
  • GitHub Check: 📦 Build Packages
🔇 Additional comments (5)
apps/meteor/client/views/room/hooks/useRoomInvitation.tsx (4)

34-38: Good use of useEffectEvent to stabilize the callback.

The pattern of gating subscription on replyInvite.isPending ensures the listener is only active during the mutation window, avoiding unnecessary event processing. The use of useEffectEvent for invalidateQueries prevents re-subscription churn.


60-66: Return interface is clean and maintains backward compatibility.

The spread of replyInvite exposes mutation state (isPending, isError, etc.) while acceptInvite and rejectInvite provide convenient action wrappers. The fire-and-forget pattern for mutate() aligns with the subscription-driven refresh approach.


14-16: Consider moving the user validation outside the render path or adding an error boundary.

Throwing synchronously during render (lines 14-16) will unmount the component tree when it propagates to the nearest error boundary. While this acts as a defensive guard for the invariant that user must exist, it's an unconventional React pattern. The parent component (Room.tsx) doesn't validate the user context before rendering RoomInvite, so if useUser() returns null during initial load or hydration, this will cause an unmount.

Consider one of these approaches:

  • Ensure the parent component validates that user exists before rendering RoomInvite, or
  • Use a conditional hook pattern or early return instead of throwing, or
  • Explicitly wrap this component with an error boundary that handles this specific case

If the calling context guarantees user is always defined, clarify this assumption in a comment.


47-57: The subscription event filter logic is correct and does not require changes.

Upon acceptance via Subscriptions.acceptInvitationById(), the database explicitly unsets both the status and inviter fields (packages/models/src/models/Subscriptions.ts lines 2102–2105). The subsequent 'updated' event emitted to the client includes this updated subscription where status is now undefined.

The condition if (event !== 'removed' && data.status !== undefined) { return; } correctly:

  • Skips invalidation when status is defined (subscription still invited, awaiting acceptance)
  • Triggers invalidation when status is undefined after acceptance (the subscription is now active)
  • Triggers invalidation when event is 'removed' (rejection/cleanup)

No code changes are needed.

packages/ddp-client/src/types/streams.ts (1)

157-157: Type extension is correct but explanation needs refinement.

The status field exists in ISubscription as an optional property with value 'INVITED'. Adding it to the Pick union enables the subscription handler to conditionally skip query invalidation when the status field is still defined (indicating an unaccepted invitation), and invalidate only when the status is undefined or the subscription is removed.

@aleksandernsilva aleksandernsilva force-pushed the regression/invite-race-condition branch from 24c427c to b413b74 Compare January 8, 2026 00:35
@github-actions
Copy link
Contributor

github-actions bot commented Jan 8, 2026

📦 Docker Image Size Report

📈 Changes

Service Current Baseline Change Percent
sum of all images 1.1GiB 0B +1.1GiB
rocketchat 355MiB 0B +355MiB
omnichannel-transcript-service 132MiB 0B +132MiB
queue-worker-service 132MiB 0B +132MiB
ddp-streamer-service 126MiB 0B +126MiB
account-service 113MiB 0B +113MiB
authorization-service 111MiB 0B +111MiB
presence-service 111MiB 0B +111MiB

📊 Historical Trend

---
config:
  theme: "dark"
  xyChart:
    width: 900
    height: 400
---
xychart
  title "Image Size Evolution by Service (Last 30 Days + This PR)"
  x-axis ["11/18 22:53", "11/19 23:02", "11/21 16:49", "11/24 17:34", "11/27 22:32", "11/28 19:05", "12/01 23:01", "12/02 21:57", "12/03 21:00", "12/04 18:17", "12/05 21:56", "12/08 20:15", "12/09 22:17", "12/10 23:26", "12/11 21:56", "12/12 22:45", "12/13 01:34", "12/15 22:31", "12/16 22:18", "12/17 21:04", "12/18 23:12", "12/19 23:27", "12/20 21:03", "12/22 18:54", "12/23 16:16", "12/24 19:38", "12/25 17:51", "12/26 13:18", "12/29 19:01", "12/30 20:52", "01/08 17:19 (PR)"]
  y-axis "Size (GB)" 0 --> 0.5
  line "account-service" [0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11]
  line "authorization-service" [0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11]
  line "ddp-streamer-service" [0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12]
  line "omnichannel-transcript-service" [0.14, 0.14, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13]
  line "presence-service" [0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11]
  line "queue-worker-service" [0.14, 0.14, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13]
  line "rocketchat" [0.35, 0.35, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.35]
Loading

Statistics (last 30 days):

  • 📊 Average: 1.5GiB
  • ⬇️ Minimum: 1.4GiB
  • ⬆️ Maximum: 1.6GiB
  • 🎯 Current PR: 1.1GiB
ℹ️ About this report

This report compares Docker image sizes from this build against the develop baseline.

  • Tag: pr-38093
  • Baseline: develop
  • Timestamp: 2026-01-08 17:19:27 UTC
  • Historical data points: 30

Updated: Thu, 08 Jan 2026 17:19:28 GMT

@aleksandernsilva aleksandernsilva marked this pull request as ready for review January 8, 2026 12:26
@aleksandernsilva aleksandernsilva requested a review from a team as a code owner January 8, 2026 12:26
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 3 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="apps/meteor/client/views/room/hooks/useRoomInvitation.tsx">

<violation number="1" location="apps/meteor/client/views/room/hooks/useRoomInvitation.tsx:52">
P1: Inverted condition logic will prevent query invalidation when accepting room invitations. The current logic skips invalidation for non-'removed' events with a defined status, but this is exactly when we need to invalidate (subscription updated after accepting). Should return early for 'removed' events instead.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @apps/meteor/client/views/room/hooks/useRoomInvitation.tsx:
- Around line 52-54: The early-return in the subscription change handler is
inverted: currently it returns when (event !== 'removed' && data.status !==
undefined) which prevents invalidation when a subscription's status becomes
defined (e.g., after accepting an invite). Change the condition to return only
when status is still undefined (e.g., if (event !== 'removed' && data.status ===
undefined) { return; }) or remove the early return so the handler invalidates
queries when data.status is defined; reference the handler's local symbols event
and data.status to locate and update the logic.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 24c427c and 4b2ce6e.

📒 Files selected for processing (3)
  • apps/meteor/client/views/room/hooks/useRoomInvitation.spec.tsx
  • apps/meteor/client/views/room/hooks/useRoomInvitation.tsx
  • packages/ddp-client/src/types/streams.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/ddp-client/src/types/streams.ts
🧰 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/hooks/useRoomInvitation.spec.tsx
  • apps/meteor/client/views/room/hooks/useRoomInvitation.tsx
🧠 Learnings (19)
📓 Common learnings
Learnt from: sampaiodiego
Repo: RocketChat/Rocket.Chat PR: 37532
File: ee/packages/federation-matrix/src/FederationMatrix.ts:920-927
Timestamp: 2025-12-09T20:01:07.355Z
Learning: In Rocket.Chat's federation invite handling (ee/packages/federation-matrix/src/FederationMatrix.ts), when a user rejects an invite via federationSDK.rejectInvite(), the subscription cleanup happens automatically through an event-driven flow: Matrix emits a leave event back, which is processed by handleLeave() in ee/packages/federation-matrix/src/events/member.ts, and that function calls Room.performUserRemoval() to clean up the subscription. No explicit cleanup is needed in the reject branch of handleInvite() because the leave event handler takes care of it.
<!-- </add_learning>
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37377
File: apps/meteor/ee/server/hooks/federation/index.ts:86-88
Timestamp: 2025-11-04T16:49:19.107Z
Learning: In Rocket.Chat's federation system (apps/meteor/ee/server/hooks/federation/), permission checks follow two distinct patterns: (1) User-initiated federation actions (creating rooms, adding users to federated rooms, joining from invites) should throw MeteorError to inform users they lack 'access-federation' permission. (2) Remote server-initiated federation events should silently skip/ignore when users lack permission. The beforeAddUserToRoom hook only executes for local user-initiated actions, so throwing an error there is correct. Remote federation events are handled separately by the federation Matrix package with silent skipping logic.
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37205
File: ee/packages/federation-matrix/src/FederationMatrix.ts:296-301
Timestamp: 2025-10-28T16:53:42.761Z
Learning: In the Rocket.Chat federation-matrix integration (ee/packages/federation-matrix/), the createRoom method from rocket.chat/federation-sdk will support a 4-argument signature (userId, roomName, visibility, displayName) in newer versions. Code using this 4-argument call is forward-compatible with planned library updates and should not be flagged as an error.
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37205
File: ee/packages/federation-matrix/src/FederationMatrix.ts:296-301
Timestamp: 2025-10-28T16:53:42.761Z
Learning: In the Rocket.Chat federation-matrix integration (ee/packages/federation-matrix/), the createRoom method from rocket.chat/federation-sdk will support a 4-argument signature (userId, roomName, visibility, displayName) in newer versions. Code using this 4-argument call is forward-compatible with planned library updates and should not be flagged as an error.
Learnt from: sampaiodiego
Repo: RocketChat/Rocket.Chat PR: 37110
File: apps/meteor/ee/server/startup/federation.ts:17-21
Timestamp: 2025-10-01T12:20:13.147Z
Learning: In Rocket.Chat's StreamerCentral broadcast for 'notify-room' events with 'user-activity', the `activity` argument is a string (e.g., 'user-typing') when a user starts typing and an empty array when the user stops typing.
📚 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/hooks/useRoomInvitation.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/hooks/useRoomInvitation.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/hooks/useRoomInvitation.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 `expect` matchers for assertions (`toEqual`, `toContain`, `toBeTruthy`, `toHaveLength`, etc.) instead of `assert` statements in Playwright tests

Applied to files:

  • apps/meteor/client/views/room/hooks/useRoomInvitation.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 : Maintain test isolation between test cases in Playwright tests

Applied to files:

  • apps/meteor/client/views/room/hooks/useRoomInvitation.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.beforeAll()` and `test.afterAll()` for setup/teardown in Playwright tests

Applied to files:

  • apps/meteor/client/views/room/hooks/useRoomInvitation.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/hooks/useRoomInvitation.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/page-objects/**/*.ts : Utilize existing page objects pattern from `apps/meteor/tests/e2e/page-objects/`

Applied to files:

  • apps/meteor/client/views/room/hooks/useRoomInvitation.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 clean state for each test execution in Playwright tests

Applied to files:

  • apps/meteor/client/views/room/hooks/useRoomInvitation.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 : Group related tests in the same file

Applied to files:

  • apps/meteor/client/views/room/hooks/useRoomInvitation.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/hooks/useRoomInvitation.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 : Implement proper wait strategies for dynamic content in Playwright tests

Applied to files:

  • apps/meteor/client/views/room/hooks/useRoomInvitation.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/hooks/useRoomInvitation.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 `page.waitFor()` with specific conditions instead of hardcoded timeouts in Playwright tests

Applied to files:

  • apps/meteor/client/views/room/hooks/useRoomInvitation.spec.tsx
📚 Learning: 2025-12-09T20:01:07.355Z
Learnt from: sampaiodiego
Repo: RocketChat/Rocket.Chat PR: 37532
File: ee/packages/federation-matrix/src/FederationMatrix.ts:920-927
Timestamp: 2025-12-09T20:01:07.355Z
Learning: In Rocket.Chat's federation invite handling (ee/packages/federation-matrix/src/FederationMatrix.ts), when a user rejects an invite via federationSDK.rejectInvite(), the subscription cleanup happens automatically through an event-driven flow: Matrix emits a leave event back, which is processed by handleLeave() in ee/packages/federation-matrix/src/events/member.ts, and that function calls Room.performUserRemoval() to clean up the subscription. No explicit cleanup is needed in the reject branch of handleInvite() because the leave event handler takes care of it.
<!-- </add_learning>

Applied to files:

  • apps/meteor/client/views/room/hooks/useRoomInvitation.tsx
📚 Learning: 2025-10-28T16:53:42.761Z
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37205
File: ee/packages/federation-matrix/src/FederationMatrix.ts:296-301
Timestamp: 2025-10-28T16:53:42.761Z
Learning: In the Rocket.Chat federation-matrix integration (ee/packages/federation-matrix/), the createRoom method from rocket.chat/federation-sdk will support a 4-argument signature (userId, roomName, visibility, displayName) in newer versions. Code using this 4-argument call is forward-compatible with planned library updates and should not be flagged as an error.

Applied to files:

  • apps/meteor/client/views/room/hooks/useRoomInvitation.tsx
📚 Learning: 2025-11-04T16:49:19.107Z
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37377
File: apps/meteor/ee/server/hooks/federation/index.ts:86-88
Timestamp: 2025-11-04T16:49:19.107Z
Learning: In Rocket.Chat's federation system (apps/meteor/ee/server/hooks/federation/), permission checks follow two distinct patterns: (1) User-initiated federation actions (creating rooms, adding users to federated rooms, joining from invites) should throw MeteorError to inform users they lack 'access-federation' permission. (2) Remote server-initiated federation events should silently skip/ignore when users lack permission. The beforeAddUserToRoom hook only executes for local user-initiated actions, so throwing an error there is correct. Remote federation events are handled separately by the federation Matrix package with silent skipping logic.

Applied to files:

  • apps/meteor/client/views/room/hooks/useRoomInvitation.tsx
📚 Learning: 2025-09-25T09:59:26.461Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 37057
File: packages/apps-engine/src/definition/accessors/IUserRead.ts:23-27
Timestamp: 2025-09-25T09:59:26.461Z
Learning: AppUserBridge.getUserRoomIds in apps/meteor/app/apps/server/bridges/users.ts always returns an array of strings (mapping subscription documents to room IDs), never undefined, even when user has no room subscriptions.

Applied to files:

  • apps/meteor/client/views/room/hooks/useRoomInvitation.tsx
🧬 Code graph analysis (1)
apps/meteor/client/views/room/hooks/useRoomInvitation.spec.tsx (1)
packages/mock-providers/src/index.ts (1)
  • mockAppRoot (3-3)
⏰ 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). (3)
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: CodeQL-Build
  • GitHub Check: CodeQL-Build
🔇 Additional comments (3)
apps/meteor/client/views/room/hooks/useRoomInvitation.spec.tsx (1)

29-29: LGTM - User context now required by implementation.

The addition of .withJohnDoe() correctly provides the user context required by the updated hook implementation, which now throws an error if no user is found (lines 14-16 in useRoomInvitation.tsx).

apps/meteor/client/views/room/hooks/useRoomInvitation.tsx (2)

14-16: LGTM - Good defensive runtime check.

Throwing an error when the user is not found prevents invalid states where invitation actions would be attempted without proper authentication context.


34-58: Pattern correctly handles race conditions and cleanup for federated room invitation acceptance.

The subscription lifecycle is properly tied to the replyInvite.isPending state. When the mutation starts, the subscription activates and remains open to capture server-side events during async processing. When the mutation completes (pending becomes false), the effect reruns and the previous subscription's cleanup function executes, preventing memory leaks. Rapid invocations are safely handled because each pending state transition triggers a new effect execution, ensuring the old subscription is cleaned up before a new one is established.

The event filtering (checking room._id and event type) correctly ensures only relevant updates trigger query invalidation, and the use of federationOriginalName in invalidateQueries properly addresses federated room references.

@codecov
Copy link

codecov bot commented Jan 8, 2026

Codecov Report

❌ Patch coverage is 11.11111% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 70.62%. Comparing base (35a6fdf) to head (153f551).
⚠️ Report is 1 commits behind head on release-8.0.0.

Additional details and impacted files

Impacted file tree graph

@@                Coverage Diff                @@
##           release-8.0.0   #38093      +/-   ##
=================================================
- Coverage          70.68%   70.62%   -0.07%     
=================================================
  Files               3145     3145              
  Lines             108726   108708      -18     
  Branches           19537    19533       -4     
=================================================
- Hits               76857    76773      -84     
- Misses             29865    29937      +72     
+ Partials            2004     1998       -6     
Flag Coverage Δ
e2e 60.19% <0.00%> (+0.03%) ⬆️
e2e-api 47.45% <ø> (-1.08%) ⬇️
unit 71.73% <100.00%> (-0.08%) ⬇️

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.

@aleksandernsilva aleksandernsilva force-pushed the regression/invite-race-condition branch from 1f87ee6 to 7edfe59 Compare January 8, 2026 12:48
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
apps/meteor/client/views/room/hooks/useRoomInvitation.tsx (1)

61-61: Remove unnecessary async keyword from acceptInvite.

The async keyword here creates a Promise that immediately resolves to undefined since mutate() returns void. This could mislead callers into thinking that awaiting acceptInvite() waits for the mutation to complete, when actual completion is signaled via the stream subscription.

♻️ Suggested fix
-		acceptInvite: async () => replyInvite.mutate({ roomId: room._id, action: 'accept' }),
+		acceptInvite: () => replyInvite.mutate({ roomId: room._id, action: 'accept' }),
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 1f87ee6 and 7edfe59.

📒 Files selected for processing (1)
  • apps/meteor/client/views/room/hooks/useRoomInvitation.tsx
🧰 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/hooks/useRoomInvitation.tsx
🧠 Learnings (7)
📓 Common learnings
Learnt from: sampaiodiego
Repo: RocketChat/Rocket.Chat PR: 37532
File: ee/packages/federation-matrix/src/FederationMatrix.ts:920-927
Timestamp: 2025-12-09T20:01:07.355Z
Learning: In Rocket.Chat's federation invite handling (ee/packages/federation-matrix/src/FederationMatrix.ts), when a user rejects an invite via federationSDK.rejectInvite(), the subscription cleanup happens automatically through an event-driven flow: Matrix emits a leave event back, which is processed by handleLeave() in ee/packages/federation-matrix/src/events/member.ts, and that function calls Room.performUserRemoval() to clean up the subscription. No explicit cleanup is needed in the reject branch of handleInvite() because the leave event handler takes care of it.
<!-- </add_learning>
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37377
File: apps/meteor/ee/server/hooks/federation/index.ts:86-88
Timestamp: 2025-11-04T16:49:19.107Z
Learning: In Rocket.Chat's federation system (apps/meteor/ee/server/hooks/federation/), permission checks follow two distinct patterns: (1) User-initiated federation actions (creating rooms, adding users to federated rooms, joining from invites) should throw MeteorError to inform users they lack 'access-federation' permission. (2) Remote server-initiated federation events should silently skip/ignore when users lack permission. The beforeAddUserToRoom hook only executes for local user-initiated actions, so throwing an error there is correct. Remote federation events are handled separately by the federation Matrix package with silent skipping logic.
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37205
File: ee/packages/federation-matrix/src/FederationMatrix.ts:296-301
Timestamp: 2025-10-28T16:53:42.761Z
Learning: In the Rocket.Chat federation-matrix integration (ee/packages/federation-matrix/), the createRoom method from rocket.chat/federation-sdk will support a 4-argument signature (userId, roomName, visibility, displayName) in newer versions. Code using this 4-argument call is forward-compatible with planned library updates and should not be flagged as an error.
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37205
File: ee/packages/federation-matrix/src/FederationMatrix.ts:296-301
Timestamp: 2025-10-28T16:53:42.761Z
Learning: In the Rocket.Chat federation-matrix integration (ee/packages/federation-matrix/), the createRoom method from rocket.chat/federation-sdk will support a 4-argument signature (userId, roomName, visibility, displayName) in newer versions. Code using this 4-argument call is forward-compatible with planned library updates and should not be flagged as an error.
📚 Learning: 2025-12-09T20:01:07.355Z
Learnt from: sampaiodiego
Repo: RocketChat/Rocket.Chat PR: 37532
File: ee/packages/federation-matrix/src/FederationMatrix.ts:920-927
Timestamp: 2025-12-09T20:01:07.355Z
Learning: In Rocket.Chat's federation invite handling (ee/packages/federation-matrix/src/FederationMatrix.ts), when a user rejects an invite via federationSDK.rejectInvite(), the subscription cleanup happens automatically through an event-driven flow: Matrix emits a leave event back, which is processed by handleLeave() in ee/packages/federation-matrix/src/events/member.ts, and that function calls Room.performUserRemoval() to clean up the subscription. No explicit cleanup is needed in the reject branch of handleInvite() because the leave event handler takes care of it.
<!-- </add_learning>

Applied to files:

  • apps/meteor/client/views/room/hooks/useRoomInvitation.tsx
📚 Learning: 2025-10-28T16:53:42.761Z
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37205
File: ee/packages/federation-matrix/src/FederationMatrix.ts:296-301
Timestamp: 2025-10-28T16:53:42.761Z
Learning: In the Rocket.Chat federation-matrix integration (ee/packages/federation-matrix/), the createRoom method from rocket.chat/federation-sdk will support a 4-argument signature (userId, roomName, visibility, displayName) in newer versions. Code using this 4-argument call is forward-compatible with planned library updates and should not be flagged as an error.

Applied to files:

  • apps/meteor/client/views/room/hooks/useRoomInvitation.tsx
📚 Learning: 2025-11-04T16:49:19.107Z
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37377
File: apps/meteor/ee/server/hooks/federation/index.ts:86-88
Timestamp: 2025-11-04T16:49:19.107Z
Learning: In Rocket.Chat's federation system (apps/meteor/ee/server/hooks/federation/), permission checks follow two distinct patterns: (1) User-initiated federation actions (creating rooms, adding users to federated rooms, joining from invites) should throw MeteorError to inform users they lack 'access-federation' permission. (2) Remote server-initiated federation events should silently skip/ignore when users lack permission. The beforeAddUserToRoom hook only executes for local user-initiated actions, so throwing an error there is correct. Remote federation events are handled separately by the federation Matrix package with silent skipping logic.

Applied to files:

  • apps/meteor/client/views/room/hooks/useRoomInvitation.tsx
📚 Learning: 2025-09-25T09:59:26.461Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 37057
File: packages/apps-engine/src/definition/accessors/IUserRead.ts:23-27
Timestamp: 2025-09-25T09:59:26.461Z
Learning: UserBridge.doGetUserRoomIds in packages/apps-engine/src/server/bridges/UserBridge.ts has a bug where it implicitly returns undefined when the app lacks read permission (missing return statement in the else case of the permission check).

Applied to files:

  • apps/meteor/client/views/room/hooks/useRoomInvitation.tsx
📚 Learning: 2025-10-06T20:32:23.658Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 37152
File: packages/apps-engine/tests/test-data/utilities.ts:557-573
Timestamp: 2025-10-06T20:32:23.658Z
Learning: In packages/apps-engine/tests/test-data/utilities.ts, the field name `isSubscripbedViaBundle` in the `IMarketplaceSubscriptionInfo` type should not be flagged as a typo, as it may match the upstream API's field name.

Applied to files:

  • apps/meteor/client/views/room/hooks/useRoomInvitation.tsx
📚 Learning: 2025-09-25T09:59:26.461Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 37057
File: packages/apps-engine/src/definition/accessors/IUserRead.ts:23-27
Timestamp: 2025-09-25T09:59:26.461Z
Learning: AppUserBridge.getUserRoomIds in apps/meteor/app/apps/server/bridges/users.ts always returns an array of strings (mapping subscription documents to room IDs), never undefined, even when user has no room subscriptions.

Applied to files:

  • apps/meteor/client/views/room/hooks/useRoomInvitation.tsx
🧬 Code graph analysis (1)
apps/meteor/client/views/room/hooks/useRoomInvitation.tsx (2)
packages/ui-contexts/src/index.ts (2)
  • useUser (83-83)
  • useStream (77-77)
apps/meteor/client/lib/queryKeys.ts (2)
  • roomsQueryKeys (13-33)
  • subscriptionsQueryKeys (35-38)
⏰ 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). (3)
  • GitHub Check: 📦 Build Packages
  • GitHub Check: CodeQL-Build
  • GitHub Check: CodeQL-Build
🔇 Additional comments (3)
apps/meteor/client/views/room/hooks/useRoomInvitation.tsx (3)

23-32: Good use of useEffectEvent for stable invalidation callback.

The pattern correctly uses useEffectEvent to access current values (room, user) without adding them to the effect's dependency array, preventing unnecessary subscription re-subscriptions while ensuring queries are invalidated with up-to-date references.


34-57: Stream-based invalidation correctly addresses the federation race condition.

The approach of subscribing to notify-user events only while the mutation is pending, and triggering invalidation on subscription status changes rather than mutation completion, properly handles the asynchronous nature of federated room joins. This ensures the UI reflects the actual backend state rather than potentially stale data.


14-16: Consider the actual rendering context before applying the hooks rule concern.

The useRoomInvitation hook is only rendered within RoomInvite, which itself is conditionally rendered in Room.tsx (lines 37-42) exclusively when subscription && isInviteSubscription(subscription) is true. The parent RoomOpener handles authentication errors (NotAuthorizedError) and only renders Room when useOpenRoom succeeds. This layered protection suggests the component operates in a context where authentication is guaranteed.

While useUser() technically returns IUser | null, the architectural pattern indicates that if user state changes, the entire route would unmount rather than remain mounted with useUser() toggling. However, without fully verifying the UserContext implementation and potential auth listener behavior, it cannot be definitively ruled out that edge cases exist.

@aleksandernsilva aleksandernsilva marked this pull request as draft January 8, 2026 15:02
@aleksandernsilva aleksandernsilva force-pushed the regression/invite-race-condition branch from d226724 to 170be37 Compare January 8, 2026 16:57
@aleksandernsilva aleksandernsilva force-pushed the regression/invite-race-condition branch from 170be37 to 153f551 Compare January 8, 2026 16:59
@aleksandernsilva aleksandernsilva marked this pull request as ready for review January 8, 2026 17:52
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

No issues found across 3 files

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
apps/meteor/client/views/room/hooks/useRoomInvitation.tsx (1)

11-14: Clarify intent: mutate vs mutateAsync

The async keyword on these functions is misleading since mutate() is fire-and-forget and returns void, not a Promise. The returned promise from these functions will resolve immediately without waiting for the mutation to complete.

If awaiting the mutation result is intended (e.g., for error handling by the caller), use mutateAsync() instead. If fire-and-forget is intentional (relying on isPending state and unmount-based invalidation), consider removing the async keyword to clarify the contract.

Option A: If fire-and-forget is intentional
 	return {
 		...replyInvite,
-		acceptInvite: async () => replyInvite.mutate({ roomId: room._id, action: 'accept' }),
-		rejectInvite: async () => {
+		acceptInvite: () => replyInvite.mutate({ roomId: room._id, action: 'accept' }),
+		rejectInvite: async () => {
 			if (await openConfirmationModal()) replyInvite.mutate({ roomId: room._id, action: 'reject' });
 		},
 	};
Option B: If awaiting mutation is needed
 	return {
 		...replyInvite,
-		acceptInvite: async () => replyInvite.mutate({ roomId: room._id, action: 'accept' }),
+		acceptInvite: () => replyInvite.mutateAsync({ roomId: room._id, action: 'accept' }),
 		rejectInvite: async () => {
-			if (await openConfirmationModal()) replyInvite.mutate({ roomId: room._id, action: 'reject' });
+			if (await openConfirmationModal()) replyInvite.mutateAsync({ roomId: room._id, action: 'reject' });
 		},
 	};
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 7edfe59 and 153f551.

📒 Files selected for processing (3)
  • apps/meteor/client/views/room/RoomInvite.tsx
  • apps/meteor/client/views/room/hooks/useRoomInvitation.spec.tsx
  • apps/meteor/client/views/room/hooks/useRoomInvitation.tsx
💤 Files with no reviewable changes (1)
  • apps/meteor/client/views/room/hooks/useRoomInvitation.spec.tsx
🧰 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/hooks/useRoomInvitation.tsx
  • apps/meteor/client/views/room/RoomInvite.tsx
🧠 Learnings (7)
📓 Common learnings
Learnt from: sampaiodiego
Repo: RocketChat/Rocket.Chat PR: 37532
File: ee/packages/federation-matrix/src/FederationMatrix.ts:920-927
Timestamp: 2025-12-09T20:01:07.355Z
Learning: In Rocket.Chat's federation invite handling (ee/packages/federation-matrix/src/FederationMatrix.ts), when a user rejects an invite via federationSDK.rejectInvite(), the subscription cleanup happens automatically through an event-driven flow: Matrix emits a leave event back, which is processed by handleLeave() in ee/packages/federation-matrix/src/events/member.ts, and that function calls Room.performUserRemoval() to clean up the subscription. No explicit cleanup is needed in the reject branch of handleInvite() because the leave event handler takes care of it.
<!-- </add_learning>
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37205
File: ee/packages/federation-matrix/src/FederationMatrix.ts:296-301
Timestamp: 2025-10-28T16:53:42.761Z
Learning: In the Rocket.Chat federation-matrix integration (ee/packages/federation-matrix/), the createRoom method from rocket.chat/federation-sdk will support a 4-argument signature (userId, roomName, visibility, displayName) in newer versions. Code using this 4-argument call is forward-compatible with planned library updates and should not be flagged as an error.
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37205
File: ee/packages/federation-matrix/src/FederationMatrix.ts:296-301
Timestamp: 2025-10-28T16:53:42.761Z
Learning: In the Rocket.Chat federation-matrix integration (ee/packages/federation-matrix/), the createRoom method from rocket.chat/federation-sdk will support a 4-argument signature (userId, roomName, visibility, displayName) in newer versions. Code using this 4-argument call is forward-compatible with planned library updates and should not be flagged as an error.
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37377
File: apps/meteor/ee/server/hooks/federation/index.ts:86-88
Timestamp: 2025-11-04T16:49:19.107Z
Learning: In Rocket.Chat's federation system (apps/meteor/ee/server/hooks/federation/), permission checks follow two distinct patterns: (1) User-initiated federation actions (creating rooms, adding users to federated rooms, joining from invites) should throw MeteorError to inform users they lack 'access-federation' permission. (2) Remote server-initiated federation events should silently skip/ignore when users lack permission. The beforeAddUserToRoom hook only executes for local user-initiated actions, so throwing an error there is correct. Remote federation events are handled separately by the federation Matrix package with silent skipping logic.
📚 Learning: 2025-12-09T20:01:07.355Z
Learnt from: sampaiodiego
Repo: RocketChat/Rocket.Chat PR: 37532
File: ee/packages/federation-matrix/src/FederationMatrix.ts:920-927
Timestamp: 2025-12-09T20:01:07.355Z
Learning: In Rocket.Chat's federation invite handling (ee/packages/federation-matrix/src/FederationMatrix.ts), when a user rejects an invite via federationSDK.rejectInvite(), the subscription cleanup happens automatically through an event-driven flow: Matrix emits a leave event back, which is processed by handleLeave() in ee/packages/federation-matrix/src/events/member.ts, and that function calls Room.performUserRemoval() to clean up the subscription. No explicit cleanup is needed in the reject branch of handleInvite() because the leave event handler takes care of it.
<!-- </add_learning>

Applied to files:

  • apps/meteor/client/views/room/hooks/useRoomInvitation.tsx
  • apps/meteor/client/views/room/RoomInvite.tsx
📚 Learning: 2025-10-28T16:53:42.761Z
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37205
File: ee/packages/federation-matrix/src/FederationMatrix.ts:296-301
Timestamp: 2025-10-28T16:53:42.761Z
Learning: In the Rocket.Chat federation-matrix integration (ee/packages/federation-matrix/), the createRoom method from rocket.chat/federation-sdk will support a 4-argument signature (userId, roomName, visibility, displayName) in newer versions. Code using this 4-argument call is forward-compatible with planned library updates and should not be flagged as an error.

Applied to files:

  • apps/meteor/client/views/room/hooks/useRoomInvitation.tsx
  • apps/meteor/client/views/room/RoomInvite.tsx
📚 Learning: 2025-11-04T16:49:19.107Z
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37377
File: apps/meteor/ee/server/hooks/federation/index.ts:86-88
Timestamp: 2025-11-04T16:49:19.107Z
Learning: In Rocket.Chat's federation system (apps/meteor/ee/server/hooks/federation/), permission checks follow two distinct patterns: (1) User-initiated federation actions (creating rooms, adding users to federated rooms, joining from invites) should throw MeteorError to inform users they lack 'access-federation' permission. (2) Remote server-initiated federation events should silently skip/ignore when users lack permission. The beforeAddUserToRoom hook only executes for local user-initiated actions, so throwing an error there is correct. Remote federation events are handled separately by the federation Matrix package with silent skipping logic.

Applied to files:

  • apps/meteor/client/views/room/hooks/useRoomInvitation.tsx
  • apps/meteor/client/views/room/RoomInvite.tsx
📚 Learning: 2025-09-25T09:59:26.461Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 37057
File: packages/apps-engine/src/definition/accessors/IUserRead.ts:23-27
Timestamp: 2025-09-25T09:59:26.461Z
Learning: UserBridge.doGetUserRoomIds in packages/apps-engine/src/server/bridges/UserBridge.ts has a bug where it implicitly returns undefined when the app lacks read permission (missing return statement in the else case of the permission check).

Applied to files:

  • apps/meteor/client/views/room/hooks/useRoomInvitation.tsx
📚 Learning: 2025-10-06T20:32:23.658Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 37152
File: packages/apps-engine/tests/test-data/utilities.ts:557-573
Timestamp: 2025-10-06T20:32:23.658Z
Learning: In packages/apps-engine/tests/test-data/utilities.ts, the field name `isSubscripbedViaBundle` in the `IMarketplaceSubscriptionInfo` type should not be flagged as a typo, as it may match the upstream API's field name.

Applied to files:

  • apps/meteor/client/views/room/hooks/useRoomInvitation.tsx
📚 Learning: 2025-09-25T09:59:26.461Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 37057
File: packages/apps-engine/src/definition/accessors/IUserRead.ts:23-27
Timestamp: 2025-09-25T09:59:26.461Z
Learning: AppUserBridge.getUserRoomIds in apps/meteor/app/apps/server/bridges/users.ts always returns an array of strings (mapping subscription documents to room IDs), never undefined, even when user has no room subscriptions.

Applied to files:

  • apps/meteor/client/views/room/hooks/useRoomInvitation.tsx
🧬 Code graph analysis (1)
apps/meteor/client/views/room/RoomInvite.tsx (4)
apps/meteor/client/views/room/hooks/useRoomInvitation.tsx (1)
  • useRoomInvitation (5-16)
packages/core-typings/src/IRoom.ts (1)
  • isRoomFederated (122-122)
apps/meteor/client/lib/links.ts (1)
  • links (3-59)
apps/meteor/client/lib/queryKeys.ts (2)
  • roomsQueryKeys (13-33)
  • subscriptionsQueryKeys (35-38)
🔇 Additional comments (1)
apps/meteor/client/views/room/RoomInvite.tsx (1)

32-42: Well-structured unmount-based invalidation approach.

The use of useEffectEvent to create a stable callback reference and deferring query invalidation to the cleanup phase effectively addresses the race condition described in the PR objectives. This ensures queries aren't invalidated until the component actually unmounts (i.e., after the invite state transition is complete), preventing stale data fetches during async join processing.

The federation-aware reference construction (federationOriginalName ?? room.name ?? room._id) correctly handles both federated and non-federated room scenarios. Based on retrieved learnings, this complements the event-driven subscription cleanup flow for federation rejects.

@aleksandernsilva aleksandernsilva added the stat: QA assured Means it has been tested and approved by a company insider label Jan 8, 2026
@dionisio-bot dionisio-bot bot added the stat: ready to merge PR tested and approved waiting for merge label Jan 8, 2026
@kodiakhq kodiakhq bot merged commit 2bda441 into release-8.0.0 Jan 8, 2026
81 of 83 checks passed
@kodiakhq kodiakhq bot deleted the regression/invite-race-condition branch January 8, 2026 20:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stat: QA assured Means it has been tested and approved by a company insider stat: ready to merge PR tested and approved waiting for merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants