Skip to content

Conversation

@tassoevan
Copy link
Contributor

@tassoevan tassoevan commented Dec 23, 2025

Proposed changes (including videos or screenshots)

This pull request refactors the way asynchronous state and record list management is handled across the codebase, removing custom hooks and types related to AsyncState in favor of more standard or modern approaches (such as React Query). The changes simplify the context and hooks used for apps, omnichannel room icons, and message lists, making the code easier to maintain and reducing complexity.

Omnichannel Room Icon Refactor:

  • The useOmnichannelRoomIcon hook and related context now return a simple string | undefined for icon data, instead of an AsyncState object. This change removes the need to track loading/error phases for icons, simplifying both the context and consumer components. [1] [2] [3] [4] [5] [6]

Apps Context Simplification:

  • The AppsContext and related hooks (useAppsReload, useAppsResult) have been refactored to remove the AsyncState pattern and instead directly use the orchestrator or undefined. This streamlines how app data is accessed and removes unnecessary complexity. [1] [2] [3] [4]

Record List and Message List Hook Removal:

  • Custom hooks for managing record lists and scrollable message lists (useRecordList, useScrollableMessageList, useScrollableRecordList, useStreamUpdatesForMessageList) have been removed, indicating a shift away from the previous event-driven and phase-tracking approach for list updates. [1] [2] [3] [4]

Async State Hook Deletion:

  • The custom useAsyncState hook and related types have been deleted, further reducing reliance on the custom async state management system in favor of more standard solutions.

MapView and useAsyncImage Modernization:

  • The useAsyncImage hook is now implemented using React Query, providing a more robust and standard way to handle asynchronous image loading. The MapView component is updated to use this new hook. [1] [2] [3]

These changes collectively modernize the codebase, reduce boilerplate, and make asynchronous data handling more consistent and maintainable.

Issue(s)

ARCH-1914

Steps to test or reproduce

Further comments

Summary by CodeRabbit

  • Refactor

    • Many list and state flows moved to server-driven infinite pagination with unified loading/error handling and simplified "load more" callbacks; canned-response department name is now optional.
  • New Features

    • Added an Apps provider, expanded marketplace and room query capabilities, and a hook to keep infinite message queries synchronized with real-time updates.
  • Bug Fixes

    • Mailer unsubscription now uses a mutation flow with clearer loading, error, and success feedback.

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

@changeset-bot
Copy link

changeset-bot bot commented Dec 23, 2025

⚠️ No Changeset found

Latest commit: 49124fd

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 Dec 23, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Replaces internal AsyncState/RecordList primitives with TanStack React Query across hooks, providers, and components; removes RecordList classes, AsyncState utilities, and many list-backed hooks/tests; migrates marketplace/apps orchestration to query-based hooks and updates consumers to query-result shapes (isPending/data/refetch).

Changes

Cohort / File(s) Summary
Async primitives & lists
apps/meteor/client/lib/asyncState/*, apps/meteor/client/lib/lists/*
Removed AsyncState types/enum/functions and the RecordList system plus list subclasses and tests (MessageList, RecordList, ThreadsList, DiscussionsList, FilesList, ImagesList, CannedResponseList, VideoConfRecordList, RecordList.spec).
List hooks & lifecycle helpers
apps/meteor/client/hooks/lists/*, apps/meteor/client/hooks/useComponentDidUpdate.ts, apps/meteor/client/hooks/useStreamUpdatesForMessageList.ts
Deleted RecordList-based hooks and lifecycle helpers (useRecordList, useScrollableRecordList, useScrollableMessageList, useStreamUpdatesForMessageList, useComponentDidUpdate).
React Query migrations (hooks & providers)
apps/meteor/client/hooks/**/*, apps/meteor/client/views/**/hooks/*, apps/meteor/client/providers/*, apps/meteor/client/contexts/*
Introduced/updated many hooks to use useQuery/useInfiniteQuery/useMutation (e.g., useInfiniteMessageQueryUpdates, useRoomsList, useImagesList, useFilesList, useThreadsList, useDiscussionsList, useTeamsChannelList, useVideoConfList, useCannedResponseList, useHistoryMessageList, useApps, useAppsReload, useFilteredApps). AppsContext surface reduced; AppsProvider implementation removed and a lightweight provider wrapper added.
Query keys & marketplace
apps/meteor/client/lib/queryKeys.ts, apps/meteor/client/views/marketplace/hooks/*, apps/meteor/client/views/marketplace/hooks/useApps.ts
Expanded/standardized queryKey APIs (rooms/files/images/discussions, marketplaceQueryKeys, videoConferenceQueryKeys, cannedResponses.list, contactMessages, teams.listChannels). Moved marketplace orchestration into useApps and added storeQueryFunction.
Components & consumers
apps/meteor/client/views/**/contextualBar/*, ImageGallery/*, RoomFiles/*, TeamsChannels*, VideoConfList*, CannedResponse*
Consumers migrated from RecordList flows to React Query infinite results. Many prop signatures changed (e.g., loadMoreItems() => void, loadingisPending, reloadrefetch); canned response departmentName typing relaxed to optional.
Image loader & mailer
apps/meteor/client/components/message/content/location/*, apps/meteor/client/hooks/useAsyncImage.ts, apps/meteor/client/views/mailer/MailerUnsubscriptionPage.tsx
Replaced custom image loader with React Query-based useAsyncImage (returns UseQueryResult); MapView adjusted to destructure data; mailer unsubscription converted to useMutation and uses mutation state flags.
Providers / Contexts
apps/meteor/client/contexts/*, apps/meteor/client/providers/*
AppsContext reduced to orchestrator-only; AppsProvider implementation removed and a simple provider wrapper added (providers/AppsProvider.tsx). Several hooks that consumed AppsContext were updated to new useApps API.
Tests & mocks
apps/meteor/client/views/marketplace/*, apps/meteor/tests/mocks/*
Tests and mocks updated for query-driven shapes; AppsContext mocks simplified and tests adapted to license-driven data where applicable.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant UI as Consumer (component/hook)
  participant Stream as Stream Service
  participant Cache as React Query Cache
  participant API as Server API

  Note over UI,Stream: useInfiniteMessageQueryUpdates live-update flow
  UI->>Cache: read cached infinite pages (queryKey)
  UI->>Stream: subscribe("room-messages")
  UI->>Stream: subscribe("notify-room")
  Stream-->>UI: message created/updated event
  UI->>Cache: mutateQueryData (insert or update if filter passes)
  Cache-->>UI: cache updated → rerender
  Stream-->>UI: deleteMessage event
  UI->>Cache: remove message by _id from cache
  Stream-->>UI: deleteMessageBulk event
  UI->>Cache: invalidateQueries(queryKey)
  Cache->>API: refetch on next trigger
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Suggested labels

stat: ready to merge, stat: QA assured

Poem

"I nibble code and hop with cheer,
AsyncState burrowed out of here,
Queries sprout where lists once stood,
Streams keep caches fresh and good,
A rabbit hops the merge — hooray! 🐰"

🚥 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 'refactor: Phase-out async state API' directly summarizes the main change: removing the custom AsyncState pattern throughout the codebase and replacing it with modern alternatives like React Query.
Linked Issues check ✅ Passed The PR implements comprehensive refactoring to phase out AsyncState as stated in ARCH-1914, removing AsyncState types/hooks, updating data-fetching patterns to React Query, and simplifying context patterns across multiple files and domains.
Out of Scope Changes check ✅ Passed All changes align with the stated objective of phasing out AsyncState. Updates to queryKeys for new data fetching patterns and changes to prop signatures follow naturally from the AsyncState removal and React Query adoption.

✏️ 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 chore/async-state

🧹 Recent nitpick comments
apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx (3)

85-90: Consider adding missing dependencies to useEffect.

The createFilterStructureItems function depends on isRequested and t, but only isRequested is in the dependency array. While t is typically stable, the inline function createFilterStructureItems should ideally be wrapped in useCallback or inlined into the effect for correctness.

♻️ Suggested improvement
+	const createFilterStructureItems = useCallback(() => {
+		return isRequested ? [...requestedFilterItems, ...baseFilterStructureItems] : baseFilterStructureItems;
+	}, [isRequested, t]);

 	useEffect(() => {
 		setSortFilterStructure({
 			label: t('Sort_By'),
 			items: createFilterStructureItems(),
 		});
-	}, [isRequested]);
+	}, [createFilterStructureItems, t]);

141-159: State update mutates nested objects directly.

The forEach loop mutates currentItem.checked properties in place before returning a shallow copy. While this works, it's an anti-pattern that can cause subtle bugs with React's reconciliation. Consider using immutable updates.

♻️ Immutable state update
 	const toggleInitialSortOption = useCallback((isRequested: boolean) => {
 		setSortFilterStructure((prevState) => {
-			prevState.items.forEach((currentItem) => {
-				if (isRequested && currentItem.id === 'urf') {
-					currentItem.checked = true;
-					return;
-				}
-
-				if (!isRequested && currentItem.id === 'mru') {
-					currentItem.checked = true;
-					return;
-				}
-
-				currentItem.checked = false;
-			});
-
-			return { ...prevState };
+			return {
+				...prevState,
+				items: prevState.items.map((item) => ({
+					...item,
+					checked: isRequested ? item.id === 'urf' : item.id === 'mru',
+				})),
+			};
 		});
 	}, []);

235-256: Chained empty-state conditions work correctly but have minor redundancies.

Since this branch is reached only when !isPending && !isError, data is guaranteed to be defined. However, some conditions are redundant:

  • Line 242: !isMarketplace is always true when context === 'private'
  • Line 243: When context !== 'private', checking !isMarketplace is needed only for 'installed' or 'requested' contexts

The logic is correct, but could be slightly simplified for clarity.

♻️ Simplified conditions
-			{(context === 'requested' && data.count === 0 && <NoAppRequestsEmptyState />) ||
-				((isMarketplace || isPremium) && data.count === 0 && (
+			{(isRequested && data.count === 0 && <NoAppRequestsEmptyState />) ||
+				((isMarketplace || isPremium) && data.count === 0 && (
 					<NoMarketplaceOrInstalledAppMatchesEmptyState shouldShowSearchText={!!data.shouldShowSearchText} text={text} />
 				)) ||
 				(context === 'installed' && data.totalAppsLength !== 0 && data.count === 0 && (
 					<NoInstalledAppMatchesEmptyState shouldShowSearchText={!!data.shouldShowSearchText} text={text} onButtonClick={handleReturn} />
 				)) ||
-				(context === 'private' && !isMarketplace && data.totalAppsLength === 0 && <PrivateEmptyState />) ||
-				(context !== 'private' && !isMarketplace && data.totalAppsLength === 0 && (
+				(context === 'private' && data.totalAppsLength === 0 && <PrivateEmptyState />) ||
+				(context === 'installed' && data.totalAppsLength === 0 && (
 					<NoInstalledAppsEmptyState onButtonClick={handleReturn} />
 				)) || (
apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts (1)

70-107: Consider removing stable callbacks from the dependency array.

The useEffectEvent callbacks (getQueryKey, doFilter, doCompare, mutateQueryData) return stable references by design and don't need to be included in the dependency array. Including them is harmless but adds noise.

♻️ Optional cleanup
-	}, [subscribeToRoomMessages, subscribeToNotifyRoom, userId, roomId, queryClient, getQueryKey, doFilter, doCompare, mutateQueryData]);
+	}, [subscribeToRoomMessages, subscribeToNotifyRoom, userId, roomId, queryClient]);
apps/meteor/client/views/marketplace/hooks/useApps.ts (1)

98-100: Consider guarding instead of throwing during render.

Throwing synchronously in hook body crashes the component tree without an error boundary. While this may be intentional to enforce proper context usage, consider returning an error state or adding enabled: !!orchestrator guards to queries if graceful degradation is preferred.


📜 Recent 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 d1284a3 and 49124fd.

📒 Files selected for processing (3)
  • apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts
  • apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx
  • apps/meteor/client/views/marketplace/hooks/useApps.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/marketplace/hooks/useApps.ts
  • apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts
  • apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx
🧠 Learnings (9)
📚 Learning: 2025-12-02T22:23:49.593Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 37654
File: apps/meteor/client/hooks/useAppSlashCommands.ts:32-38
Timestamp: 2025-12-02T22:23:49.593Z
Learning: In apps/meteor/client/hooks/useAppSlashCommands.ts, the `data?.forEach((command) => slashCommands.add(command))` call during render is intentional. The query is configured with `structuralSharing: false` to prevent React Query from keeping stable data references, and `slashCommands.add` is idempotent, so executing on every render is acceptable and ensures the command registry stays current.

Applied to files:

  • apps/meteor/client/views/marketplace/hooks/useApps.ts
  • apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts
  • apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx
📚 Learning: 2025-11-07T14:50:33.544Z
Learnt from: KevLehman
Repo: RocketChat/Rocket.Chat PR: 37423
File: packages/i18n/src/locales/en.i18n.json:18-18
Timestamp: 2025-11-07T14:50:33.544Z
Learning: Rocket.Chat settings: in apps/meteor/ee/server/settings/abac.ts, the Abac_Cache_Decision_Time_Seconds setting uses invalidValue: 0 as the fallback when ABAC is unlicensed. With a valid license, admins can still set the value to 0 to intentionally disable the ABAC decision cache.

Applied to files:

  • apps/meteor/client/views/marketplace/hooks/useApps.ts
📚 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/marketplace/hooks/useApps.ts
  • apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts
  • apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.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/marketplace/hooks/useApps.ts
📚 Learning: 2025-11-19T12:32:29.696Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 37547
File: packages/i18n/src/locales/en.i18n.json:634-634
Timestamp: 2025-11-19T12:32:29.696Z
Learning: Repo: RocketChat/Rocket.Chat
Context: i18n workflow
Learning: In this repository, new translation keys should be added to packages/i18n/src/locales/en.i18n.json only; other locale files are populated via the external translation pipeline and/or fall back to English. Do not request adding the same key to all locale files in future reviews.

Applied to files:

  • apps/meteor/client/views/marketplace/hooks/useApps.ts
  • apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx
📚 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/marketplace/hooks/useApps.ts
📚 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/marketplace/hooks/useApps.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/marketplace/hooks/useApps.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/page-objects/**/*.ts : Utilize existing page objects pattern from `apps/meteor/tests/e2e/page-objects/`

Applied to files:

  • apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx
🧬 Code graph analysis (3)
apps/meteor/client/views/marketplace/hooks/useApps.ts (5)
apps/meteor/tests/data/apps/apps-data.ts (2)
  • apps (10-12)
  • installedApps (14-16)
apps/meteor/client/lib/queryKeys.ts (1)
  • marketplaceQueryKeys (165-184)
apps/meteor/client/contexts/AppsContext.tsx (1)
  • AppsContext (29-29)
packages/ui-contexts/src/index.ts (2)
  • usePermission (57-57)
  • useStream (77-77)
apps/meteor/client/hooks/useIsEnterprise.ts (1)
  • useIsEnterprise (4-6)
apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts (2)
packages/mock-providers/src/MockedAppRootBuilder.tsx (1)
  • queryClient (270-280)
packages/ui-contexts/src/index.ts (1)
  • useStream (77-77)
apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx (2)
apps/meteor/client/views/marketplace/hooks/useAppsReload.ts (1)
  • useAppsReload (6-11)
apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts (1)
  • useFilteredApps (15-130)
🔇 Additional comments (8)
apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx (3)

165-187: Good loading state implementation.

The loading branch correctly shows the skeleton while keeping filters interactive, providing good UX. The context fallback ensures the translation key is always valid.


189-215: Error handling is functional with acknowledged tech debt.

The TODO comment appropriately documents the fragility of string-matching error messages. The reload function from useAppsReload properly invalidates React Query cache for retry functionality.


246-256: Clean integration with the new data-driven approach.

The AppsPageContentBody receives data as appsResult, which aligns with the new useFilteredApps return shape containing items, count, totalAppsLength, and pagination fields.

apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts (2)

1-18: LGTM on imports and hook signature.

The generic constraints are well-designed, and the default compare function correctly sorts messages by timestamp descending. The type guard pattern for the filter function provides clean type narrowing.


28-66: Well-structured cache mutation logic.

The mutation function correctly:

  • Tracks length changes to adjust itemCount dynamically
  • Guards against division by zero with the items.length > 0 check
  • Reconstructs page structure while preserving pagination semantics
apps/meteor/client/views/marketplace/hooks/useApps.ts (3)

1-14: LGTM!

The use of toSorted correctly avoids mutating the input array, addressing the previous mutation concern.


21-75: LGTM!

The aggregation logic correctly merges marketplace and installed app data, properly handling the categorization into marketplace, installed, and private apps.


133-159: LGTM!

Both queries are properly configured with appropriate stale times and refetch strategies.

✏️ Tip: You can disable this entire section by setting review_details to false in your review 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

codecov bot commented Dec 23, 2025

Codecov Report

❌ Patch coverage is 54.86443% with 283 lines in your changes missing coverage. Please review.
✅ Project coverage is 70.67%. Comparing base (0008ed2) to head (49124fd).
⚠️ Report is 14 commits behind head on develop.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop   #37944      +/-   ##
===========================================
- Coverage    70.70%   70.67%   -0.03%     
===========================================
  Files         3147     3133      -14     
  Lines       108849   108340     -509     
  Branches     19568    19513      -55     
===========================================
- Hits         76964    76574     -390     
+ Misses       29875    29771     -104     
+ Partials      2010     1995      -15     
Flag Coverage Δ
e2e 60.31% <44.72%> (+0.15%) ⬆️
e2e-api 47.45% <ø> (-0.95%) ⬇️
unit 71.75% <48.67%> (-0.11%) ⬇️

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.

@github-actions
Copy link
Contributor

github-actions bot commented Dec 23, 2025

📦 Docker Image Size Report

➡️ Changes

Service Current Baseline Change Percent
sum of all images 0B 0B 0B
account-service 0B 0B 0B
authorization-service 0B 0B 0B
ddp-streamer-service 0B 0B 0B
omnichannel-transcript-service 0B 0B 0B
presence-service 0B 0B 0B
queue-worker-service 0B 0B 0B
rocketchat 0B 0B 0B

📊 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/15 15:07 (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.00]
  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.00]
  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.00]
  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.00]
  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.00]
  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.00]
  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.00]
Loading

Statistics (last 30 days):

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

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

  • Tag: pr-37944
  • Baseline: develop
  • Timestamp: 2026-01-15 15:07:53 UTC
  • Historical data points: 30

Updated: Thu, 15 Jan 2026 15:07:53 GMT

@tassoevan tassoevan force-pushed the chore/async-state branch 5 times, most recently from 361a56a to aa4b3ef Compare December 24, 2025 14:11
@dionisio-bot
Copy link
Contributor

dionisio-bot bot commented Dec 24, 2025

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

@tassoevan tassoevan added this to the 8.1.0 milestone Dec 24, 2025
@tassoevan tassoevan marked this pull request as ready for review December 24, 2025 23:02
@tassoevan tassoevan requested a review from a team as a code owner December 24, 2025 23:02
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.

15 issues found across 72 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/marketplace/hooks/usePrivateAppsEnabled.ts">

<violation number="1" location="apps/meteor/client/views/marketplace/hooks/usePrivateAppsEnabled.ts:3">
P2: Hook returns `false` during loading state when data is undefined, causing UI flicker. The async hook&#39;s `data` is initially `undefined`, making the expression evaluate to `false` (disabled) before potentially becoming `true` once loaded. Consider returning the loading state alongside the value, or returning `undefined` during loading so consumers can handle it appropriately.</violation>
</file>

<file name="apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts">

<violation number="1" location="apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts:83">
P1: Query key does not include `type` and `text` parameters, causing stale data when filters change. React-query won&#39;t refetch when these parameters change since the key only contains `rid`.</violation>

<violation number="2" location="apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts:109">
P1: Pagination offset is calculated from filtered item count, but should track raw API response count. If client-side filtering removes any items, the offset will be incorrect - causing duplicate fetches and potentially missing items. Consider tracking the pre-filtered count separately for offset calculation, or remove client-side filtering if server-side filtering is comprehensive.</violation>
</file>

<file name="apps/meteor/client/views/teams/contextualBar/channels/hooks/useTeamsChannelList.ts">

<violation number="1" location="apps/meteor/client/views/teams/contextualBar/channels/hooks/useTeamsChannelList.ts:39">
P1: Spreading an array into an object creates numeric keys instead of preserving the `usersWaitingForE2EKeys` property. This corrupts the room object structure - the property won&#39;t exist, and numeric keys will be added instead.</violation>
</file>

<file name="apps/meteor/client/providers/AppsProvider.tsx">

<violation number="1" location="apps/meteor/client/providers/AppsProvider.tsx:11">
P2: Context value object is created inline, causing unnecessary re-renders of all consumers. Since `AppClientOrchestratorInstance` is a static import, define the value object outside the component to maintain referential stability.</violation>
</file>

<file name="apps/meteor/client/views/marketplace/hooks/useApps.ts">

<violation number="1" location="apps/meteor/client/views/marketplace/hooks/useApps.ts:127">
P2: Using `!isEnterprise` when `isEnterprise` can be `undefined` causes unintended license invalidation during loading. Since `isEnterprise` is explicitly set to `undefined` while loading (line 100), and `!undefined === true`, the license query will be invalidated before the enterprise status is determined. Use explicit equality check `isEnterprise === false` instead.</violation>

<violation number="2" location="apps/meteor/client/views/marketplace/hooks/useApps.ts:164">
P2: Missing error handling for `instance` query. Marketplace errors are propagated but instance errors are silently ignored, which could show no installed apps without any error indication if the instance query fails.</violation>
</file>

<file name="apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx">

<violation number="1" location="apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx:135">
P2: Missing `isSuccess` check for consistency with other conditions. All similar state variables (`noInstalledApps`, `noMarketplaceOrInstalledAppMatches`, `noInstalledAppMatches`) check `isSuccess` first before accessing `data`, but `noAppRequests` does not. Add the `isSuccess &amp;&amp;` check for consistency and to ensure `data` is only accessed after a successful query.

(Based on your team&#39;s feedback about avoiding treating undefined as falsy and adding explicit loading checks.) [FEEDBACK_USED]</violation>
</file>

<file name="apps/meteor/client/views/mailer/MailerUnsubscriptionPage.tsx">

<violation number="1" location="apps/meteor/client/views/mailer/MailerUnsubscriptionPage.tsx:43">
P2: The mutation starts in `isIdle` state before the `useEffect` triggers it, causing the UI to briefly show nothing. Consider using `isPending || isIdle` or `!isError &amp;&amp; !isSuccess` to show the loading state during the initial render and when params are missing.

(Based on your team&#39;s feedback about async hooks having undefined initial states that can cause UI flicker.) [FEEDBACK_USED]</violation>
</file>

<file name="apps/meteor/client/components/message/content/location/MapView.tsx">

<violation number="1" location="apps/meteor/client/components/message/content/location/MapView.tsx:16">
P2: Consider destructuring `isLoading` to distinguish between &quot;no image URL provided&quot; and &quot;image still loading&quot; states. Currently, `!imageUrl` is true both when the API key is missing AND while the image is loading, which may cause UI flicker (showing fallback briefly before the image appears).

(Based on your team&#39;s feedback about adding explicit loading checks when destructuring from async hooks to prevent UI flicker.) [FEEDBACK_USED]</violation>
</file>

<file name="apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/WrapCannedResponseList.tsx">

<violation number="1" location="apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/WrapCannedResponseList.tsx:34">
P2: Removed loading state causes UI flicker - during initial fetch, `data` is undefined so `itemCount` defaults to 0, causing the empty state (&#39;No Canned Responses&#39;) to briefly display instead of a loading indicator before data arrives.</violation>
</file>

<file name="apps/meteor/client/components/RoomIcon/OmnichannelRoomIcon/provider/OmnichannelRoomIconProvider.tsx">

<violation number="1" location="apps/meteor/client/components/RoomIcon/OmnichannelRoomIcon/provider/OmnichannelRoomIconProvider.tsx:50">
P1: Caching breaks for undefined values after type change. The `!snapshot` check will always be true when `extractSnapshot` returns `undefined`, causing repeated API calls via `OmnichannelRoomIconManager.get()` until the icon loads. Use `snapshots.has()` instead to properly cache undefined values.</violation>
</file>

<file name="apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts">

<violation number="1" location="apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts:45">
P2: `itemCount` becomes stale after real-time updates because it preserves the original server count instead of reflecting the mutated array length. This affects Virtuoso&#39;s `totalCount` and pagination metadata in components using this hook.</violation>
</file>

<file name="apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts">

<violation number="1" location="apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts:63">
P1: The sorting methods mutate the array in-place with `apps.sort()`. In a react-query `select` function, this will corrupt the cached data since the same array reference is shared. Use `[...apps].sort()` to create a copy before sorting, or change `fallback` to return a copy: `(apps: App[]) =&gt; [...apps]`.</violation>
</file>

<file name="apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useFilesList.ts">

<violation number="1" location="apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useFilesList.ts:25">
P2: Consider adding `enabled: !!room` to prevent the query from running before `useUserRoom` returns room data. Without this, if `room` is temporarily undefined, the query may use the wrong API endpoint (defaults to `channels.files` instead of the correct endpoint for DMs or private groups).</violation>
</file>

Since this is your first cubic review, here's how it works:

  • cubic automatically reviews your code and comments on bugs and improvements
  • Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
  • Ask questions if you need clarification on any suggestion

Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR

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

Caution

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

⚠️ Outside diff range comments (2)
apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useFilesList.ts (1)

10-21: Handle case when room is undefined.

If useUserRoom(rid) returns undefined (e.g., room not found or user lacks access), room?.t falls back to 'c' for the endpoint selection. This may lead to an API call to /v1/channels.files for a room the user cannot access, potentially resulting in a 403 or 404. Consider adding a guard or disabling the query when room is undefined.

🔎 Suggested fix
 export const useFilesList = ({ rid, type, text }: { rid: Required<IUpload>['rid']; type: string; text: string }) => {
 	const room = useUserRoom(rid);

 	const roomTypes = {
 		c: '/v1/channels.files',
 		l: '/v1/channels.files',
 		v: '/v1/channels.files',
 		d: '/v1/im.files',
 		p: '/v1/groups.files',
 	} as const;

-	const getFiles = useEndpoint('GET', roomTypes[room?.t ?? 'c']);
+	const roomType = room?.t ?? 'c';
+	const getFiles = useEndpoint('GET', roomTypes[roomType]);

Then add enabled: !!room to the query options:

 	return useInfiniteQuery({
 		queryKey: roomsQueryKeys.files(rid, { type, text }),
+		enabled: !!room,
 		queryFn: async ({ pageParam: offset }) => {
apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx (1)

86-91: Missing dependency in useEffect.

The useEffect depends on t (from useTranslation) via createFilterStructureItems() which uses it, but t is not in the dependency array. This could cause stale translations if the locale changes.

🔎 Proposed fix
 useEffect(() => {
 	setSortFilterStructure({
 		label: t('Sort_By'),
 		items: createFilterStructureItems(),
 	});
-}, [isRequested]);
+}, [isRequested, t]);

Note: You may also need to memoize createFilterStructureItems or inline it to avoid lint warnings.

🧹 Nitpick comments (11)
apps/meteor/client/views/room/ImageGallery/hooks/useImagesList.ts (1)

30-46: Consider adding error handling for E2E decryption.

If e2e.decryptFileContent(file) or e2e.getInstanceByRoomId() throws an error for any single file, the entire query will fail and no images will be displayed. Consider wrapping the decryption logic in a try-catch to gracefully handle corrupted or unreadable encrypted files while still displaying the rest.

🔎 Proposed fix
 			for await (const file of items) {
 				if (file.rid && file.content) {
-					const e2eRoom = await e2e.getInstanceByRoomId(file.rid);
-					if (e2eRoom?.shouldConvertReceivedMessages()) {
-						const decrypted = await e2e.decryptFileContent(file);
-						const key = Base64.encode(
-							JSON.stringify({
-								...decrypted.encryption,
-								name: String.fromCharCode(...new TextEncoder().encode(decrypted.name)),
-								type: decrypted.type,
-							}),
-						);
-						decrypted.path = `/file-decrypt${decrypted.path}?key=${key}`;
-						Object.assign(file, decrypted);
+					try {
+						const e2eRoom = await e2e.getInstanceByRoomId(file.rid);
+						if (e2eRoom?.shouldConvertReceivedMessages()) {
+							const decrypted = await e2e.decryptFileContent(file);
+							const key = Base64.encode(
+								JSON.stringify({
+									...decrypted.encryption,
+									name: String.fromCharCode(...new TextEncoder().encode(decrypted.name)),
+									type: decrypted.type,
+								}),
+							);
+							decrypted.path = `/file-decrypt${decrypted.path}?key=${key}`;
+							Object.assign(file, decrypted);
+						}
+					} catch {
+						// Skip files that fail to decrypt
 					}
 				}
 			}
apps/meteor/client/components/message/content/location/hooks/useAsyncImage.ts (1)

8-11: Consider removing redundant guard clause.

The guard clause on lines 8-11 is redundant since enabled: Boolean(src) on line 22 already prevents the query from running when src is falsy. React Query won't execute queryFn when enabled is false.

🔎 Proposed simplification
 queryFn: () =>
 	new Promise<string>((resolve, reject) => {
-		if (!src) {
-			reject(new Error('No src provided'));
-			return;
-		}
-
 		const image = new Image();
 		image.addEventListener('load', () => {
 			resolve(image.src);
 		});
 		image.addEventListener('error', (e) => {
-			reject(e.error);
+			reject(new Error('Failed to load image'));
 		});
 		image.src = src;
 	}),

Note: If you keep the guard, add a TypeScript non-null assertion image.src = src!; on line 20 since the type checker won't know src is defined inside the closure.

apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts (2)

24-37: Consider memoizing the regex creation for repeated filter calls.

The filter function creates a new RegExp on every invocation. Since this filter is called for each message in both queryFn and real-time updates, the regex could be created once when text changes rather than on every filter call.

🔎 Proposed optimization
+	const regex = text ? new RegExp(escapeRegExp(text), 'i') : undefined;
+
 	const filter = useEffectEvent((message: IMessage): message is IDiscussionMessage => {
 		if (!isDiscussionMessageInRoom(message, rid)) {
 			return false;
 		}
 
-		if (text) {
-			const regex = new RegExp(escapeRegExp(text), 'i');
-			if (!isDiscussionTextMatching(message, regex)) {
-				return false;
-			}
+		if (regex && !isDiscussionTextMatching(message, regex)) {
+			return false;
 		}
 
 		return true;
 	}) as (message: IMessage) => message is IDiscussionMessage;

56-60: Redundant filtering after server-filtered API response.

The server already filters discussions by text at the database level in findDiscussionsByRoomAndText, applying a regex match on the msg field. The client-side re-filtering via .filter(filter) duplicates this work. If this pattern is intentional for consistency with how real-time updates are handled through useInfiniteMessageQueryUpdates, consider documenting that intent; otherwise, the client-side filter on line 59 can be removed for API results and applied only to real-time updates.

apps/meteor/client/views/room/contextualBar/Discussions/DiscussionsListContextBar.tsx (1)

45-46: Consider using hasNextPage guard before calling fetchNextPage.

The loadMoreItems callback unconditionally calls fetchNextPage(). While React Query handles this gracefully (it won't refetch if there's no next page), adding a guard improves clarity and avoids unnecessary function calls.

🔎 Proposed improvement
-	const { isPending, error, data, fetchNextPage } = useDiscussionsList(options);
+	const { isPending, error, data, fetchNextPage, hasNextPage } = useDiscussionsList(options);
-			loadMoreItems={() => fetchNextPage()}
+			loadMoreItems={() => hasNextPage && fetchNextPage()}
apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts (1)

28-66: itemCount may become stale after real-time mutations.

When messages are added or removed via subscriptions, itemCount (line 45) is preserved from the last fetched page rather than recalculated. This could cause minor UI inconsistencies (e.g., a "load more" indicator appearing when all items are already loaded).

Consider updating itemCount to reflect the actual items.length after mutations, or document this as intentional (relying on the next fetch to reconcile).

🔎 Optional fix to update itemCount
-		const itemCount = lastPage.itemCount || items.length;
+		const itemCount = items.length;

Or, if preserving server-side total is preferred, leave as-is and rely on query invalidation for reconciliation.

apps/meteor/client/views/mailer/MailerUnsubscriptionPage.tsx (1)

31-31: Optional: Remove stable reference from dependency array.

The mutate function from React Query has a stable reference and doesn't need to be included in the dependency array. While not harmful, removing it makes the intent clearer.

🔎 Proposed refactor
-	}, [_id, createdAt, mutate]);
+	}, [_id, createdAt]);
apps/meteor/client/views/marketplace/AppsPage/PrivateEmptyState.spec.tsx (1)

27-32: Consider moving appRoot inside beforeEach or it for better test isolation.

The appRoot is defined at describe-block scope but uses a mock that's configured in beforeEach. While this works because appRoot.build() is called inside it(), placing appRoot construction closer to usage would make the test's data flow clearer and prevent potential issues if tests are reordered or parallelized.

apps/meteor/client/lib/queryKeys.ts (1)

164-164: Consider stronger typing for appsStored arguments.

Using ...args: unknown[] loses type information for cache key matching. If the arguments follow a known pattern (similar to how appsMarketplace and appsInstance use canManageApps?: boolean), consider defining explicit parameter types.

apps/meteor/client/providers/AppsProvider.tsx (1)

10-12: Use JSX children syntax instead of the children prop.

The children prop pattern works but is non-idiomatic. Per React best practices (and flagged by Biome), pass children as JSX content.

🔎 Proposed fix
-const AppsProvider = ({ children }: AppsProviderProps) => {
-	return <AppsContext.Provider children={children} value={{ orchestrator: AppClientOrchestratorInstance }} />;
-};
+const AppsProvider = ({ children }: AppsProviderProps) => {
+	return <AppsContext.Provider value={{ orchestrator: AppClientOrchestratorInstance }}>{children}</AppsContext.Provider>;
+};
apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx (1)

125-136: Consider guarding data access in noAppRequests check.

At line 135, data?.count is accessed without checking isSuccess first. While the optional chaining prevents runtime errors, the condition could be true during loading state if data happens to be undefined.

🔎 Suggested improvement
-const noAppRequests = context === 'requested' && data?.count === 0;
+const noAppRequests = isSuccess && context === 'requested' && data.count === 0;
📜 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 108f7f5 and 032f070.

📒 Files selected for processing (72)
  • apps/meteor/client/components/RoomIcon/OmnichannelRoomIcon/OmnichannelAppSourceRoomIcon.tsx
  • apps/meteor/client/components/RoomIcon/OmnichannelRoomIcon/context/OmnichannelRoomIconContext.tsx
  • apps/meteor/client/components/RoomIcon/OmnichannelRoomIcon/provider/OmnichannelRoomIconProvider.tsx
  • apps/meteor/client/components/message/content/location/MapView.tsx
  • apps/meteor/client/components/message/content/location/hooks/useAsyncImage.ts
  • apps/meteor/client/contexts/AppsContext.tsx
  • apps/meteor/client/contexts/hooks/useAppsReload.ts
  • apps/meteor/client/contexts/hooks/useAppsResult.ts
  • apps/meteor/client/hooks/lists/useRecordList.ts
  • apps/meteor/client/hooks/lists/useScrollableMessageList.ts
  • apps/meteor/client/hooks/lists/useScrollableRecordList.ts
  • apps/meteor/client/hooks/lists/useStreamUpdatesForMessageList.ts
  • apps/meteor/client/hooks/useAsyncState.ts
  • apps/meteor/client/hooks/useComponentDidUpdate.ts
  • apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts
  • apps/meteor/client/hooks/useRoomsList.ts
  • apps/meteor/client/lib/asyncState/AsyncState.ts
  • apps/meteor/client/lib/asyncState/AsyncStatePhase.ts
  • apps/meteor/client/lib/asyncState/functions.ts
  • apps/meteor/client/lib/asyncState/index.ts
  • apps/meteor/client/lib/lists/CannedResponseList.ts
  • apps/meteor/client/lib/lists/DiscussionsList.ts
  • apps/meteor/client/lib/lists/FilesList.ts
  • apps/meteor/client/lib/lists/ImagesList.ts
  • apps/meteor/client/lib/lists/MessageList.ts
  • apps/meteor/client/lib/lists/RecordList.spec.ts
  • apps/meteor/client/lib/lists/RecordList.ts
  • apps/meteor/client/lib/lists/ThreadsList.ts
  • apps/meteor/client/lib/queryKeys.ts
  • apps/meteor/client/providers/AppsProvider.tsx
  • apps/meteor/client/providers/AppsProvider/AppsProvider.tsx
  • apps/meteor/client/providers/AppsProvider/index.ts
  • apps/meteor/client/providers/AppsProvider/storeQueryFunction.ts
  • apps/meteor/client/views/mailer/MailerUnsubscriptionPage.tsx
  • apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppRequests/AppRequests.tsx
  • apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx
  • apps/meteor/client/views/marketplace/AppsPage/PrivateEmptyState.spec.tsx
  • apps/meteor/client/views/marketplace/AppsPage/UnsupportedEmptyState.spec.tsx
  • apps/meteor/client/views/marketplace/hooks/useAppInfo.ts
  • apps/meteor/client/views/marketplace/hooks/useAppInstances.ts
  • apps/meteor/client/views/marketplace/hooks/useApps.spec.ts
  • apps/meteor/client/views/marketplace/hooks/useApps.ts
  • apps/meteor/client/views/marketplace/hooks/useAppsReload.ts
  • apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts
  • apps/meteor/client/views/marketplace/hooks/useInstallApp.tsx
  • apps/meteor/client/views/marketplace/hooks/useLogs.ts
  • apps/meteor/client/views/marketplace/hooks/usePrivateAppsEnabled.ts
  • apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/CannedResponseList.tsx
  • apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/WrapCannedResponseList.tsx
  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessagesList.tsx
  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts
  • apps/meteor/client/views/omnichannel/contactInfo/tabs/ContactInfoHistory/ContactInfoHistoryMessages.tsx
  • apps/meteor/client/views/omnichannel/departments/EditDepartment.tsx
  • apps/meteor/client/views/omnichannel/hooks/useCannedResponseList.ts
  • apps/meteor/client/views/room/ImageGallery/ImageGalleryData.tsx
  • apps/meteor/client/views/room/ImageGallery/hooks/useImagesList.ts
  • apps/meteor/client/views/room/contextualBar/Discussions/DiscussionsListContextBar.tsx
  • apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts
  • apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx
  • apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFilesWithData.tsx
  • apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useFilesList.ts
  • apps/meteor/client/views/room/contextualBar/Threads/ThreadList.tsx
  • apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts
  • apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/VideoConfList.tsx
  • apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/VideoConfListWithData.tsx
  • apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/VideoConfRecordList.ts
  • apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/useVideoConfList.ts
  • apps/meteor/client/views/teams/contextualBar/channels/TeamsChannels.tsx
  • apps/meteor/client/views/teams/contextualBar/channels/TeamsChannelsWithData.tsx
  • apps/meteor/client/views/teams/contextualBar/channels/hooks/useTeamsChannelList.ts
  • apps/meteor/ee/server/apps/marketplace/fetchMarketplaceApps.ts
  • apps/meteor/tests/mocks/client/marketplace.tsx
💤 Files with no reviewable changes (25)
  • apps/meteor/client/lib/lists/MessageList.ts
  • apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/VideoConfRecordList.ts
  • apps/meteor/client/lib/asyncState/AsyncState.ts
  • apps/meteor/client/contexts/hooks/useAppsReload.ts
  • apps/meteor/client/lib/lists/ImagesList.ts
  • apps/meteor/client/hooks/useComponentDidUpdate.ts
  • apps/meteor/client/contexts/hooks/useAppsResult.ts
  • apps/meteor/client/providers/AppsProvider/AppsProvider.tsx
  • apps/meteor/client/hooks/lists/useRecordList.ts
  • apps/meteor/client/providers/AppsProvider/storeQueryFunction.ts
  • apps/meteor/client/lib/asyncState/AsyncStatePhase.ts
  • apps/meteor/client/contexts/AppsContext.tsx
  • apps/meteor/client/lib/lists/RecordList.ts
  • apps/meteor/client/hooks/lists/useStreamUpdatesForMessageList.ts
  • apps/meteor/client/hooks/lists/useScrollableMessageList.ts
  • apps/meteor/client/lib/lists/DiscussionsList.ts
  • apps/meteor/client/lib/asyncState/functions.ts
  • apps/meteor/client/hooks/lists/useScrollableRecordList.ts
  • apps/meteor/client/hooks/useAsyncState.ts
  • apps/meteor/client/lib/asyncState/index.ts
  • apps/meteor/client/providers/AppsProvider/index.ts
  • apps/meteor/client/lib/lists/ThreadsList.ts
  • apps/meteor/client/lib/lists/CannedResponseList.ts
  • apps/meteor/client/lib/lists/FilesList.ts
  • apps/meteor/client/lib/lists/RecordList.spec.ts
🧰 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:

  • apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/VideoConfList.tsx
  • apps/meteor/client/views/marketplace/hooks/useAppsReload.ts
  • apps/meteor/client/views/marketplace/hooks/useAppInstances.ts
  • apps/meteor/ee/server/apps/marketplace/fetchMarketplaceApps.ts
  • apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppRequests/AppRequests.tsx
  • apps/meteor/client/views/marketplace/AppsPage/PrivateEmptyState.spec.tsx
  • apps/meteor/client/components/RoomIcon/OmnichannelRoomIcon/OmnichannelAppSourceRoomIcon.tsx
  • apps/meteor/client/views/marketplace/hooks/useLogs.ts
  • apps/meteor/client/views/omnichannel/contactInfo/tabs/ContactInfoHistory/ContactInfoHistoryMessages.tsx
  • apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts
  • apps/meteor/client/views/omnichannel/hooks/useCannedResponseList.ts
  • apps/meteor/client/views/room/contextualBar/Discussions/DiscussionsListContextBar.tsx
  • apps/meteor/client/views/room/ImageGallery/ImageGalleryData.tsx
  • apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts
  • apps/meteor/client/views/marketplace/hooks/useApps.spec.ts
  • apps/meteor/client/views/marketplace/AppsPage/UnsupportedEmptyState.spec.tsx
  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessagesList.tsx
  • apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/VideoConfListWithData.tsx
  • apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFilesWithData.tsx
  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts
  • apps/meteor/client/views/marketplace/hooks/useAppInfo.ts
  • apps/meteor/client/providers/AppsProvider.tsx
  • apps/meteor/client/views/marketplace/hooks/usePrivateAppsEnabled.ts
  • apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts
  • apps/meteor/client/views/marketplace/hooks/useApps.ts
  • apps/meteor/client/components/message/content/location/hooks/useAsyncImage.ts
  • apps/meteor/client/views/teams/contextualBar/channels/TeamsChannelsWithData.tsx
  • apps/meteor/tests/mocks/client/marketplace.tsx
  • apps/meteor/client/views/mailer/MailerUnsubscriptionPage.tsx
  • apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx
  • apps/meteor/client/views/omnichannel/departments/EditDepartment.tsx
  • apps/meteor/client/views/marketplace/hooks/useInstallApp.tsx
  • apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useFilesList.ts
  • apps/meteor/client/views/teams/contextualBar/channels/TeamsChannels.tsx
  • apps/meteor/client/components/RoomIcon/OmnichannelRoomIcon/provider/OmnichannelRoomIconProvider.tsx
  • apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/CannedResponseList.tsx
  • apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts
  • apps/meteor/client/lib/queryKeys.ts
  • apps/meteor/client/components/RoomIcon/OmnichannelRoomIcon/context/OmnichannelRoomIconContext.tsx
  • apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/useVideoConfList.ts
  • apps/meteor/client/views/teams/contextualBar/channels/hooks/useTeamsChannelList.ts
  • apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx
  • apps/meteor/client/components/message/content/location/MapView.tsx
  • apps/meteor/client/views/room/ImageGallery/hooks/useImagesList.ts
  • apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/WrapCannedResponseList.tsx
  • apps/meteor/client/hooks/useRoomsList.ts
  • apps/meteor/client/views/room/contextualBar/Threads/ThreadList.tsx
**/*.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/marketplace/hooks/useApps.spec.ts
🧠 Learnings (24)
📚 Learning: 2025-12-02T22:23:49.593Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 37654
File: apps/meteor/client/hooks/useAppSlashCommands.ts:32-38
Timestamp: 2025-12-02T22:23:49.593Z
Learning: In apps/meteor/client/hooks/useAppSlashCommands.ts, the `data?.forEach((command) => slashCommands.add(command))` call during render is intentional. The query is configured with `structuralSharing: false` to prevent React Query from keeping stable data references, and `slashCommands.add` is idempotent, so executing on every render is acceptable and ensures the command registry stays current.

Applied to files:

  • apps/meteor/client/views/marketplace/hooks/useAppsReload.ts
  • apps/meteor/client/views/marketplace/hooks/useAppInstances.ts
  • apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppRequests/AppRequests.tsx
  • apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts
  • apps/meteor/client/views/marketplace/hooks/useApps.ts
  • apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts
  • apps/meteor/client/views/teams/contextualBar/channels/hooks/useTeamsChannelList.ts
  • apps/meteor/client/hooks/useRoomsList.ts
📚 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/ee/server/apps/marketplace/fetchMarketplaceApps.ts
  • apps/meteor/client/views/marketplace/hooks/useAppInfo.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/page-objects/**/*.ts : Utilize existing page objects pattern from `apps/meteor/tests/e2e/page-objects/`

Applied to files:

  • apps/meteor/ee/server/apps/marketplace/fetchMarketplaceApps.ts
  • apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppRequests/AppRequests.tsx
  • apps/meteor/client/views/marketplace/AppsPage/PrivateEmptyState.spec.tsx
  • apps/meteor/client/views/marketplace/hooks/useApps.spec.ts
  • apps/meteor/client/views/marketplace/AppsPage/UnsupportedEmptyState.spec.tsx
  • apps/meteor/client/views/marketplace/hooks/useAppInfo.ts
  • apps/meteor/tests/mocks/client/marketplace.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/marketplace/AppDetailsPage/tabs/AppRequests/AppRequests.tsx
  • apps/meteor/client/views/marketplace/AppsPage/PrivateEmptyState.spec.tsx
  • apps/meteor/client/views/marketplace/hooks/useApps.spec.ts
  • apps/meteor/client/views/marketplace/AppsPage/UnsupportedEmptyState.spec.tsx
  • apps/meteor/tests/mocks/client/marketplace.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/marketplace/AppsPage/PrivateEmptyState.spec.tsx
  • apps/meteor/client/views/marketplace/hooks/useApps.spec.ts
  • apps/meteor/client/views/marketplace/AppsPage/UnsupportedEmptyState.spec.tsx
  • apps/meteor/tests/mocks/client/marketplace.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/marketplace/AppsPage/PrivateEmptyState.spec.tsx
  • apps/meteor/client/views/marketplace/hooks/useApps.spec.ts
  • apps/meteor/client/views/marketplace/AppsPage/UnsupportedEmptyState.spec.tsx
  • apps/meteor/tests/mocks/client/marketplace.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/marketplace/AppsPage/PrivateEmptyState.spec.tsx
  • apps/meteor/client/views/marketplace/hooks/useApps.spec.ts
  • apps/meteor/client/views/marketplace/AppsPage/UnsupportedEmptyState.spec.tsx
  • apps/meteor/tests/mocks/client/marketplace.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/marketplace/AppsPage/PrivateEmptyState.spec.tsx
  • apps/meteor/client/views/marketplace/hooks/useApps.spec.ts
  • apps/meteor/client/views/marketplace/AppsPage/UnsupportedEmptyState.spec.tsx
  • apps/meteor/tests/mocks/client/marketplace.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} : Store commonly used locators in variables/constants for reuse

Applied to files:

  • apps/meteor/client/views/marketplace/AppsPage/PrivateEmptyState.spec.tsx
  • apps/meteor/client/views/marketplace/hooks/useApps.spec.ts
  • apps/meteor/tests/mocks/client/marketplace.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/marketplace/AppsPage/PrivateEmptyState.spec.tsx
  • apps/meteor/client/views/marketplace/hooks/useApps.spec.ts
  • apps/meteor/client/views/marketplace/AppsPage/UnsupportedEmptyState.spec.tsx
  • apps/meteor/tests/mocks/client/marketplace.tsx
📚 Learning: 2025-10-06T20:30:45.540Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 37152
File: packages/apps-engine/tests/test-data/storage/storage.ts:101-122
Timestamp: 2025-10-06T20:30:45.540Z
Learning: In `packages/apps-engine/tests/test-data/storage/storage.ts`, the stub methods (updatePartialAndReturnDocument, updateStatus, updateSetting, updateAppInfo, updateMarketplaceInfo) intentionally throw "Method not implemented." Tests using these methods must stub them using `SpyOn` from the test library rather than relying on actual implementations.

Applied to files:

  • apps/meteor/client/views/marketplace/AppsPage/PrivateEmptyState.spec.tsx
  • apps/meteor/client/views/marketplace/hooks/useApps.spec.ts
  • apps/meteor/client/views/marketplace/AppsPage/UnsupportedEmptyState.spec.tsx
  • apps/meteor/tests/mocks/client/marketplace.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/marketplace/AppsPage/PrivateEmptyState.spec.tsx
📚 Learning: 2025-11-17T15:07:13.273Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 37398
File: packages/fuselage-ui-kit/src/surfaces/FuselageSurfaceRenderer.tsx:357-363
Timestamp: 2025-11-17T15:07:13.273Z
Learning: In packages/fuselage-ui-kit/src/surfaces/FuselageSurfaceRenderer.tsx, IconElement is a presentational, non-actionable element that does not require wrapping in AppIdProvider, similar to plain_text and mrkdwn renderers. Only actionable elements (those with actions, actionId, or interactive behavior) should be wrapped in AppIdProvider.

Applied to files:

  • apps/meteor/client/components/RoomIcon/OmnichannelRoomIcon/OmnichannelAppSourceRoomIcon.tsx
📚 Learning: 2025-12-18T15:18:31.688Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 37773
File: apps/meteor/client/views/mediaCallHistory/MediaCallHistoryInternal.tsx:24-34
Timestamp: 2025-12-18T15:18:31.688Z
Learning: In apps/meteor/client/views/mediaCallHistory/MediaCallHistoryInternal.tsx, for internal call history items, the item.contactId is guaranteed to always match either the caller.id or callee.id in the call data, so the contact resolution in getContact will never result in undefined.

Applied to files:

  • apps/meteor/client/views/omnichannel/contactInfo/tabs/ContactInfoHistory/ContactInfoHistoryMessages.tsx
  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessagesList.tsx
  • apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/VideoConfListWithData.tsx
  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts
  • apps/meteor/client/lib/queryKeys.ts
📚 Learning: 2025-11-19T18:20:37.116Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 37419
File: apps/meteor/server/services/media-call/service.ts:141-141
Timestamp: 2025-11-19T18:20:37.116Z
Learning: In apps/meteor/server/services/media-call/service.ts, the sendHistoryMessage method should use call.caller.id or call.createdBy?.id as the message author, not call.transferredBy?.id. Even for transferred calls, the message should appear in the DM between the two users who are calling each other, not sent by the person who transferred the call.

Applied to files:

  • apps/meteor/client/views/omnichannel/contactInfo/tabs/ContactInfoHistory/ContactInfoHistoryMessages.tsx
  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessagesList.tsx
  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.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 : All test files must be created in `apps/meteor/tests/e2e/` directory

Applied to files:

  • apps/meteor/client/views/marketplace/hooks/useApps.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/marketplace/hooks/useApps.spec.ts
  • apps/meteor/client/views/marketplace/AppsPage/UnsupportedEmptyState.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/marketplace/AppsPage/UnsupportedEmptyState.spec.tsx
  • apps/meteor/tests/mocks/client/marketplace.tsx
📚 Learning: 2025-11-27T17:56:26.050Z
Learnt from: MartinSchoeler
Repo: RocketChat/Rocket.Chat PR: 37557
File: apps/meteor/client/views/admin/ABAC/AdminABACRooms.tsx:115-116
Timestamp: 2025-11-27T17:56:26.050Z
Learning: In Rocket.Chat, the GET /v1/abac/rooms endpoint (implemented in ee/packages/abac/src/index.ts) only returns rooms where abacAttributes exists and is not an empty array (query: { abacAttributes: { $exists: true, $ne: [] } }). Therefore, in components consuming this endpoint (like AdminABACRooms.tsx), room.abacAttributes is guaranteed to be defined for all returned rooms, and optional chaining before calling array methods like .join() is sufficient without additional null coalescing.

Applied to files:

  • apps/meteor/client/views/omnichannel/departments/EditDepartment.tsx
  • apps/meteor/client/lib/queryKeys.ts
  • apps/meteor/client/views/teams/contextualBar/channels/hooks/useTeamsChannelList.ts
  • apps/meteor/client/views/room/ImageGallery/hooks/useImagesList.ts
  • apps/meteor/client/hooks/useRoomsList.ts
📚 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/lib/queryKeys.ts
📚 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/lib/queryKeys.ts
📚 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/lib/queryKeys.ts
📚 Learning: 2025-10-24T08:36:09.247Z
Learnt from: yiweigao0226
Repo: RocketChat/Rocket.Chat PR: 36746
File: apps/meteor/client/views/room/ShareLocation/LiveLocationModal.tsx:0-0
Timestamp: 2025-10-24T08:36:09.247Z
Learning: The location sharing feature in apps/meteor/client/views/room/ShareLocation/ uses MapLibre GL JS for map rendering instead of Google Maps or LocationIQ, eliminating the need for API keys in client code.

Applied to files:

  • apps/meteor/client/components/message/content/location/MapView.tsx
📚 Learning: 2025-10-24T08:37:21.101Z
Learnt from: yiweigao0226
Repo: RocketChat/Rocket.Chat PR: 36746
File: apps/meteor/client/views/room/ShareLocation/ShareLocationModal.tsx:0-0
Timestamp: 2025-10-24T08:37:21.101Z
Learning: The location sharing feature is migrating from Google Maps and LocationIQ to MapLibre GL JS to eliminate hardcoded API keys in client code, addressing security concerns about key exposure in the client bundle and message attachments.

Applied to files:

  • apps/meteor/client/components/message/content/location/MapView.tsx
🧬 Code graph analysis (29)
apps/meteor/client/views/marketplace/hooks/useAppsReload.ts (2)
packages/mock-providers/src/MockedAppRootBuilder.tsx (1)
  • queryClient (270-280)
apps/meteor/client/lib/queryKeys.ts (1)
  • marketplaceQueryKeys (160-179)
apps/meteor/client/views/marketplace/hooks/useAppInstances.ts (2)
packages/rest-typings/src/index.ts (1)
  • OperationResult (191-193)
apps/meteor/client/lib/queryKeys.ts (1)
  • marketplaceQueryKeys (160-179)
apps/meteor/client/views/marketplace/AppsPage/PrivateEmptyState.spec.tsx (2)
packages/mock-providers/src/index.ts (1)
  • mockAppRoot (3-3)
apps/meteor/client/contexts/AppsContext.tsx (1)
  • AppsContext (31-33)
apps/meteor/client/components/RoomIcon/OmnichannelRoomIcon/OmnichannelAppSourceRoomIcon.tsx (1)
apps/meteor/client/components/RoomIcon/OmnichannelRoomIcon/context/OmnichannelRoomIconContext.tsx (1)
  • useOmnichannelRoomIcon (11-15)
apps/meteor/client/views/marketplace/hooks/useLogs.ts (1)
apps/meteor/client/lib/queryKeys.ts (1)
  • marketplaceQueryKeys (160-179)
apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts (3)
packages/core-typings/src/IMessage/IMessage.ts (1)
  • IDiscussionMessage (329-333)
apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts (1)
  • useInfiniteMessageQueryUpdates (8-108)
apps/meteor/client/lib/queryKeys.ts (1)
  • roomsQueryKeys (13-33)
apps/meteor/client/views/omnichannel/hooks/useCannedResponseList.ts (1)
apps/meteor/client/lib/queryKeys.ts (1)
  • cannedResponsesQueryKeys (40-43)
apps/meteor/client/views/room/contextualBar/Discussions/DiscussionsListContextBar.tsx (1)
apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts (1)
  • useDiscussionsList (19-74)
apps/meteor/client/views/room/ImageGallery/ImageGalleryData.tsx (1)
apps/meteor/client/views/room/ImageGallery/hooks/useImagesList.ts (1)
  • useImagesList (9-62)
apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts (1)
packages/mock-providers/src/MockedAppRootBuilder.tsx (1)
  • queryClient (270-280)
apps/meteor/client/views/marketplace/AppsPage/UnsupportedEmptyState.spec.tsx (1)
apps/meteor/client/contexts/AppsContext.tsx (1)
  • AppsContext (31-33)
apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessagesList.tsx (1)
apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts (1)
  • useHistoryMessageList (21-57)
apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFilesWithData.tsx (1)
apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useFilesList.ts (1)
  • useFilesList (10-79)
apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts (2)
apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts (1)
  • useInfiniteMessageQueryUpdates (8-108)
apps/meteor/client/lib/queryKeys.ts (1)
  • omnichannelQueryKeys (50-96)
apps/meteor/client/views/marketplace/hooks/useAppInfo.ts (1)
apps/meteor/client/views/marketplace/hooks/useApps.ts (1)
  • useApps (76-180)
apps/meteor/client/providers/AppsProvider.tsx (1)
apps/meteor/client/contexts/AppsContext.tsx (1)
  • AppsContext (31-33)
apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts (1)
apps/meteor/client/views/marketplace/hooks/useApps.ts (1)
  • useApps (76-180)
apps/meteor/client/views/teams/contextualBar/channels/TeamsChannelsWithData.tsx (1)
apps/meteor/client/views/teams/contextualBar/channels/hooks/useTeamsChannelList.ts (1)
  • useTeamsChannelList (15-57)
apps/meteor/tests/mocks/client/marketplace.tsx (1)
apps/meteor/client/contexts/AppsContext.tsx (1)
  • AppsContext (31-33)
apps/meteor/client/views/omnichannel/departments/EditDepartment.tsx (2)
packages/ui-contexts/src/index.ts (1)
  • useTranslation (81-81)
apps/meteor/client/hooks/useRoomsList.ts (1)
  • useRoomsList (6-41)
apps/meteor/client/views/teams/contextualBar/channels/TeamsChannels.tsx (1)
packages/livechat/src/store/index.tsx (1)
  • Dispatch (209-209)
apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts (6)
packages/core-typings/src/ISubscription.ts (1)
  • ISubscription (11-81)
packages/core-typings/src/IMessage/IMessage.ts (1)
  • IThreadMainMessage (316-320)
apps/meteor/client/meteor/minimongo/Cursor.ts (1)
  • count (277-283)
packages/ui-contexts/src/hooks/useUserId.ts (1)
  • useUserId (5-5)
apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts (1)
  • useInfiniteMessageQueryUpdates (8-108)
apps/meteor/client/lib/queryKeys.ts (1)
  • roomsQueryKeys (13-33)
apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/useVideoConfList.ts (1)
apps/meteor/client/lib/queryKeys.ts (1)
  • videoConferenceQueryKeys (181-184)
apps/meteor/client/views/teams/contextualBar/channels/hooks/useTeamsChannelList.ts (1)
apps/meteor/client/lib/queryKeys.ts (1)
  • teamsQueryKeys (120-129)
apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx (3)
packages/ui-contexts/src/index.ts (1)
  • useTranslation (81-81)
apps/meteor/client/views/marketplace/hooks/useAppsReload.ts (1)
  • useAppsReload (6-11)
apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts (1)
  • useFilteredApps (15-130)
apps/meteor/client/components/message/content/location/MapView.tsx (1)
apps/meteor/client/components/message/content/location/hooks/useAsyncImage.ts (1)
  • useAsyncImage (3-25)
apps/meteor/client/views/room/ImageGallery/hooks/useImagesList.ts (1)
apps/meteor/client/lib/queryKeys.ts (1)
  • roomsQueryKeys (13-33)
apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/WrapCannedResponseList.tsx (1)
apps/meteor/client/views/omnichannel/hooks/useCannedResponseList.ts (1)
  • useCannedResponseList (7-57)
apps/meteor/client/hooks/useRoomsList.ts (1)
apps/meteor/client/lib/queryKeys.ts (1)
  • roomsQueryKeys (13-33)
🪛 Biome (2.1.2)
apps/meteor/client/providers/AppsProvider.tsx

[error] 11-11: Avoid passing children using a prop

The canonical way to pass children in React is to use JSX elements

(lint/correctness/noChildrenProp)

⏰ 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). (10)
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: cubic · AI code reviewer

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

♻️ Duplicate comments (21)
apps/meteor/client/components/message/content/location/hooks/useAsyncImage.ts (1)

17-18: Fix error handling - e.error is undefined.

The error event on Image elements provides a simple Event object, not an ErrorEvent. The Event object doesn't have an error property, so e.error will be undefined.

🔎 Proposed fix
 image.addEventListener('error', (e) => {
-	reject(e.error);
+	reject(new Error('Failed to load image'));
 });
apps/meteor/client/components/RoomIcon/OmnichannelRoomIcon/provider/OmnichannelRoomIconProvider.tsx (1)

50-59: Critical: Caching broken for undefined values.

The if (!snapshot) check at line 53 cannot distinguish between a missing cache entry and a cached undefined value. When extractSnapshot returns undefined (icon not yet loaded), every call to getSnapshot will:

  1. Retrieve undefined from the map
  2. Evaluate !snapshot as true
  3. Call extractSnapshot again → triggers OmnichannelRoomIconManager.get() → potentially fires another API request
  4. Re-cache undefined

This defeats caching and causes repeated API calls until the icon loads.

Recommended fix using Map.has()
 						() => {
-							let snapshot = snapshots.get(`${app}-${iconName}`);
-
-							if (!snapshot) {
-								snapshot = extractSnapshot(app, iconName);
-								snapshots.set(`${app}-${iconName}`, snapshot);
+							const key = `${app}-${iconName}`;
+							if (!snapshots.has(key)) {
+								snapshots.set(key, extractSnapshot(app, iconName));
 							}
-
-							return snapshot;
+							return snapshots.get(key);
 						},
apps/meteor/client/views/mailer/MailerUnsubscriptionPage.tsx (3)

25-31: Consider preventing duplicate mutation calls on remount.

The mutation will re-trigger if the component remounts with the same route parameters. While unsubscription APIs are typically idempotent, you can prevent unnecessary calls by tracking whether the mutation has already been attempted using a ref.

🔎 Proposed refactor
+	const hasMutatedRef = useRef(false);
+
 	useEffect(() => {
-		if (!_id || !createdAt) {
+		if (!_id || !createdAt || hasMutatedRef.current) {
 			return;
 		}
-
+		hasMutatedRef.current = true;
 		mutate({ _id, createdAt });
 	}, [_id, createdAt, mutate]);

43-45: Consider handling the initial idle state to prevent blank UI.

When the component first renders, the mutation is in isIdle state before the useEffect triggers it. During this brief moment, all state flags are false, causing the condition chain to evaluate to false and render nothing. While the flicker is minimal in React 18, you can eliminate it by checking for the idle state explicitly.

🔎 Proposed refactor using isIdle
-	const { isPending, isError, error, isSuccess } = useMailerUnsubscriptionMutation();
+	const { isPending, isError, error, isSuccess, isIdle } = useMailerUnsubscriptionMutation();

 	return (
 		<HeroLayout>
 			<Box color='default' marginInline='auto' marginBlock={16} maxWidth={800}>
-				{(isPending && <Throbber disabled />) ||
+				{((isPending || isIdle) && <Throbber disabled />) ||
 					(isError && <Callout type='danger' title={error.message} />) ||
 					(isSuccess && <Callout type='success' title={t('You_have_successfully_unsubscribed')} />)}
 			</Box>

Alternatively, use a more concise pattern:

-				{(isPending && <Throbber disabled />) ||
+				{(!isError && !isSuccess && <Throbber disabled />) ||
 					(isError && <Callout type='danger' title={error.message} />) ||
 					(isSuccess && <Callout type='success' title={t('You_have_successfully_unsubscribed')} />)}

18-20: Fix error handling to pass error message string.

The dispatchToastMessage expects a string for the message field, but the entire error object is being passed. Update the type annotation to Error (matching the mutation's generic type) and pass error.message.

🔎 Proposed fix
-		onError: (error: unknown) => {
-			dispatchToastMessage({ type: 'error', message: error });
+		onError: (error: Error) => {
+			dispatchToastMessage({ type: 'error', message: error.message });
 		},
apps/meteor/client/providers/AppsProvider.tsx (1)

11-11: Define context value outside component to prevent unnecessary re-renders.

The inline object creation { orchestrator: AppClientOrchestratorInstance } creates a new reference on every render, causing all AppsContext consumers to re-render even though the value never changes. Since AppClientOrchestratorInstance is a static singleton, define the value object once outside the component.

🔎 Proposed fix
+const contextValue = { orchestrator: AppClientOrchestratorInstance };
+
 const AppsProvider = ({ children }: AppsProviderProps) => {
-	return <AppsContext.Provider children={children} value={{ orchestrator: AppClientOrchestratorInstance }} />;
+	return <AppsContext.Provider value={contextValue}>{children}</AppsContext.Provider>;
 };
apps/meteor/client/views/omnichannel/contactInfo/tabs/ContactInfoHistory/ContactInfoHistoryMessages.tsx (1)

97-97: Critical: undefined variable history will cause ReferenceError.

Line 97 references history.length but the variable history is not defined in this component. Based on line 50, this should reference messages instead.

🔎 Proposed fix
-				{!error && totalItemCount > 0 && history.length > 0 && (
+				{!error && totalItemCount > 0 && messages.length > 0 && (
apps/meteor/client/views/marketplace/hooks/usePrivateAppsEnabled.ts (1)

3-3: Loading state causes premature false return, potentially causing UI flicker.

During the initial load, data is undefined, causing the expression to evaluate to false (since 0 !== 0). If the actual license value is non-zero, the UI will briefly show the disabled state before switching to enabled once data loads.

Consider returning the loading state alongside the boolean value, or return undefined during loading so consumers can handle the pending state appropriately:

export const usePrivateAppsEnabled = () => {
  const { data, isPending } = useLicense({ loadValues: true });
  return {
    enabled: (data?.limits?.privateApps?.max ?? 0) !== 0,
    isPending,
  };
};

Or return undefined during loading:

export const usePrivateAppsEnabled = () => {
  const { data, isPending } = useLicense({ loadValues: true });
  if (isPending) return undefined;
  return (data?.limits?.privateApps?.max ?? 0) !== 0;
};

Based on the past review comment, this issue was previously identified.

apps/meteor/client/views/omnichannel/hooks/useCannedResponseList.ts (2)

28-28: Departments fetched on every page is inefficient.

The getDepartments call executes for every page fetched within the queryFn, causing unnecessary network requests. This should be extracted to a separate query with appropriate caching.

Consider using a dedicated useQuery for departments:

const { data: departments } = useQuery({
  queryKey: ['departments'],
  queryFn: () => getDepartments({ text: '' }),
  staleTime: 5 * 60 * 1000, // Cache for 5 minutes
});

Then reference departments within the infinite query's select function or pass as an enabled dependency.

Based on the past review comment, this issue was previously identified.


40-40: Unsafe non-null assertion when department may not exist.

The non-null assertion departmentName! is unsafe because departmentName can be undefined if:

  • The department was deleted
  • Permission restrictions prevent access
  • The department ID is invalid

This could cause downstream issues in components expecting a defined value. Remove the assertion and update the return type:

-items: cannedResponses.map((cannedResponse): IOmnichannelCannedResponse & { departmentName: ILivechatDepartment['name'] } => {
+items: cannedResponses.map((cannedResponse): IOmnichannelCannedResponse & { departmentName: ILivechatDepartment['name'] | undefined } => {
-  departmentName: departmentName!,
+  departmentName,

Update consumers to handle the undefined case with conditional rendering or fallback values.

Based on the past review comment, this issue was previously identified.

apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useFilesList.ts (1)

25-26: Consider adding enabled: !!room to prevent incorrect endpoint usage.

If useUserRoom(rid) returns undefined initially, the query runs with the channels.files endpoint (due to fallback on line 21). For DMs (d) or private groups (p), this would fetch from the wrong API endpoint until the room data loads.

🔎 Proposed fix
 	return useInfiniteQuery({
+		enabled: !!room,
 		queryKey: roomsQueryKeys.files(rid, { type, text }),
apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessagesList.tsx (1)

117-117: history is undefined — will cause runtime ReferenceError.

The variable history is not defined in this component. Based on line 65, it should be messages.

🔎 Proposed fix
-				{!error && totalItemCount > 0 && history.length > 0 && (
+				{!error && totalItemCount > 0 && messages.length > 0 && (
apps/meteor/client/views/teams/contextualBar/channels/hooks/useTeamsChannelList.ts (1)

39-39: Bug: Spreading mapped array corrupts room object structure.

Spreading usersWaitingForE2EKeys?.map(...) directly into the object produces numeric keys ({0: {...}, 1: {...}}) instead of preserving usersWaitingForE2EKeys as an array property.

🔎 Proposed fix
-						...usersWaitingForE2EKeys?.map(({ userId, ts }) => ({ userId, ts: new Date(ts) })),
+						...(usersWaitingForE2EKeys && {
+							usersWaitingForE2EKeys: usersWaitingForE2EKeys.map(({ userId, ts }) => ({ userId, ts: new Date(ts) })),
+						}),
apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts (1)

61-76: Sorting functions mutate React Query cached data.

The sorting methods use apps.sort() which mutates the array in-place. When combined with fallback returning the same array reference, this can corrupt the query cache. React Query expects cached data to remain immutable.

🔎 Proposed fix: Create copies before sorting
 const sortingMethods: Record<string, (apps: App[]) => App[]> = {
 	urf: (apps: App[]) =>
-		apps.sort(
+		[...apps].sort(
 			(firstApp, secondApp) => (secondApp?.appRequestStats?.totalUnseen || 0) - (firstApp?.appRequestStats?.totalUnseen || 0),
 		),
 	url: (apps: App[]) =>
-		apps.sort(
+		[...apps].sort(
 			(firstApp, secondApp) => (firstApp?.appRequestStats?.totalUnseen || 0) - (secondApp?.appRequestStats?.totalUnseen || 0),
 		),
-	az: (apps: App[]) => apps.sort((firstApp, secondApp) => sortAppsByAlphabeticalOrInverseOrder(firstApp.name, secondApp.name)),
-	za: (apps: App[]) => apps.sort((firstApp, secondApp) => sortAppsByAlphabeticalOrInverseOrder(secondApp.name, firstApp.name)),
+	az: (apps: App[]) => [...apps].sort((firstApp, secondApp) => sortAppsByAlphabeticalOrInverseOrder(firstApp.name, secondApp.name)),
+	za: (apps: App[]) => [...apps].sort((firstApp, secondApp) => sortAppsByAlphabeticalOrInverseOrder(secondApp.name, firstApp.name)),
 	mru: (apps: App[]) =>
-		apps.sort((firstApp, secondApp) => sortAppsByClosestOrFarthestModificationDate(firstApp.modifiedAt, secondApp.modifiedAt)),
+		[...apps].sort((firstApp, secondApp) => sortAppsByClosestOrFarthestModificationDate(firstApp.modifiedAt, secondApp.modifiedAt)),
 	lru: (apps: App[]) =>
-		apps.sort((firstApp, secondApp) => sortAppsByClosestOrFarthestModificationDate(secondApp.modifiedAt, firstApp.modifiedAt)),
+		[...apps].sort((firstApp, secondApp) => sortAppsByClosestOrFarthestModificationDate(secondApp.modifiedAt, firstApp.modifiedAt)),
 };
apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts (1)

43-52: itemCount becomes stale after real-time mutations.

When messages are added or deleted via real-time updates, itemCount (line 45) preserves the original server count instead of reflecting the actual mutated array length. This can cause pagination metadata inconsistencies and affect Virtuoso's totalCount in consuming components.

🔎 Suggested improvement

Consider updating itemCount to reflect the actual items length after mutation:

-		const itemCount = lastPage.itemCount || items.length;
+		const itemCount = items.length;

However, this may have trade-offs with server-side pagination expectations. An alternative is to keep itemCount as the server's total but document this limitation.

apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts (1)

108-113: Pagination offset calculated from filtered items may cause inconsistencies.

The getNextPageParam calculates loadedItemsCount from the filtered page.items.length. If client-side filtering (lines 101-104) removes items that the server returned, the offset sent to the next API call may not align with server expectations, potentially causing duplicates or gaps.

The FIXME comment acknowledges this. Consider tracking the raw API response count separately for offset calculation, or ensure server-side filtering is comprehensive enough that client-side filtering is redundant.

apps/meteor/client/views/marketplace/hooks/useApps.ts (4)

13-13: sortByName mutates the input array.

Array.sort() mutates in-place, which can corrupt React Query's cached data. Create a copy before sorting.

🔎 Proposed fix
-const sortByName = (apps: App[]): App[] => apps.sort((a, b) => (a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1));
+const sortByName = (apps: App[]): App[] => [...apps].sort((a, b) => (a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1));

127-129: Unintended license invalidation during loading state.

When isEnterprise is undefined (during loading), !isEnterprise evaluates to true, triggering license invalidation before enterprise status is determined.

🔎 Proposed fix
-			if (['app/added', 'app/removed'].includes(key) && !isEnterprise) {
+			if (['app/added', 'app/removed'].includes(key) && isEnterprise === false) {

164-166: Missing error handling for instance query.

Marketplace errors are propagated but instance query errors are silently ignored. If the instance query fails, users will see no installed apps without error indication.

🔎 Proposed fix
 		queryFn: async () => {
 			if (marketplace.isError) {
 				throw marketplace.error;
 			}
+			if (instance.isError) {
+				throw instance.error;
+			}

33-33: Mutating cached marketplace data.

sortByName(marketplace.data || []) will mutate the cached array when marketplace.data exists. This corrupts React Query's cache and can cause subtle bugs.

🔎 Proposed fix
-	sortByName(marketplace.data || []).forEach((app) => {
+	sortByName([...(marketplace.data || [])]).forEach((app) => {
apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx (1)

135-135: Missing isSuccess check for consistency.

All similar state variables (noInstalledApps, noMarketplaceOrInstalledAppMatches, noInstalledAppMatches) check isSuccess before accessing data, but noAppRequests does not. This is inconsistent and could access undefined data during loading.

🔎 Proposed fix
-	const noAppRequests = context === 'requested' && data?.count === 0;
+	const noAppRequests = isSuccess && context === 'requested' && data.count === 0;
🧹 Nitpick comments (5)
apps/meteor/client/components/message/content/location/hooks/useAsyncImage.ts (1)

8-11: Remove redundant guard clause.

The if (!src) check in the queryFn is redundant because enabled: Boolean(src) on line 22 already prevents the query from running when src is falsy. React Query will not invoke queryFn when enabled is false.

🔎 Proposed simplification
 queryFn: () =>
 	new Promise<string>((resolve, reject) => {
-		if (!src) {
-			reject(new Error('No src provided'));
-			return;
-		}
-
 		const image = new Image();
 		image.addEventListener('load', () => {
 			resolve(image.src);
apps/meteor/client/components/RoomIcon/OmnichannelRoomIcon/provider/OmnichannelRoomIconProvider.tsx (1)

49-49: Remove outdated comment.

This comment is now misleading after the type change—caching actually breaks for undefined values (see below). Per coding guidelines, avoid implementation comments.

Proposed fix
-						// No problem here, because it's return value is a cached in the snapshots map on subsequent calls
 						() => {
apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx (1)

86-91: Missing dependency in useEffect.

The useEffect calls createFilterStructureItems() but only depends on isRequested. Since createFilterStructureItems is defined inside the component and references isRequested, this works but could break if the function is refactored. Consider adding t to dependencies or extracting the logic.

apps/meteor/client/lib/queryKeys.ts (1)

165-165: Consider stronger typing for appsStored query key.

Using ...args: unknown[] accepts any arguments without type safety. Since this key is used in useApps.ts with specific data shapes, consider narrowing the type or documenting the expected usage.

🔎 Example with explicit optional parameters
-	appsStored: (...args: unknown[]) => [...marketplaceQueryKeys.all, 'apps-stored', ...args] as const,
+	appsStored: (instanceData?: App[], marketplaceData?: App[]) => [...marketplaceQueryKeys.all, 'apps-stored', instanceData, marketplaceData] as const,

Note: This would require importing the App type or using a more general array type.

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

30-46: E2E decryption runs on every fetch.

The decryption logic runs inside queryFn, meaning it executes on every page fetch. If decryption is expensive, consider caching decrypted results or moving decryption to the select function to avoid re-decrypting on refetch. This is likely acceptable given the small page size (5 items).

📜 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 032f070 and f8e2128.

📒 Files selected for processing (72)
  • apps/meteor/client/components/RoomIcon/OmnichannelRoomIcon/OmnichannelAppSourceRoomIcon.tsx
  • apps/meteor/client/components/RoomIcon/OmnichannelRoomIcon/context/OmnichannelRoomIconContext.tsx
  • apps/meteor/client/components/RoomIcon/OmnichannelRoomIcon/provider/OmnichannelRoomIconProvider.tsx
  • apps/meteor/client/components/message/content/location/MapView.tsx
  • apps/meteor/client/components/message/content/location/hooks/useAsyncImage.ts
  • apps/meteor/client/contexts/AppsContext.tsx
  • apps/meteor/client/contexts/hooks/useAppsReload.ts
  • apps/meteor/client/contexts/hooks/useAppsResult.ts
  • apps/meteor/client/hooks/lists/useRecordList.ts
  • apps/meteor/client/hooks/lists/useScrollableMessageList.ts
  • apps/meteor/client/hooks/lists/useScrollableRecordList.ts
  • apps/meteor/client/hooks/lists/useStreamUpdatesForMessageList.ts
  • apps/meteor/client/hooks/useAsyncState.ts
  • apps/meteor/client/hooks/useComponentDidUpdate.ts
  • apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts
  • apps/meteor/client/hooks/useRoomsList.ts
  • apps/meteor/client/lib/asyncState/AsyncState.ts
  • apps/meteor/client/lib/asyncState/AsyncStatePhase.ts
  • apps/meteor/client/lib/asyncState/functions.ts
  • apps/meteor/client/lib/asyncState/index.ts
  • apps/meteor/client/lib/lists/CannedResponseList.ts
  • apps/meteor/client/lib/lists/DiscussionsList.ts
  • apps/meteor/client/lib/lists/FilesList.ts
  • apps/meteor/client/lib/lists/ImagesList.ts
  • apps/meteor/client/lib/lists/MessageList.ts
  • apps/meteor/client/lib/lists/RecordList.spec.ts
  • apps/meteor/client/lib/lists/RecordList.ts
  • apps/meteor/client/lib/lists/ThreadsList.ts
  • apps/meteor/client/lib/queryKeys.ts
  • apps/meteor/client/providers/AppsProvider.tsx
  • apps/meteor/client/providers/AppsProvider/AppsProvider.tsx
  • apps/meteor/client/providers/AppsProvider/index.ts
  • apps/meteor/client/providers/AppsProvider/storeQueryFunction.ts
  • apps/meteor/client/views/mailer/MailerUnsubscriptionPage.tsx
  • apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppRequests/AppRequests.tsx
  • apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx
  • apps/meteor/client/views/marketplace/AppsPage/PrivateEmptyState.spec.tsx
  • apps/meteor/client/views/marketplace/AppsPage/UnsupportedEmptyState.spec.tsx
  • apps/meteor/client/views/marketplace/hooks/useAppInfo.ts
  • apps/meteor/client/views/marketplace/hooks/useAppInstances.ts
  • apps/meteor/client/views/marketplace/hooks/useApps.spec.ts
  • apps/meteor/client/views/marketplace/hooks/useApps.ts
  • apps/meteor/client/views/marketplace/hooks/useAppsReload.ts
  • apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts
  • apps/meteor/client/views/marketplace/hooks/useInstallApp.tsx
  • apps/meteor/client/views/marketplace/hooks/useLogs.ts
  • apps/meteor/client/views/marketplace/hooks/usePrivateAppsEnabled.ts
  • apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/CannedResponseList.tsx
  • apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/WrapCannedResponseList.tsx
  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessagesList.tsx
  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts
  • apps/meteor/client/views/omnichannel/contactInfo/tabs/ContactInfoHistory/ContactInfoHistoryMessages.tsx
  • apps/meteor/client/views/omnichannel/departments/EditDepartment.tsx
  • apps/meteor/client/views/omnichannel/hooks/useCannedResponseList.ts
  • apps/meteor/client/views/room/ImageGallery/ImageGalleryData.tsx
  • apps/meteor/client/views/room/ImageGallery/hooks/useImagesList.ts
  • apps/meteor/client/views/room/contextualBar/Discussions/DiscussionsListContextBar.tsx
  • apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts
  • apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx
  • apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFilesWithData.tsx
  • apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useFilesList.ts
  • apps/meteor/client/views/room/contextualBar/Threads/ThreadList.tsx
  • apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts
  • apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/VideoConfList.tsx
  • apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/VideoConfListWithData.tsx
  • apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/VideoConfRecordList.ts
  • apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/useVideoConfList.ts
  • apps/meteor/client/views/teams/contextualBar/channels/TeamsChannels.tsx
  • apps/meteor/client/views/teams/contextualBar/channels/TeamsChannelsWithData.tsx
  • apps/meteor/client/views/teams/contextualBar/channels/hooks/useTeamsChannelList.ts
  • apps/meteor/ee/server/apps/marketplace/fetchMarketplaceApps.ts
  • apps/meteor/tests/mocks/client/marketplace.tsx
💤 Files with no reviewable changes (25)
  • apps/meteor/client/contexts/hooks/useAppsReload.ts
  • apps/meteor/client/hooks/lists/useStreamUpdatesForMessageList.ts
  • apps/meteor/client/hooks/lists/useRecordList.ts
  • apps/meteor/client/lib/asyncState/index.ts
  • apps/meteor/client/providers/AppsProvider/index.ts
  • apps/meteor/client/hooks/lists/useScrollableRecordList.ts
  • apps/meteor/client/lib/lists/CannedResponseList.ts
  • apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/VideoConfRecordList.ts
  • apps/meteor/client/lib/lists/DiscussionsList.ts
  • apps/meteor/client/lib/asyncState/AsyncState.ts
  • apps/meteor/client/lib/lists/FilesList.ts
  • apps/meteor/client/hooks/useAsyncState.ts
  • apps/meteor/client/lib/lists/ImagesList.ts
  • apps/meteor/client/lib/lists/ThreadsList.ts
  • apps/meteor/client/lib/lists/MessageList.ts
  • apps/meteor/client/hooks/useComponentDidUpdate.ts
  • apps/meteor/client/lib/asyncState/functions.ts
  • apps/meteor/client/contexts/hooks/useAppsResult.ts
  • apps/meteor/client/contexts/AppsContext.tsx
  • apps/meteor/client/lib/lists/RecordList.spec.ts
  • apps/meteor/client/hooks/lists/useScrollableMessageList.ts
  • apps/meteor/client/lib/lists/RecordList.ts
  • apps/meteor/client/lib/asyncState/AsyncStatePhase.ts
  • apps/meteor/client/providers/AppsProvider/AppsProvider.tsx
  • apps/meteor/client/providers/AppsProvider/storeQueryFunction.ts
🚧 Files skipped from review as they are similar to previous changes (15)
  • apps/meteor/client/views/marketplace/hooks/useAppsReload.ts
  • apps/meteor/client/views/marketplace/hooks/useAppInfo.ts
  • apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx
  • apps/meteor/ee/server/apps/marketplace/fetchMarketplaceApps.ts
  • apps/meteor/client/views/marketplace/hooks/useLogs.ts
  • apps/meteor/client/views/marketplace/hooks/useApps.spec.ts
  • apps/meteor/client/components/RoomIcon/OmnichannelRoomIcon/OmnichannelAppSourceRoomIcon.tsx
  • apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/WrapCannedResponseList.tsx
  • apps/meteor/tests/mocks/client/marketplace.tsx
  • apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppRequests/AppRequests.tsx
  • apps/meteor/client/views/marketplace/AppsPage/UnsupportedEmptyState.spec.tsx
  • apps/meteor/client/views/teams/contextualBar/channels/TeamsChannelsWithData.tsx
  • apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/VideoConfListWithData.tsx
  • apps/meteor/client/views/marketplace/hooks/useInstallApp.tsx
  • apps/meteor/client/components/message/content/location/MapView.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/contextualBar/Threads/ThreadList.tsx
  • apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/VideoConfList.tsx
  • apps/meteor/client/views/room/contextualBar/Discussions/DiscussionsListContextBar.tsx
  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessagesList.tsx
  • apps/meteor/client/views/marketplace/hooks/usePrivateAppsEnabled.ts
  • apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts
  • apps/meteor/client/components/message/content/location/hooks/useAsyncImage.ts
  • apps/meteor/client/views/mailer/MailerUnsubscriptionPage.tsx
  • apps/meteor/client/views/teams/contextualBar/channels/TeamsChannels.tsx
  • apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts
  • apps/meteor/client/hooks/useRoomsList.ts
  • apps/meteor/client/views/omnichannel/hooks/useCannedResponseList.ts
  • apps/meteor/client/views/marketplace/hooks/useApps.ts
  • apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFilesWithData.tsx
  • apps/meteor/client/views/omnichannel/contactInfo/tabs/ContactInfoHistory/ContactInfoHistoryMessages.tsx
  • apps/meteor/client/views/teams/contextualBar/channels/hooks/useTeamsChannelList.ts
  • apps/meteor/client/components/RoomIcon/OmnichannelRoomIcon/context/OmnichannelRoomIconContext.tsx
  • apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx
  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts
  • apps/meteor/client/views/marketplace/AppsPage/PrivateEmptyState.spec.tsx
  • apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/useVideoConfList.ts
  • apps/meteor/client/views/omnichannel/departments/EditDepartment.tsx
  • apps/meteor/client/components/RoomIcon/OmnichannelRoomIcon/provider/OmnichannelRoomIconProvider.tsx
  • apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts
  • apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/CannedResponseList.tsx
  • apps/meteor/client/lib/queryKeys.ts
  • apps/meteor/client/views/room/ImageGallery/hooks/useImagesList.ts
  • apps/meteor/client/views/room/ImageGallery/ImageGalleryData.tsx
  • apps/meteor/client/views/marketplace/hooks/useAppInstances.ts
  • apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useFilesList.ts
  • apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts
  • apps/meteor/client/providers/AppsProvider.tsx
🧠 Learnings (20)
📚 Learning: 2025-12-18T15:18:31.688Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 37773
File: apps/meteor/client/views/mediaCallHistory/MediaCallHistoryInternal.tsx:24-34
Timestamp: 2025-12-18T15:18:31.688Z
Learning: In apps/meteor/client/views/mediaCallHistory/MediaCallHistoryInternal.tsx, for internal call history items, the item.contactId is guaranteed to always match either the caller.id or callee.id in the call data, so the contact resolution in getContact will never result in undefined.

Applied to files:

  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessagesList.tsx
  • apps/meteor/client/views/omnichannel/contactInfo/tabs/ContactInfoHistory/ContactInfoHistoryMessages.tsx
  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts
  • apps/meteor/client/lib/queryKeys.ts
📚 Learning: 2025-11-19T18:20:37.116Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 37419
File: apps/meteor/server/services/media-call/service.ts:141-141
Timestamp: 2025-11-19T18:20:37.116Z
Learning: In apps/meteor/server/services/media-call/service.ts, the sendHistoryMessage method should use call.caller.id or call.createdBy?.id as the message author, not call.transferredBy?.id. Even for transferred calls, the message should appear in the DM between the two users who are calling each other, not sent by the person who transferred the call.

Applied to files:

  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessagesList.tsx
  • apps/meteor/client/views/omnichannel/contactInfo/tabs/ContactInfoHistory/ContactInfoHistoryMessages.tsx
  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts
📚 Learning: 2025-12-02T22:23:49.593Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 37654
File: apps/meteor/client/hooks/useAppSlashCommands.ts:32-38
Timestamp: 2025-12-02T22:23:49.593Z
Learning: In apps/meteor/client/hooks/useAppSlashCommands.ts, the `data?.forEach((command) => slashCommands.add(command))` call during render is intentional. The query is configured with `structuralSharing: false` to prevent React Query from keeping stable data references, and `slashCommands.add` is idempotent, so executing on every render is acceptable and ensures the command registry stays current.

Applied to files:

  • apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts
  • apps/meteor/client/views/mailer/MailerUnsubscriptionPage.tsx
  • apps/meteor/client/hooks/useRoomsList.ts
  • apps/meteor/client/views/omnichannel/hooks/useCannedResponseList.ts
  • apps/meteor/client/views/marketplace/hooks/useApps.ts
  • apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFilesWithData.tsx
  • apps/meteor/client/views/teams/contextualBar/channels/hooks/useTeamsChannelList.ts
  • apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx
  • apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts
  • apps/meteor/client/views/marketplace/hooks/useAppInstances.ts
📚 Learning: 2025-11-27T17:56:26.050Z
Learnt from: MartinSchoeler
Repo: RocketChat/Rocket.Chat PR: 37557
File: apps/meteor/client/views/admin/ABAC/AdminABACRooms.tsx:115-116
Timestamp: 2025-11-27T17:56:26.050Z
Learning: In Rocket.Chat, the GET /v1/abac/rooms endpoint (implemented in ee/packages/abac/src/index.ts) only returns rooms where abacAttributes exists and is not an empty array (query: { abacAttributes: { $exists: true, $ne: [] } }). Therefore, in components consuming this endpoint (like AdminABACRooms.tsx), room.abacAttributes is guaranteed to be defined for all returned rooms, and optional chaining before calling array methods like .join() is sufficient without additional null coalescing.

Applied to files:

  • apps/meteor/client/hooks/useRoomsList.ts
  • apps/meteor/client/views/teams/contextualBar/channels/hooks/useTeamsChannelList.ts
  • apps/meteor/client/views/omnichannel/departments/EditDepartment.tsx
  • apps/meteor/client/lib/queryKeys.ts
  • apps/meteor/client/views/room/ImageGallery/hooks/useImagesList.ts
  • apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useFilesList.ts
📚 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/marketplace/hooks/useApps.ts
📚 Learning: 2025-11-07T14:50:33.544Z
Learnt from: KevLehman
Repo: RocketChat/Rocket.Chat PR: 37423
File: packages/i18n/src/locales/en.i18n.json:18-18
Timestamp: 2025-11-07T14:50:33.544Z
Learning: Rocket.Chat settings: in apps/meteor/ee/server/settings/abac.ts, the Abac_Cache_Decision_Time_Seconds setting uses invalidValue: 0 as the fallback when ABAC is unlicensed. With a valid license, admins can still set the value to 0 to intentionally disable the ABAC decision cache.

Applied to files:

  • apps/meteor/client/views/marketplace/hooks/useApps.ts
📚 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/teams/contextualBar/channels/hooks/useTeamsChannelList.ts
  • apps/meteor/client/lib/queryKeys.ts
📚 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/teams/contextualBar/channels/hooks/useTeamsChannelList.ts
  • apps/meteor/client/lib/queryKeys.ts
📚 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/teams/contextualBar/channels/hooks/useTeamsChannelList.ts
  • apps/meteor/client/components/RoomIcon/OmnichannelRoomIcon/provider/OmnichannelRoomIconProvider.tsx
  • apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useFilesList.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/page-objects/**/*.ts : Utilize existing page objects pattern from `apps/meteor/tests/e2e/page-objects/`

Applied to files:

  • apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx
  • apps/meteor/client/views/marketplace/AppsPage/PrivateEmptyState.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 : Utilize Playwright fixtures (`test`, `page`, `expect`) for consistency in test files

Applied to files:

  • apps/meteor/client/views/marketplace/AppsPage/PrivateEmptyState.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/marketplace/AppsPage/PrivateEmptyState.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/marketplace/AppsPage/PrivateEmptyState.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/marketplace/AppsPage/PrivateEmptyState.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} : Store commonly used locators in variables/constants for reuse

Applied to files:

  • apps/meteor/client/views/marketplace/AppsPage/PrivateEmptyState.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/marketplace/AppsPage/PrivateEmptyState.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/marketplace/AppsPage/PrivateEmptyState.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/marketplace/AppsPage/PrivateEmptyState.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/marketplace/AppsPage/PrivateEmptyState.spec.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/lib/queryKeys.ts
🧬 Code graph analysis (19)
apps/meteor/client/views/room/contextualBar/Threads/ThreadList.tsx (2)
apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts (1)
  • useThreadsList (48-120)
packages/ui-client/src/components/Contextualbar/index.ts (1)
  • ContextualbarEmptyContent (34-34)
apps/meteor/client/views/room/contextualBar/Discussions/DiscussionsListContextBar.tsx (1)
apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts (1)
  • useDiscussionsList (19-76)
apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessagesList.tsx (2)
apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts (1)
  • useHistoryMessageList (21-59)
packages/ui-client/src/components/Contextualbar/index.ts (1)
  • ContextualbarEmptyContent (34-34)
apps/meteor/client/views/mailer/MailerUnsubscriptionPage.tsx (2)
packages/ddp-client/src/legacy/RocketchatSDKLegacy.ts (1)
  • unsubscribe (246-248)
packages/ui-contexts/src/index.ts (3)
  • useRouteParameter (65-65)
  • useToastMessageDispatch (78-78)
  • useTranslation (81-81)
apps/meteor/client/hooks/useRoomsList.ts (1)
apps/meteor/client/lib/queryKeys.ts (1)
  • roomsQueryKeys (13-34)
apps/meteor/client/views/omnichannel/hooks/useCannedResponseList.ts (2)
apps/meteor/client/meteor/minimongo/Cursor.ts (1)
  • count (277-283)
apps/meteor/client/lib/queryKeys.ts (1)
  • cannedResponsesQueryKeys (41-44)
apps/meteor/client/views/marketplace/hooks/useApps.ts (3)
apps/meteor/client/lib/queryKeys.ts (1)
  • marketplaceQueryKeys (161-180)
apps/meteor/client/contexts/AppsContext.tsx (1)
  • AppsContext (31-33)
packages/ui-contexts/src/index.ts (2)
  • usePermission (57-57)
  • useStream (77-77)
apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFilesWithData.tsx (1)
apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useFilesList.ts (1)
  • useFilesList (10-79)
apps/meteor/client/views/teams/contextualBar/channels/hooks/useTeamsChannelList.ts (1)
apps/meteor/client/lib/queryKeys.ts (1)
  • teamsQueryKeys (121-130)
apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx (2)
apps/meteor/client/views/marketplace/hooks/useAppsReload.ts (1)
  • useAppsReload (6-11)
apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts (1)
  • useFilteredApps (15-130)
apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts (2)
apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts (1)
  • useInfiniteMessageQueryUpdates (8-108)
apps/meteor/client/lib/queryKeys.ts (1)
  • omnichannelQueryKeys (51-97)
apps/meteor/client/views/marketplace/AppsPage/PrivateEmptyState.spec.tsx (2)
packages/mock-providers/src/index.ts (1)
  • mockAppRoot (3-3)
apps/meteor/client/contexts/AppsContext.tsx (1)
  • AppsContext (31-33)
apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/useVideoConfList.ts (1)
apps/meteor/client/lib/queryKeys.ts (1)
  • videoConferenceQueryKeys (182-185)
apps/meteor/client/views/omnichannel/departments/EditDepartment.tsx (1)
apps/meteor/client/hooks/useRoomsList.ts (1)
  • useRoomsList (6-41)
apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts (1)
apps/meteor/client/views/marketplace/hooks/useApps.ts (1)
  • useApps (76-180)
apps/meteor/client/views/room/ImageGallery/hooks/useImagesList.ts (2)
apps/meteor/client/meteor/minimongo/Cursor.ts (1)
  • count (277-283)
apps/meteor/client/lib/queryKeys.ts (1)
  • roomsQueryKeys (13-34)
apps/meteor/client/views/room/ImageGallery/ImageGalleryData.tsx (1)
apps/meteor/client/views/room/ImageGallery/hooks/useImagesList.ts (1)
  • useImagesList (9-62)
apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts (3)
packages/core-typings/src/IMessage/IMessage.ts (1)
  • IDiscussionMessage (329-333)
apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts (1)
  • useInfiniteMessageQueryUpdates (8-108)
apps/meteor/client/lib/queryKeys.ts (1)
  • roomsQueryKeys (13-34)
apps/meteor/client/providers/AppsProvider.tsx (1)
apps/meteor/client/contexts/AppsContext.tsx (1)
  • AppsContext (31-33)
🪛 Biome (2.1.2)
apps/meteor/client/providers/AppsProvider.tsx

[error] 11-11: Avoid passing children using a prop

The canonical way to pass children in React is to use JSX elements

(lint/correctness/noChildrenProp)

⏰ 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

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

Caution

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

⚠️ Outside diff range comments (2)
apps/meteor/client/views/omnichannel/hooks/useScopeDict.ts (1)

4-12: Potential empty label when scope is 'department' with undefined departmentName.

The function now returns string | undefined (implicit return type after removing the explicit : string annotation). When scope is 'department' and departmentName is undefined, the function returns undefined, which renders as an empty label in the UI.

Consider either:

  1. Providing a fallback label (e.g., t('Department')) when departmentName is undefined, or
  2. Explicitly documenting that an empty label is acceptable for this edge case.
🔎 Proposed fix with fallback label
-export const useScopeDict = (scope: IOmnichannelCannedResponse['scope'], departmentName: ILivechatDepartment['name'] | undefined) => {
+export const useScopeDict = (scope: IOmnichannelCannedResponse['scope'], departmentName: ILivechatDepartment['name'] | undefined): string => {
 	const { t } = useTranslation();
 
 	const dict: Record<string, string> = {
 		global: t('Public'),
 		user: t('Private'),
 	};
 
-	return dict[scope] || departmentName;
+	return dict[scope] || departmentName || t('Department');
 };
apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/CannedResponseList.tsx (1)

34-34: Replace any with proper typing for onClickItem.

The FIXME comment indicates this needs attention. The data parameter should be typed as the canned response item type rather than any.

🔎 Proposed fix
-	onClickItem: (data: any) => void; // FIXME: fix typings
+	onClickItem: (data: IOmnichannelCannedResponse & { departmentName?: ILivechatDepartment['name'] }) => void;
🧹 Nitpick comments (5)
apps/meteor/client/views/omnichannel/hooks/useCannedResponseList.ts (1)

28-29: Address TODO: Departments can be fetched once with a separate query.

The TODO comments correctly identify that fetching departments on every page is inefficient. You can extract departments into a separate useQuery with appropriate caching to avoid refetching.

🔎 Proposed refactor using a separate departments query

Extract department fetching into its own query and reference it in the infinite query:

 export const useCannedResponseList = ({ filter, type }: { filter: string; type: string }) => {
 	const getCannedResponses = useEndpoint('GET', '/v1/canned-responses');
 	const getDepartments = useEndpoint('GET', '/v1/livechat/department');
 
 	const count = 25;
+
+	const { data: departmentsData } = useQuery({
+		queryKey: ['departments'],
+		queryFn: () => getDepartments({ text: '' }),
+		staleTime: 5 * 60 * 1000, // Cache for 5 minutes
+	});
+
+	const departments = departmentsData?.departments ?? [];
 
 	return useInfiniteQuery({
 		queryKey: cannedResponsesQueryKeys.list({ filter, type }),
 		queryFn: async ({ pageParam: offset }) => {
 			const { cannedResponses, total } = await getCannedResponses({
 				...(filter && { text: filter }),
 				...(type && ['global', 'user'].find((option) => option === type) && { scope: type }),
 				...(type &&
 					!['global', 'user', 'all'].find((option) => option === type) && {
 						scope: 'department',
 						departmentId: type,
 					}),
 				offset,
 				count,
 			});
 
-			// TODO: Optimize by creating an endpoint that returns canned responses with department names
-			// TODO: Use another query for departments to avoid refetching
-			const { departments } = await getDepartments({ text: '' });
-
 			return {
 				items: cannedResponses.map((cannedResponse): IOmnichannelCannedResponse & { departmentName?: ILivechatDepartment['name'] } => {
 					const departmentName = cannedResponse.departmentId
 						? departments.find((department) => department._id === cannedResponse.departmentId)?.name
 						: undefined;
 
 					return {
 						...cannedResponse,
 						_updatedAt: new Date(cannedResponse._updatedAt),
 						_createdAt: new Date(cannedResponse._createdAt),
 						departmentName,
 					};
 				}),
 				itemCount: total,
 			};
 		},
+		enabled: departmentsData !== undefined,
 		initialPageParam: 0,

Would you like me to generate a complete implementation or open a new issue to track this optimization?

apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessagesList.tsx (1)

63-66: React Query migration implemented correctly.

The hook signature change and derived values are sound. The defaults ([] for messages, 0 for totalItemCount) safely handle loading states.

Optional: Consider destructuring hasNextPage and isFetchingNextPage from the hook result to guard the fetchNextPage() call on line 127:

const { isPending, error, isSuccess, data, fetchNextPage, hasNextPage, isFetchingNextPage } = useHistoryMessageList(query);

Then on line 127:

endReached={() => hasNextPage && !isFetchingNextPage && fetchNextPage()}

While fetchNextPage() is safe to call without guards (React Query handles it), this is a common best practice for clarity and avoiding unnecessary calls.

apps/meteor/client/views/omnichannel/contactInfo/tabs/ContactInfoHistory/ContactInfoHistoryMessages.tsx (3)

48-51: Verify pagination optimizations available from the hook.

The query destructuring is correct, but the hook returns additional properties from useInfiniteQuery that could improve the UX. Consider destructuring hasNextPage and isFetchingNextPage to provide more explicit pagination control and loading states.

const { isPending, error, isSuccess, data, fetchNextPage, hasNextPage, isFetchingNextPage } = useHistoryMessageList(query);

Then use these in the endReached callback (line 107) for more explicit control.


95-97: Adjust empty state condition to reflect client-filtered results.

The empty state (line 95) checks totalItemCount === 0, but should check messages.length === 0. Since the hook applies client-side filtering (per the relevant code snippet in useHistoryMessageList.ts), totalItemCount (server count) can be greater than zero while messages (client-filtered) is empty. This leaves a gap where neither the empty state nor the list is displayed.

🔎 Proposed fix
-				{isSuccess && totalItemCount === 0 && <ContextualbarEmptyContent title={t('No_results_found')} />}
+				{isSuccess && messages.length === 0 && <ContextualbarEmptyContent title={t('No_results_found')} />}

107-107: Consider guarding fetchNextPage() with hasNextPage check.

While fetchNextPage() internally handles cases where there's no next page or a fetch is already in progress, explicitly checking hasNextPage && !isFetchingNextPage would make the pagination logic clearer and avoid unnecessary function calls.

🔎 Proposed refinement

First, destructure the additional properties on line 48:

const { isPending, error, isSuccess, data, fetchNextPage, hasNextPage, isFetchingNextPage } = useHistoryMessageList(query);

Then update the endReached callback:

-								endReached={() => fetchNextPage()}
+								endReached={() => {
+									if (hasNextPage && !isFetchingNextPage) {
+										fetchNextPage();
+									}
+								}}
📜 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 b996e22 and f1a19e7.

📒 Files selected for processing (9)
  • apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/CannedResponse.tsx
  • apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/CannedResponseList.tsx
  • apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/Item.tsx
  • apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/WrapCannedResponse.tsx
  • apps/meteor/client/views/omnichannel/cannedResponses/modals/CreateCannedResponse/CreateCannedResponseModal.tsx
  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessagesList.tsx
  • apps/meteor/client/views/omnichannel/contactInfo/tabs/ContactInfoHistory/ContactInfoHistoryMessages.tsx
  • apps/meteor/client/views/omnichannel/hooks/useCannedResponseList.ts
  • apps/meteor/client/views/omnichannel/hooks/useScopeDict.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/omnichannel/cannedResponses/contextualBar/CannedResponse/Item.tsx
  • apps/meteor/client/views/omnichannel/hooks/useScopeDict.ts
  • apps/meteor/client/views/omnichannel/hooks/useCannedResponseList.ts
  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessagesList.tsx
  • apps/meteor/client/views/omnichannel/contactInfo/tabs/ContactInfoHistory/ContactInfoHistoryMessages.tsx
  • apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/WrapCannedResponse.tsx
  • apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/CannedResponseList.tsx
  • apps/meteor/client/views/omnichannel/cannedResponses/modals/CreateCannedResponse/CreateCannedResponseModal.tsx
  • apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/CannedResponse.tsx
🧠 Learnings (3)
📚 Learning: 2025-12-02T22:23:49.593Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 37654
File: apps/meteor/client/hooks/useAppSlashCommands.ts:32-38
Timestamp: 2025-12-02T22:23:49.593Z
Learning: In apps/meteor/client/hooks/useAppSlashCommands.ts, the `data?.forEach((command) => slashCommands.add(command))` call during render is intentional. The query is configured with `structuralSharing: false` to prevent React Query from keeping stable data references, and `slashCommands.add` is idempotent, so executing on every render is acceptable and ensures the command registry stays current.

Applied to files:

  • apps/meteor/client/views/omnichannel/hooks/useCannedResponseList.ts
📚 Learning: 2025-12-18T15:18:31.688Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 37773
File: apps/meteor/client/views/mediaCallHistory/MediaCallHistoryInternal.tsx:24-34
Timestamp: 2025-12-18T15:18:31.688Z
Learning: In apps/meteor/client/views/mediaCallHistory/MediaCallHistoryInternal.tsx, for internal call history items, the item.contactId is guaranteed to always match either the caller.id or callee.id in the call data, so the contact resolution in getContact will never result in undefined.

Applied to files:

  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessagesList.tsx
  • apps/meteor/client/views/omnichannel/contactInfo/tabs/ContactInfoHistory/ContactInfoHistoryMessages.tsx
📚 Learning: 2025-11-19T18:20:37.116Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 37419
File: apps/meteor/server/services/media-call/service.ts:141-141
Timestamp: 2025-11-19T18:20:37.116Z
Learning: In apps/meteor/server/services/media-call/service.ts, the sendHistoryMessage method should use call.caller.id or call.createdBy?.id as the message author, not call.transferredBy?.id. Even for transferred calls, the message should appear in the DM between the two users who are calling each other, not sent by the person who transferred the call.

Applied to files:

  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessagesList.tsx
  • apps/meteor/client/views/omnichannel/contactInfo/tabs/ContactInfoHistory/ContactInfoHistoryMessages.tsx
🧬 Code graph analysis (2)
apps/meteor/client/views/omnichannel/hooks/useCannedResponseList.ts (2)
apps/meteor/client/meteor/minimongo/Cursor.ts (1)
  • count (277-283)
apps/meteor/client/lib/queryKeys.ts (1)
  • cannedResponsesQueryKeys (41-44)
apps/meteor/client/views/omnichannel/contactInfo/tabs/ContactInfoHistory/ContactInfoHistoryMessages.tsx (1)
apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts (1)
  • useHistoryMessageList (21-59)
⏰ 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 (14)
apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/CannedResponse.tsx (1)

22-22: LGTM: Type relaxation aligns with broader refactor.

The optional departmentName is consistent with the updated useScopeDict signature and the broader PR objective to phase out AsyncState patterns.

apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/Item.tsx (1)

11-11: LGTM: Consistent type update.

The optional departmentName aligns with the related changes in CannedResponse.tsx and WrapCannedResponse.tsx.

apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/WrapCannedResponse.tsx (1)

12-12: LGTM: Type consistency maintained.

The optional departmentName in cannedItem is properly propagated to the CannedResponse component, which now accepts optional departmentName.

apps/meteor/client/views/omnichannel/cannedResponses/modals/CreateCannedResponse/CreateCannedResponseModal.tsx (1)

1-1: LGTM: Clean removal of departmentName dependency.

The changes correctly replace departmentName with departmentId from the IOmnichannelCannedResponse type, simplifying the data model. The conditional inclusion of departmentId in the save payload (line 47) ensures backward compatibility when the field is absent.

Also applies to: 14-21, 24-24

apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/CannedResponseList.tsx (1)

24-24: The parent component correctly implements the parameterless loadMoreItems signature with fetchNextPage from useInfiniteQuery. React Query manages pagination internally via pageParam, eliminating the need for explicit start/end parameters. The implementation is correct and all consumers are properly updated.

apps/meteor/client/views/omnichannel/hooks/useCannedResponseList.ts (1)

7-58: Well-structured migration to useInfiniteQuery.

The refactor from AsyncState to react-query's useInfiniteQuery is implemented correctly:

  • Centralized query keys are used properly
  • Offset-based pagination with getNextPageParam is correct
  • The select function appropriately flattens pages into a consumable shape
  • Type transformations maintain compatibility with existing consumers
apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessagesList.tsx (6)

26-27: LGTM! Import changes align with React Query migration.

The removal of useUserId is correct since the new useHistoryMessageList hook no longer requires a userId parameter.


103-107: Loading state correctly uses isPending.

The initial loading state is handled appropriately with the React Query isPending property.


115-115: Empty state condition is correct.

The check ensures the empty state only appears when the query successfully returns zero results.


117-117: Content condition is correct.

The past issue with the undefined history variable has been addressed. The current condition correctly uses the messages array and safely guards the Virtuoso rendering.


127-127: Pagination trigger correctly uses fetchNextPage().

The endReached callback correctly triggers pagination with React Query's fetchNextPage() method.


130-137: itemContent callback correctly renders messages.

The function logic is sound, and TypeScript inference handles the return type correctly. The implementation properly computes sequential and new-day flags for message grouping.

apps/meteor/client/views/omnichannel/contactInfo/tabs/ContactInfoHistory/ContactInfoHistoryMessages.tsx (2)

17-46: LGTM! Clean refactoring to react-query.

The removal of useUserId and the query setup using react-query patterns are correct. The debounced query construction properly memoizes the parameters.


53-82: LGTM! Event handling and layout are correct.

The search handler, message grouping logic, and footer rendering are implemented correctly.

Also applies to: 110-132

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/providers/AppsProvider.tsx (1)

10-12: Prefer idiomatic JSX children pattern.

Using children={children} as a prop works but is not the canonical React pattern. Wrap children in the JSX body for better readability and to align with React conventions.

🔎 Proposed refactor
 const AppsProvider = ({ children }: AppsProviderProps) => {
-	return <AppsContext.Provider children={children} value={AppClientOrchestratorInstance} />;
+	return <AppsContext.Provider value={AppClientOrchestratorInstance}>{children}</AppsContext.Provider>;
 };
📜 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 f1a19e7 and c5b0ac9.

📒 Files selected for processing (10)
  • apps/meteor/client/contexts/AppsContext.tsx
  • apps/meteor/client/providers/AppsProvider.tsx
  • apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx
  • apps/meteor/client/views/marketplace/AppsPage/AppsPageContentBody.tsx
  • apps/meteor/client/views/marketplace/AppsPage/PrivateEmptyState.spec.tsx
  • apps/meteor/client/views/marketplace/AppsPage/UnsupportedEmptyState.spec.tsx
  • apps/meteor/client/views/marketplace/hooks/useApps.ts
  • apps/meteor/client/views/marketplace/hooks/useAppsOrchestration.ts
  • apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts
  • apps/meteor/tests/mocks/client/marketplace.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • apps/meteor/client/views/marketplace/AppsPage/PrivateEmptyState.spec.tsx
  • apps/meteor/client/views/marketplace/AppsPage/UnsupportedEmptyState.spec.tsx
  • apps/meteor/client/views/marketplace/hooks/useApps.ts
  • apps/meteor/tests/mocks/client/marketplace.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/marketplace/AppsPage/AppsPageContentBody.tsx
  • apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts
  • apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx
  • apps/meteor/client/views/marketplace/hooks/useAppsOrchestration.ts
  • apps/meteor/client/contexts/AppsContext.tsx
  • apps/meteor/client/providers/AppsProvider.tsx
🧠 Learnings (3)
📚 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/marketplace/AppsPage/AppsPageContentBody.tsx
  • apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx
  • apps/meteor/client/views/marketplace/hooks/useAppsOrchestration.ts
📚 Learning: 2025-12-02T22:23:49.593Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 37654
File: apps/meteor/client/hooks/useAppSlashCommands.ts:32-38
Timestamp: 2025-12-02T22:23:49.593Z
Learning: In apps/meteor/client/hooks/useAppSlashCommands.ts, the `data?.forEach((command) => slashCommands.add(command))` call during render is intentional. The query is configured with `structuralSharing: false` to prevent React Query from keeping stable data references, and `slashCommands.add` is idempotent, so executing on every render is acceptable and ensures the command registry stays current.

Applied to files:

  • apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts
  • apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.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/marketplace/AppsPage/AppsPageContent.tsx
🧬 Code graph analysis (4)
apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts (1)
apps/meteor/client/views/marketplace/hooks/useApps.ts (1)
  • useApps (76-180)
apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx (4)
apps/meteor/client/contexts/AppsContext.tsx (1)
  • AppsContext (29-29)
packages/ui-contexts/src/index.ts (3)
  • useTranslation (81-81)
  • useRouter (63-63)
  • useRouteParameter (65-65)
apps/meteor/client/views/marketplace/hooks/useAppsReload.ts (1)
  • useAppsReload (6-11)
apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts (1)
  • useFilteredApps (15-130)
apps/meteor/client/views/marketplace/hooks/useAppsOrchestration.ts (1)
apps/meteor/client/contexts/AppsContext.tsx (1)
  • AppsContext (29-29)
apps/meteor/client/providers/AppsProvider.tsx (1)
apps/meteor/client/contexts/AppsContext.tsx (1)
  • AppsContext (29-29)
🪛 Biome (2.1.2)
apps/meteor/client/providers/AppsProvider.tsx

[error] 11-11: Avoid passing children using a prop

The canonical way to pass children in React is to use JSX elements

(lint/correctness/noChildrenProp)

⏰ 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). (4)
  • GitHub Check: 📦 Build Packages
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: CodeQL-Build
  • GitHub Check: CodeQL-Build
🔇 Additional comments (9)
apps/meteor/client/contexts/AppsContext.tsx (1)

27-29: LGTM! Clean API surface simplification.

The context type has been appropriately simplified to expose only the orchestrator, removing the AsyncState-based properties. This aligns perfectly with the PR's objective to phase out the AsyncState pattern.

apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts (2)

61-76: LGTM! Non-mutating sort correctly implemented.

The use of toSorted instead of sort ensures the cached query data is not mutated, addressing the previous review concerns. This is the correct approach when transforming React Query data in a select function.


34-130: Well-structured migration to React Query.

The refactor successfully replaces the AsyncState pattern with a clean React Query select transformation. The data flow is clear: context-based app source selection → composed filtering pipeline → pagination. The implementation properly handles all the filtering, sorting, and pagination logic in a single cohesive transformation.

apps/meteor/client/views/marketplace/hooks/useAppsOrchestration.ts (1)

5-5: LGTM! Simplified hook implementation.

The hook now returns the full context value directly instead of wrapping it in an object. This is a cleaner API that aligns with the AppsContext simplification. Consumers should note this is a breaking change requiring updates from const { orchestrator } = useAppsOrchestration() to const orchestrator = useAppsOrchestration().

apps/meteor/client/views/marketplace/AppsPage/AppsPageContentBody.tsx (1)

47-50: LGTM! Cleaner presentational component.

Removing the error guard simplifies this component to be purely presentational. Error handling is now appropriately managed at the parent level in AppsPageContent, resulting in better separation of concerns.

apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx (4)

113-122: LGTM! Proper React Query integration.

The migration to useFilteredApps returning React Query's discriminated union type (isPending, isError, error, data) is implemented correctly. The return type is properly leveraged with early returns for pending and error states.


165-187: LGTM! Appropriate pending state handling.

The pending state correctly renders the header, filters, and skeleton, providing good user feedback during data loading.


189-215: LGTM! Proper error state handling.

The error state correctly distinguishes between unsupported version and connection errors, rendering appropriate UI for each case. The TODO comment on line 190 appropriately documents the technical debt around error message string comparison—this is existing technical debt, not introduced by this PR.


235-256: LGTM! Type-safe data access via early returns.

The direct access to data properties (without optional chaining) is safe because of the early returns for isPending and isError. At this point in the code flow, TypeScript correctly infers that data is defined, ensuring type safety. The chain of empty state conditions with proper fallback to AppsPageContentBody is well-structured.

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 4 files (changes from recent commits).

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/contextualBar/Threads/hooks/useThreadsList.ts">

<violation number="1" location="apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts:107">
P2: The `itemCount` adjustment formula is inconsistent across pages. Each page calculates `itemCount` based only on that page&#39;s filtering ratio (`items.length - threads.length`), but `getNextPageParam` compares cumulative loaded items against the last page&#39;s `itemCount`. This causes the pagination threshold to change unpredictably depending on which items get filtered on the last page.

Consider tracking cumulative filtered items in the query context, or removing the adjustment entirely since the FIXME comment already acknowledges this is an estimation.</violation>
</file>

Reply to cubic to teach it or ask questions. 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: 0

♻️ Duplicate comments (3)
apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/WrapCannedResponseList.tsx (1)

34-35: Address the TODO: Handle loading state to prevent UI flicker.

During initial fetch, data is undefined, causing itemCount to default to 0 and cannedItems to [] (lines 73-74). This will briefly show the empty state before data arrives, creating a jarring user experience.

🔎 Suggested fix

Destructure isPending from the hook and handle it appropriately:

-	// TODO: handle pending and error states
-	const { data, fetchNextPage, refetch } = useCannedResponseList({ filter: debouncedText, type });
+	const { data, fetchNextPage, refetch, isPending } = useCannedResponseList({ filter: debouncedText, type });

Then either:

  1. Pass isPending to CannedResponseList if it accepts a loading prop, or
  2. Conditionally render a loading indicator before the list:
 	return (
+		isPending ? </* loading indicator */> : (
 		<CannedResponseList
 			loadMoreItems={fetchNextPage}
 			cannedItems={data?.cannedItems ?? []}
 			itemCount={data?.total ?? 0}
 			...
 		/>
+		)
 	);

Similarly, consider handling isError and error to show error states.

apps/meteor/client/views/mailer/MailerUnsubscriptionPage.tsx (1)

18-20: Fix error handling to pass error message string.

The dispatchToastMessage expects a string for the message field, but the entire error object is being passed. This will cause incorrect toast display or runtime errors.

🔎 Proposed fix
-		onError: (error) => {
-			dispatchToastMessage({ type: 'error', message: error });
+		onError: (error: Error) => {
+			dispatchToastMessage({ type: 'error', message: error.message });
 		},
apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts (1)

110-116: Pagination offset estimation documented.

The FIXME at lines 112-113 correctly documents that offset-based pagination using filtered item count is an estimation. Since the server filters with the same type and text parameters, client-side filtering should rarely diverge. The acknowledged limitation is acceptable for this refactor, with cursor-based pagination as a future improvement path.

🧹 Nitpick comments (5)
apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts (2)

24-37: Minor: Regex created on every filter invocation.

The RegExp constructor is called each time filter is invoked (once per message). For typical batch sizes, this is negligible, but for large datasets it could be optimized by memoizing the regex separately and passing it into the filter closure.

Not blocking given the expected discussion list sizes and the complexity of refactoring around useEffectEvent.


56-63: Consider whether client-side filtering is necessary.

The API call at lines 49-54 already passes roomId and text for server-side filtering. The client-side filter at line 58 applies the same conditions (rid match and text regex).

If this is intentional for:

  1. Type narrowing to IDiscussionMessage — the filter serves as a type guard
  2. Ensuring consistency with the real-time update filter used in useInfiniteMessageQueryUpdates

...then the redundancy is acceptable. However, if the server is guaranteed to return only matching items, consider whether the filter could be simplified to just the type guard (isDiscussionMessageInRoom) for the initial fetch, reserving the full filter for real-time updates.

The itemCount adjustment at line 63 is a sensible defensive measure for edge cases where client and server filtering diverge.

apps/meteor/client/views/mailer/MailerUnsubscriptionPage.tsx (1)

28-28: Simplify mutation invocation.

The .call(null, ...) syntax is unnecessary. React Query's mutate function can be called directly.

🔎 Proposed fix
-		mutation.mutate.call(null, { _id, createdAt });
+		mutation.mutate({ _id, createdAt });
apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts (1)

75-86: Unnecessary type assertion before filter call.

At line 76, message as T is cast before calling the filter, but the filter signature accepts IMessage and narrows to T. The cast is redundant since IMessage is the expected input.

🔎 Suggested simplification
 const unsubscribeFromRoomMessages = subscribeToRoomMessages(roomId, (message) => {
-  if (!getFilter()(message as T)) return;
+  if (!getFilter()(message)) return;

   mutateQueryData((items) => {
     const index = items.findIndex((i) => i._id === message._id);
     if (index !== -1) {
-      items[index] = message as T;
+      items[index] = message as T; // Safe: filter confirmed message is T
     } else {
       items.push(message as T);
       items.sort(getCompare());
     }
   });
 });
apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts (1)

43-51: Redundant double-sort of items.

The items are sorted twice with the same compare function: once at line 46 after filtering, then again at line 49 when returning. Remove one of the .toSorted(compare) calls.

🔎 Proposed fix
 const items = messages
   .map((message) => mapMessageFromApi(message))
   .filter(filter)
   .toSorted(compare);

 return {
-  items: items.toSorted(compare),
+  items,
   itemCount: total + (items.length - messages.length), // adjust total to account for filtered out items
 };
📜 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 c5b0ac9 and f5d93bf.

📒 Files selected for processing (6)
  • apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts
  • apps/meteor/client/views/mailer/MailerUnsubscriptionPage.tsx
  • apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/WrapCannedResponseList.tsx
  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts
  • apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts
  • apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.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/hooks/useInfiniteMessageQueryUpdates.ts
  • apps/meteor/client/views/mailer/MailerUnsubscriptionPage.tsx
  • apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts
  • apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts
  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts
  • apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/WrapCannedResponseList.tsx
🧠 Learnings (3)
📚 Learning: 2025-12-02T22:23:49.593Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 37654
File: apps/meteor/client/hooks/useAppSlashCommands.ts:32-38
Timestamp: 2025-12-02T22:23:49.593Z
Learning: In apps/meteor/client/hooks/useAppSlashCommands.ts, the `data?.forEach((command) => slashCommands.add(command))` call during render is intentional. The query is configured with `structuralSharing: false` to prevent React Query from keeping stable data references, and `slashCommands.add` is idempotent, so executing on every render is acceptable and ensures the command registry stays current.

Applied to files:

  • apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts
  • apps/meteor/client/views/mailer/MailerUnsubscriptionPage.tsx
  • apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts
📚 Learning: 2025-11-19T18:20:37.116Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 37419
File: apps/meteor/server/services/media-call/service.ts:141-141
Timestamp: 2025-11-19T18:20:37.116Z
Learning: In apps/meteor/server/services/media-call/service.ts, the sendHistoryMessage method should use call.caller.id or call.createdBy?.id as the message author, not call.transferredBy?.id. Even for transferred calls, the message should appear in the DM between the two users who are calling each other, not sent by the person who transferred the call.

Applied to files:

  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts
📚 Learning: 2025-12-18T15:18:31.688Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 37773
File: apps/meteor/client/views/mediaCallHistory/MediaCallHistoryInternal.tsx:24-34
Timestamp: 2025-12-18T15:18:31.688Z
Learning: In apps/meteor/client/views/mediaCallHistory/MediaCallHistoryInternal.tsx, for internal call history items, the item.contactId is guaranteed to always match either the caller.id or callee.id in the call data, so the contact resolution in getContact will never result in undefined.

Applied to files:

  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts
🧬 Code graph analysis (4)
apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts (5)
packages/core-typings/src/ISubscription.ts (1)
  • ISubscription (11-81)
packages/core-typings/src/IMessage/IMessage.ts (1)
  • IThreadMainMessage (316-320)
packages/ui-contexts/src/hooks/useUserId.ts (1)
  • useUserId (5-5)
apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts (1)
  • useInfiniteMessageQueryUpdates (8-108)
apps/meteor/client/lib/queryKeys.ts (1)
  • roomsQueryKeys (13-34)
apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts (2)
apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts (1)
  • useInfiniteMessageQueryUpdates (8-108)
apps/meteor/client/lib/queryKeys.ts (1)
  • roomsQueryKeys (13-34)
apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts (1)
apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts (1)
  • useInfiniteMessageQueryUpdates (8-108)
apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/WrapCannedResponseList.tsx (1)
apps/meteor/client/views/omnichannel/hooks/useCannedResponseList.ts (1)
  • useCannedResponseList (7-59)
⏰ 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). (4)
  • GitHub Check: 📦 Build Packages
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: CodeQL-Build
  • GitHub Check: CodeQL-Build
🔇 Additional comments (14)
apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts (3)

1-10: LGTM!

Imports are appropriate for the React Query-based approach and align with the broader refactoring pattern in this PR.


12-17: Well-structured helper functions.

The type guards and comparison function are cleanly extracted, making the filtering and sorting logic testable and readable.


67-76: Pagination logic is sound with acknowledged limitations.

The offset-based pagination via getNextPageParam correctly computes the next offset from loaded items count. The select transformer properly flattens pages and extracts the latest itemCount.

The FIXME at lines 72-73 appropriately documents the inherent limitation with offset-based pagination when items can be created/removed mid-pagination. The suggested improvement (cursor-based pagination using createdAt/updatedAt) would be a more robust long-term solution but is out of scope for this refactor.

apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/WrapCannedResponseList.tsx (2)

67-67: LGTM: Correctly using React Query's refetch.

The migration from reload to refetch is appropriate for React Query.


72-85: LGTM: Props correctly mapped to React Query hook return values.

The prop mappings align with the hook's return shape:

  • loadMoreItems={fetchNextPage} — correct for infinite queries
  • cannedItems={data?.cannedItems ?? []} — appropriate fallback for undefined data
  • itemCount={data?.total ?? 0} — appropriate fallback for undefined data
  • reload={refetch} — correct for manual re-fetching
apps/meteor/client/views/mailer/MailerUnsubscriptionPage.tsx (1)

41-43: Rendering logic looks good.

The inclusion of isIdle in the loading state check addresses the initial render concern from previous reviews. The conditional rendering correctly handles all mutation states.

Note: Line 42 assumes error has a .message property, which will be correct once the error handler fix (lines 18-20) is applied.

apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts (3)

8-18: LGTM!

Well-structured generic hook signature with proper type constraints. The type guard pattern for filter ensures type safety when narrowing IMessage to T.


46-47: Edge case when pageSize falls back to items.length.

If lastPage.items.length is 0 (e.g., on first mutation before any data is loaded) but items exist after mutation, using items.length as pageSize creates a single page containing all items. This works but could produce unexpectedly large pages. Since initial empty state is rare and the behavior is safe, this is acceptable.


102-107: LGTM!

Clean subscription cleanup and appropriate dependency tracking. The useEffectEvent wrappers ensure stable references while accessing fresh values.

apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts (2)

15-31: LGTM!

Clean setup with proper query key including searchTerm for cache isolation. The filter and compare functions are appropriately simple.


53-64: Pagination estimation acknowledged.

The FIXME comment correctly identifies that the offset-based pagination may drift if messages are created or deleted during pagination. Since the server handles searchTerm filtering, client-side filtering should rarely remove items, minimizing pagination issues.

apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts (3)

12-46: LGTM!

Well-designed discriminated union for options and clean helper predicates. The compare function correctly prioritizes tlm (thread last message time) with fallback to ts.


66-70: Client-side tunread check may diverge from server.

The isThreadUnread check at lines 66-70 uses the subscription's tunread array passed from the client, while the server's type: 'unread' filter may have slightly different criteria or timing. If they diverge, some threads could be filtered out client-side that weren't filtered server-side, affecting pagination offset accuracy.

This is likely rare in practice, and the FIXME at line 112-113 acknowledges the estimation limitation.


100-108: LGTM!

The mapping, filtering, and sorting pipeline is correct. The itemCount adjustment properly accounts for any items removed by client-side filtering.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (3)
apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts (1)

47-50: Acknowledged: Offset-based pagination has known limitations.

The FIXME correctly identifies that offset-based pagination can become inconsistent when items are created or removed during pagination. This is a known limitation but acceptable for current UX requirements.

For future improvement, consider cursor-based pagination using ts, _id, or server-returned continuation tokens to avoid offset drift.

apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts (1)

58-61: Acknowledged: Offset-based pagination has known limitations.

The FIXME correctly identifies that offset-based pagination can become inconsistent when discussions are created or removed during pagination. This is a known limitation but acceptable for current UX requirements.

For future improvement, consider cursor-based pagination using ts, _id, or server-returned continuation tokens to avoid offset drift.

apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts (1)

73-78: Optional: Consider memoizing regex creation.

The regex is recreated on every message filter (line 74). While useEffectEvent memoizes the filter function, the regex itself could be pre-computed:

const textRegex = text ? new RegExp(escapeRegExp(text), 'i') : null;

// Later in filter:
if (textRegex && !isThreadTextMatching(message, textRegex)) {
	return false;
}

This is a minor optimization and can be deferred.

📜 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 f5d93bf and 493cfc4.

📒 Files selected for processing (4)
  • apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts
  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts
  • apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts
  • apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.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/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts
  • apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts
  • apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts
  • apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts
🧠 Learnings (3)
📚 Learning: 2025-11-19T18:20:37.116Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 37419
File: apps/meteor/server/services/media-call/service.ts:141-141
Timestamp: 2025-11-19T18:20:37.116Z
Learning: In apps/meteor/server/services/media-call/service.ts, the sendHistoryMessage method should use call.caller.id or call.createdBy?.id as the message author, not call.transferredBy?.id. Even for transferred calls, the message should appear in the DM between the two users who are calling each other, not sent by the person who transferred the call.

Applied to files:

  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts
📚 Learning: 2025-12-18T15:18:31.688Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 37773
File: apps/meteor/client/views/mediaCallHistory/MediaCallHistoryInternal.tsx:24-34
Timestamp: 2025-12-18T15:18:31.688Z
Learning: In apps/meteor/client/views/mediaCallHistory/MediaCallHistoryInternal.tsx, for internal call history items, the item.contactId is guaranteed to always match either the caller.id or callee.id in the call data, so the contact resolution in getContact will never result in undefined.

Applied to files:

  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts
📚 Learning: 2025-12-02T22:23:49.593Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 37654
File: apps/meteor/client/hooks/useAppSlashCommands.ts:32-38
Timestamp: 2025-12-02T22:23:49.593Z
Learning: In apps/meteor/client/hooks/useAppSlashCommands.ts, the `data?.forEach((command) => slashCommands.add(command))` call during render is intentional. The query is configured with `structuralSharing: false` to prevent React Query from keeping stable data references, and `slashCommands.add` is idempotent, so executing on every render is acceptable and ensures the command registry stays current.

Applied to files:

  • apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts
  • apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts
🧬 Code graph analysis (3)
apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts (3)
apps/meteor/client/meteor/minimongo/Cursor.ts (1)
  • count (277-283)
apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts (1)
  • useInfiniteMessageQueryUpdates (8-108)
apps/meteor/client/lib/queryKeys.ts (1)
  • omnichannelQueryKeys (51-97)
apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts (6)
packages/core-typings/src/ISubscription.ts (1)
  • ISubscription (11-81)
packages/core-typings/src/IMessage/IMessage.ts (1)
  • IThreadMainMessage (316-320)
apps/meteor/client/meteor/minimongo/Cursor.ts (1)
  • count (277-283)
packages/ui-contexts/src/hooks/useUserId.ts (1)
  • useUserId (5-5)
apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts (1)
  • useInfiniteMessageQueryUpdates (8-108)
apps/meteor/client/lib/queryKeys.ts (1)
  • roomsQueryKeys (13-34)
apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts (3)
packages/core-typings/src/IMessage/IMessage.ts (1)
  • IDiscussionMessage (329-333)
apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts (1)
  • useInfiniteMessageQueryUpdates (8-108)
apps/meteor/client/lib/queryKeys.ts (1)
  • roomsQueryKeys (13-34)
🪛 ast-grep (0.40.3)
apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts

[warning] 24-24: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(searchTerm, 'ig')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)

⏰ 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 (4)
apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts (2)

28-66: LGTM: mutateQueryData correctly maintains itemCount consistency.

The mutation logic properly adjusts itemCount based on the delta between before and after mutation (line 52), ensuring the total count reflects real-time additions and removals. This addresses the previous concern about stale counts.


70-107: LGTM: Real-time subscription handling is robust.

The effect correctly:

  • Gates on userId and roomId availability
  • Handles message updates (update existing or add+sort)
  • Handles individual and bulk deletions appropriately
  • Cleans up all subscriptions on unmount
apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts (1)

21-39: LGTM: Proper RegExp sanitization with escapeRegExp.

The filter correctly uses escapeRegExp (line 31) to sanitize the text parameter before constructing the RegExp, preventing ReDoS vulnerabilities. This is the correct pattern that should be applied consistently across similar hooks.

apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts (1)

86-113: Pagination and query implementation looks correct.

The refactoring successfully addresses the previous review concerns:

  • Query key now includes { type, text } (lines 87, 53) ✓
  • Pagination offset is based on actual fetched items without client-side filtering in queryFn (lines 98, 106) ✓
  • itemCount uses server's total directly without adjustment (line 99) ✓

The FIXME comment (lines 104-105) appropriately acknowledges the pagination estimation limitation.

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/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts (1)

19-19: Optional: Simplify the count parsing.

The template literal wrapper is unnecessary. Consider simplifying to:

const count = Number(getConfig('historyMessageListSize', 10)) || 10;
📜 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 beef3fa and 8c3e8b0.

📒 Files selected for processing (2)
  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts
  • apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.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/contextualBar/Threads/hooks/useThreadsList.ts
  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts
🧠 Learnings (6)
📚 Learning: 2025-12-02T22:23:49.593Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 37654
File: apps/meteor/client/hooks/useAppSlashCommands.ts:32-38
Timestamp: 2025-12-02T22:23:49.593Z
Learning: In apps/meteor/client/hooks/useAppSlashCommands.ts, the `data?.forEach((command) => slashCommands.add(command))` call during render is intentional. The query is configured with `structuralSharing: false` to prevent React Query from keeping stable data references, and `slashCommands.add` is idempotent, so executing on every render is acceptable and ensures the command registry stays current.

Applied to files:

  • apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts
📚 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/contextualBar/Threads/hooks/useThreadsList.ts
📚 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/contextualBar/Threads/hooks/useThreadsList.ts
📚 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/contextualBar/Threads/hooks/useThreadsList.ts
📚 Learning: 2025-11-19T18:20:37.116Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 37419
File: apps/meteor/server/services/media-call/service.ts:141-141
Timestamp: 2025-11-19T18:20:37.116Z
Learning: In apps/meteor/server/services/media-call/service.ts, the sendHistoryMessage method should use call.caller.id or call.createdBy?.id as the message author, not call.transferredBy?.id. Even for transferred calls, the message should appear in the DM between the two users who are calling each other, not sent by the person who transferred the call.

Applied to files:

  • apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts
  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts
📚 Learning: 2025-12-18T15:18:31.688Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 37773
File: apps/meteor/client/views/mediaCallHistory/MediaCallHistoryInternal.tsx:24-34
Timestamp: 2025-12-18T15:18:31.688Z
Learning: In apps/meteor/client/views/mediaCallHistory/MediaCallHistoryInternal.tsx, for internal call history items, the item.contactId is guaranteed to always match either the caller.id or callee.id in the call data, so the contact resolution in getContact will never result in undefined.

Applied to files:

  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts
🧬 Code graph analysis (1)
apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts (2)
apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts (1)
  • useInfiniteMessageQueryUpdates (8-108)
apps/meteor/client/lib/queryKeys.ts (1)
  • omnichannelQueryKeys (51-97)
⏰ 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 (5)
apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts (3)

1-9: LGTM: Imports support the migration to react-query.

The addition of escapeRegExp (line 2) addresses the previous ReDoS security concern. The new react-query imports and supporting utilities are appropriate for the refactored implementation.


21-30: Excellent: ReDoS vulnerability resolved.

The real-time updates hook is correctly configured. Line 27 now properly escapes searchTerm using escapeRegExp before constructing the RegExp, which addresses the security concern from the previous review. The filter and sort logic correctly replicate server-side behavior.


32-58: Approve: Infinite query implementation follows best practices.

The useInfiniteQuery setup is correct:

  • Query key consistency ensures real-time updates work (lines 22 & 33)
  • Sort order is consistent between client and server (ascending by timestamp)
  • Data transformation and flattening logic is appropriate

The FIXME comment (lines 49-50) identifies a known limitation with offset-based pagination during real-time updates. While cursor-based pagination would be more robust, the current approach is functional and the developer is aware of the trade-off.

apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts (2)

58-62: Verify userId availability when type is 'following'.

When type === 'following' but userId is undefined (e.g., during session expiration), the follower check is skipped and the filter returns true for all messages (line 77). This could cause real-time updates to incorrectly include all threads instead of just followed ones.

Confirm that your auth model guarantees userId is always defined when viewing threads, or add an explicit guard:

if (type === 'following') {
	if (!userId) {
		return false;
	}
	if (!isThreadFollowedByUser(message, userId)) {
		return false;
	}
}

42-110: Solid refactoring to React Query patterns.

The migration from AsyncState to useInfiniteQuery is well-structured:

  • Server-side pagination with offset-based getNextPageParam
  • Consistent query keys between initial fetch and real-time updates
  • Clean separation: server filtering in queryFn, client-side replication in useInfiniteMessageQueryUpdates for real-time messages
  • Proper type safety with discriminated union for options

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 (2)
apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts (2)

14-14: Simplify the config parsing.

The template literal wrapping getConfig before parsing is verbose. Consider simplifying to make the intent clearer.

🔎 Proposed simplification
-	const count = parseInt(`${getConfig('discussionListSize', 10)}`, 10);
+	const count = Number(getConfig('discussionListSize', 10));

52-57: Consider tracking the pagination estimation issue.

The FIXME correctly identifies that offset-based pagination can be unreliable when discussions are created or removed during pagination. This can lead to duplicate or missing items.

Consider one of the following approaches to resolve this:

  1. Server-side cursor support: Modify the API to return a cursor (e.g., the _id or ts of the last item) that can be used for consistent pagination.
  2. Timestamp-based pagination: Use createdAt or updatedAt fields for pagination boundaries instead of offsets.
  3. Track this technical debt: If immediate changes aren't feasible, create a follow-up issue to address this limitation.

Would you like me to open a new issue to track this pagination improvement, or help draft an API enhancement proposal?

📜 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 8c3e8b0 and 5310582.

📒 Files selected for processing (2)
  • apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts
  • apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.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/contextualBar/Threads/hooks/useThreadsList.ts
  • apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts
🧠 Learnings (5)
📚 Learning: 2025-12-02T22:23:49.593Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 37654
File: apps/meteor/client/hooks/useAppSlashCommands.ts:32-38
Timestamp: 2025-12-02T22:23:49.593Z
Learning: In apps/meteor/client/hooks/useAppSlashCommands.ts, the `data?.forEach((command) => slashCommands.add(command))` call during render is intentional. The query is configured with `structuralSharing: false` to prevent React Query from keeping stable data references, and `slashCommands.add` is idempotent, so executing on every render is acceptable and ensures the command registry stays current.

Applied to files:

  • apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts
📚 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/contextualBar/Threads/hooks/useThreadsList.ts
📚 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/contextualBar/Threads/hooks/useThreadsList.ts
📚 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/contextualBar/Threads/hooks/useThreadsList.ts
📚 Learning: 2025-11-19T18:20:37.116Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 37419
File: apps/meteor/server/services/media-call/service.ts:141-141
Timestamp: 2025-11-19T18:20:37.116Z
Learning: In apps/meteor/server/services/media-call/service.ts, the sendHistoryMessage method should use call.caller.id or call.createdBy?.id as the message author, not call.transferredBy?.id. Even for transferred calls, the message should appear in the DM between the two users who are calling each other, not sent by the person who transferred the call.

Applied to files:

  • apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts
🧬 Code graph analysis (2)
apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts (4)
packages/core-typings/src/ISubscription.ts (1)
  • ISubscription (11-81)
packages/ui-contexts/src/hooks/useUserId.ts (1)
  • useUserId (5-5)
apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts (1)
  • useInfiniteMessageQueryUpdates (8-108)
packages/core-typings/src/IMessage/IMessage.ts (1)
  • IThreadMainMessage (316-320)
apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts (3)
apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts (1)
  • useInfiniteMessageQueryUpdates (8-108)
packages/core-typings/src/IMessage/IMessage.ts (1)
  • IDiscussionMessage (329-333)
apps/meteor/client/lib/queryKeys.ts (1)
  • roomsQueryKeys (13-34)
⏰ 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 (6)
apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts (3)

11-29: LGTM on the type definition cleanup.

The discriminated union is well-structured, and the previous issue with the unused uid parameter has been properly addressed by removing it entirely from all variants.


38-70: Well-structured real-time update filtering.

The previous issues have been properly addressed:

  • Query key now includes { type, text } for proper cache invalidation
  • The userId check on line 48 prevents unsafe access when undefined

The tunread array is intentionally excluded from the query key since it changes frequently (as users read threads), and useInfiniteMessageQueryUpdates handles real-time updates appropriately.


72-99: Clean migration to React Query infinite pagination.

The previous pagination issues have been resolved by:

  1. Removing client-side filtering from queryFn - server now handles filtering via type and text params
  2. Using raw total for itemCount without adjustment
  3. Query key matching between useInfiniteQuery and useInfiniteMessageQueryUpdates

The FIXME comment (lines 90-91) appropriately documents the known limitation with offset-based pagination estimation.

apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts (3)

1-11: LGTM: Clean migration to React Query pattern.

The hook signature is well-typed and the imports are appropriate for the new implementation using useInfiniteQuery and useInfiniteMessageQueryUpdates.


58-62: LGTM: Efficient data transformation with select.

The select function efficiently flattens pages into a single array and extracts the total count from the last page. Using .at(-1) is a clean, modern approach for accessing the last element.


16-34: The client-side filter correctly replicates the server-side filtering logic. Both implementations check for drid existence and apply the same case-insensitive regex matching using escapeRegExp() for text filtering. The behavior is consistent when text is empty (both include all discussions) and when text is provided (both apply regex matching).

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts (1)

14-14: Simplify redundant template literal.

The template literal wrapping is unnecessary before parseInt. You can simplify this to:

const count = parseInt(getConfig('discussionListSize', 10), 10);
🔎 Proposed simplification
-	const count = parseInt(`${getConfig('discussionListSize', 10)}`, 10);
+	const count = parseInt(getConfig('discussionListSize', 10), 10);
apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts (1)

72-99: LGTM! Infinite query implementation is correct.

The pagination logic properly calculates offsets from loaded items (past issue resolved per 493cfc4), and the query key correctly includes filter parameters (past issue resolved per f8e2128). The FIXME comment appropriately documents the estimation limitation.

The type assertion on line 84 (as IThreadMainMessage[]) assumes the API contract is correct. Consider adding runtime validation if API consistency is a concern, but this pattern is standard and acceptable for typed APIs.

📜 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 8c3e8b0 and 1c43aa8.

📒 Files selected for processing (2)
  • apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts
  • apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.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/contextualBar/Threads/hooks/useThreadsList.ts
  • apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts
🧠 Learnings (5)
📚 Learning: 2025-12-02T22:23:49.593Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 37654
File: apps/meteor/client/hooks/useAppSlashCommands.ts:32-38
Timestamp: 2025-12-02T22:23:49.593Z
Learning: In apps/meteor/client/hooks/useAppSlashCommands.ts, the `data?.forEach((command) => slashCommands.add(command))` call during render is intentional. The query is configured with `structuralSharing: false` to prevent React Query from keeping stable data references, and `slashCommands.add` is idempotent, so executing on every render is acceptable and ensures the command registry stays current.

Applied to files:

  • apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts
📚 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/contextualBar/Threads/hooks/useThreadsList.ts
📚 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/contextualBar/Threads/hooks/useThreadsList.ts
📚 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/contextualBar/Threads/hooks/useThreadsList.ts
📚 Learning: 2025-11-19T18:20:37.116Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 37419
File: apps/meteor/server/services/media-call/service.ts:141-141
Timestamp: 2025-11-19T18:20:37.116Z
Learning: In apps/meteor/server/services/media-call/service.ts, the sendHistoryMessage method should use call.caller.id or call.createdBy?.id as the message author, not call.transferredBy?.id. Even for transferred calls, the message should appear in the DM between the two users who are calling each other, not sent by the person who transferred the call.

Applied to files:

  • apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts
🧬 Code graph analysis (2)
apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts (5)
packages/core-typings/src/ISubscription.ts (1)
  • ISubscription (11-81)
packages/ui-contexts/src/hooks/useUserId.ts (1)
  • useUserId (5-5)
apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts (1)
  • useInfiniteMessageQueryUpdates (8-108)
apps/meteor/client/lib/queryKeys.ts (1)
  • roomsQueryKeys (13-34)
packages/core-typings/src/IMessage/IMessage.ts (1)
  • IThreadMainMessage (316-320)
apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts (3)
apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts (1)
  • useInfiniteMessageQueryUpdates (8-108)
apps/meteor/client/lib/queryKeys.ts (1)
  • roomsQueryKeys (13-34)
packages/core-typings/src/IMessage/IMessage.ts (1)
  • IDiscussionMessage (329-333)
⏰ 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 (7)
apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts (3)

1-11: LGTM! Clean migration to React Query.

The simplified hook signature and migration to useInfiniteQuery align well with the PR objectives to phase out the custom AsyncState pattern. The parameter structure is clear and type-safe.


16-34: Well-implemented real-time updates filter.

The filter logic correctly:

  • Type-guards for IDiscussionMessage by checking the drid property
  • Escapes the text parameter to prevent regex injection
  • Mirrors server-side filtering to maintain data consistency

52-56: Pagination limitation acknowledged.

The FIXME comment correctly identifies that the offset-based pagination can be inaccurate when discussions are created or removed during pagination. The suggested approach of using timestamps (createdAt or updatedAt) would provide more stable pagination.

This is a known trade-off with the current implementation. Consider prioritizing the server-side pagination improvement if stale data issues are observed in practice.

apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts (4)

1-9: LGTM! Clean imports for React Query migration.

The imports properly support the refactored infinite-query pattern with real-time updates.


11-29: LGTM! Type definition correctly models filter variants.

The discriminated union properly enforces that tunread is required only for the 'unread' type, and the past uid parameter issue has been resolved.


31-36: LGTM! Hook initialization is correct.

The count parsing safely handles the config value, and userId is properly sourced from context.


38-70: LGTM! Real-time updates and filtering are well-implemented.

The filter properly replicates server-side logic with correct handling of all edge cases (undefined userId, text escaping, type guards). Past issues with query keys and undefined userId have been resolved.

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

Caution

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

⚠️ Outside diff range comments (3)
apps/meteor/client/components/RoomIcon/OmnichannelRoomIcon/provider/OmnichannelRoomIconProvider.tsx (1)

50-58: Caching does not work for undefined values.

When extractSnapshot returns undefined (icon not found), it's stored in the map. On subsequent calls, snapshots.get() returns undefined, which fails the !snapshot check, causing repeated extractions. Use Map.has() to properly distinguish between "not cached" and "cached as undefined".

🐛 Proposed fix
                         // No problem here, because it's return value is a cached in the snapshots map on subsequent calls
                         () => {
-                            let snapshot = snapshots.get(`${app}-${iconName}`);
+                            const key = `${app}-${iconName}`;
 
-                            if (!snapshot) {
-                                snapshot = extractSnapshot(app, iconName);
-                                snapshots.set(`${app}-${iconName}`, snapshot);
+                            if (!snapshots.has(key)) {
+                                snapshots.set(key, extractSnapshot(app, iconName));
                             }
 
-                            return snapshot;
+                            return snapshots.get(key);
                         },
apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/WrapCannedResponseList.tsx (1)

37-42: Type mismatch: departmentName is required in callback but optional in data.

The onClickItem callback expects departmentName: ILivechatDepartment['name'] (required), but cannedItems from useCannedResponseList have departmentName?: ILivechatDepartment['name'] (optional). This could cause type errors when clicking items without a department.

🔧 Suggested fix
 const onClickItem = useEffectEvent(
 	(
 		data: IOmnichannelCannedResponse & {
-			departmentName: ILivechatDepartment['name'];
+			departmentName?: ILivechatDepartment['name'];
 		},
 	) => {
apps/meteor/client/views/room/contextualBar/Threads/ThreadList.tsx (1)

78-104: Options object includes uid which is not in ThreadsListOptions.

The options object on lines 91-93 includes uid for the 'following' type, but looking at ThreadsListOptions in useThreadsList.ts, the 'following' variant doesn't accept a uid property. The hook uses useUserId() internally instead.

🔧 Suggested fix: Remove uid from options
 				case 'following':
 					return {
 						rid,
 						text,
 						type,
-						uid,
 					};
🤖 Fix all issues with AI agents
In
@apps/meteor/client/components/message/content/location/hooks/useAsyncImage.ts:
- Around line 17-19: In useAsyncImage's image error handler replace the invalid
e.error usage (image.addEventListener('error', (e) => { reject(e.error); }))
with rejecting a real Error that includes useful context; e.g., get the failing
element via e.target (or image) and call reject(new Error(`Image failed to load:
${image.src || (e.target as HTMLImageElement)?.src || 'unknown'}`)) so the
promise receives a meaningful error message.

In @apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts:
- Around line 75-86: The code is casting message to T before calling the type
guard, which defeats the guard; update the subscribeToRoomMessages callback to
call doFilter(message) with the original message (no cast) so the guard can
narrow its type, and keep the existing casts inside mutateQueryData
(items[index] = message as T and items.push(message as T)) since those are used
after the guard and within useEffectEvent context; target the callback passed to
subscribeToRoomMessages and adjust the doFilter invocation accordingly, leaving
mutateQueryData and doCompare usage unchanged.

In @apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx:
- Line 168: The translation key construction t(`Apps_context_${context}`) in
AppsPageContent can produce a bogus key when context is undefined; update the
code to use the same fallback pattern as AppsFilters (use the existing
AppsFilters fallback value when context is falsy) so the t(...) call uses a
valid default (e.g., replace the direct template with one that falls back to the
AppsFilters default when context is undefined); apply the same change for the
other two occurrences of the template translation call in this file (the ones
also building `Apps_context_${context}`).

In @apps/meteor/client/views/marketplace/hooks/useApps.ts:
- Around line 161-162: The query key is unstable because it includes object
references (instance.data, marketplace.data); change the useQuery key to use
stable primitives instead (e.g. instance.data?.id or instanceId and
marketplace.data?.id or marketplaceUpdatedAt) or a single derived token like
marketplaceQueryKeys.appsStored(instanceId, marketplaceUpdatedAt) so the key
only changes when meaningful identifiers/timestamps change; update the call site
that constructs the key (marketplaceQueryKeys.appsStored and the useQuery
invocation) to accept and pass those stable values rather than the full data
objects.

In @apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts:
- Around line 61-76: The sortingMethods map uses Array.prototype.toSorted (in
entries like the urf/url/az/za/mru/lru handlers) which isn't
transpiled/polyfilled for our targets; replace uses of toSorted with a
compatible pattern such as cloning the array (e.g., using spread or slice) and
then calling .sort(...) with the same comparator to avoid mutating the original
array, and remove or actually import the array.prototype.tosorted polyfill if
you prefer polyfilling instead; update only the sortingFunctions inside
useFilteredApps.ts (the sortingMethods object) so each value clones apps then
sorts with the existing comparator logic.

In @apps/meteor/client/views/marketplace/hooks/usePrivateAppsEnabled.ts:
- Line 3: The hook usePrivateAppsEnabled currently returns false when license
data is undefined, causing UI flicker during loading; change it to explicitly
check useLicense({ loadValues: true }).isPending and return null (or undefined)
while isPending is true so consumers can treat loading distinctly from a
definitive false, update the return type to boolean | null, and update any
consumers/tests (e.g., PrivateEmptyState, MarketplaceHeader.handleClickPrivate,
UninstallGrandfatheredAppModal) to handle the null loading state and adjust
tests to mock isPending: true where appropriate.

In @apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts:
- Line 84: The current cast "as IThreadMainMessage[]" hides potential API
mismatches because mapMessageFromApi returns IMessage; replace the unsafe
assertion by adding a runtime type guard (e.g., isThreadMainMessage) that checks
required IThreadMainMessage properties and use it to filter/transform the mapped
results (threads.map(mapMessageFromApi).filter(isThreadMainMessage)) before
assigning to items, or perform the validation/transformation immediately after
fetching /v1/chat.getThreadsList so the rest of the code can safely rely on a
type-safe IThreadMainMessage array; do not use the direct "as
IThreadMainMessage[]" assertion.
🧹 Nitpick comments (17)
apps/meteor/client/views/omnichannel/hooks/useCannedResponseList.ts (2)

28-30: Consider extracting department fetching to a separate cached query.

The getDepartments call inside queryFn executes on every page fetch, causing redundant network requests. Since departments rarely change during pagination, consider using a separate useQuery with caching:

♻️ Suggested approach
+import { useQuery, useInfiniteQuery } from '@tanstack/react-query';
+
 export const useCannedResponseList = ({ filter, type }: { filter: string; type: string }) => {
 	const getCannedResponses = useEndpoint('GET', '/v1/canned-responses');
 	const getDepartments = useEndpoint('GET', '/v1/livechat/department');
 
+	const { data: departmentsData } = useQuery({
+		queryKey: ['livechat', 'departments'],
+		queryFn: () => getDepartments({ text: '' }),
+		staleTime: 5 * 60 * 1000,
+	});
+
 	const count = 25;
 
 	return useInfiniteQuery({
 		queryKey: cannedResponsesQueryKeys.list({ filter, type }),
 		queryFn: async ({ pageParam: offset }) => {
 			const { cannedResponses, total } = await getCannedResponses({
 				// ... existing params
 			});
 
-			// TODO: Optimize by creating an endpoint that returns canned responses with department names
-			// TODO: Use another query for departments to avoid refetching
-			const { departments } = await getDepartments({ text: '' });
+			const departments = departmentsData?.departments ?? [];
 
 			return {
 				// ... rest unchanged
 			};
 		},
+		enabled: !!departmentsData,
 		// ... rest unchanged
 	});
 };

17-23: Consider using includes() for clearer membership checks.

The current approach using .find() works but includes() is more idiomatic for simple membership tests.

♻️ Suggested refinement
-			...(type && ['global', 'user'].find((option) => option === type) && { scope: type }),
-			...(type &&
-				!['global', 'user', 'all'].find((option) => option === type) && {
+			...(type && ['global', 'user'].includes(type) && { scope: type }),
+			...(type &&
+				!['global', 'user', 'all'].includes(type) && {
 				scope: 'department',
 				departmentId: type,
 			}),
apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useFilesList.ts (2)

10-11: Potential runtime error when room is undefined.

If useUserRoom(rid) returns undefined (e.g., when the room is not yet loaded or the user doesn't have access), room?.t falls back to 'c', but the query will still execute and potentially fail or return unexpected results.

Consider adding a check to disable the query when the room is not available:

♻️ Suggested improvement
 export const useFilesList = ({ rid, type, text }: { rid: Required<IUpload>['rid']; type: string; text: string }) => {
 	const room = useUserRoom(rid);
 
 	// ... roomTypes definition ...
 
 	const getFiles = useEndpoint('GET', roomTypes[room?.t ?? 'c']);
 
 	const count = parseInt(`${getConfig('discussionListSize', 10)}`, 10);
 
 	return useInfiniteQuery({
 		queryKey: roomsQueryKeys.files(rid, { type, text }),
+		enabled: !!room,
 		queryFn: async ({ pageParam: offset }) => {

46-62: Consider using a regular for...of loop instead of for await.

The items array is synchronous; using for await on it is valid but semantically misleading. The await calls inside the loop would work the same with a standard for...of loop.

♻️ Suggested change
-			for await (const file of items) {
+			for (const file of items) {
 				if (file.rid && file.content) {
 					const e2eRoom = await e2e.getInstanceByRoomId(file.rid);
apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFilesWithData.tsx (1)

30-41: Consider exposing isFetchingNextPage for pagination loading state.

Currently, loading={isPending} only shows the loading indicator during the initial fetch. If you want to provide visual feedback while loading additional pages (e.g., a footer spinner in the list), you could also destructure and pass isFetchingNextPage to the component.

This is optional since the current UX may be intentional for seamless infinite scrolling.

apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts (1)

21-30: Consider memoizing the regex for the filter callback.

The filter callback creates a new RegExp object for each message evaluated. While the impact is likely minimal for update-based filtering, moving the regex creation outside the callback would be a slight performance improvement.

♻️ Optional optimization
+	const searchRegex = searchTerm ? new RegExp(escapeRegExp(searchTerm), 'ig') : null;
+
 	useInfiniteMessageQueryUpdates({
 		queryKey: omnichannelQueryKeys.contactMessages(roomId, { searchTerm }),
 		roomId,
-		// Replicates the filtering done server-side
 		filter: (message): message is IMessage =>
 			(!('t' in message) || message.t === 'livechat-close') &&
-			(!searchTerm || new RegExp(escapeRegExp(searchTerm), 'ig').test(message.msg)),
-		// Replicates the sorting forced on server-side
+			(!searchRegex || searchRegex.test(message.msg)),
 		compare: (a, b) => a.ts.getTime() - b.ts.getTime(),
 	});
apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessagesList.tsx (1)

63-66: Consider destructuring hasNextPage and isFetchingNextPage for better UX.

The hook returns additional pagination state that could enhance the user experience—hasNextPage to guard fetchNextPage calls, and isFetchingNextPage to show a loading indicator at the list's end while fetching more items.

♻️ Optional enhancement
-	const { isPending, error, isSuccess, data, fetchNextPage } = useHistoryMessageList(query);
+	const { isPending, error, isSuccess, data, fetchNextPage, hasNextPage, isFetchingNextPage } = useHistoryMessageList(query);

Then update the endReached callback:

-								endReached={() => fetchNextPage()}
+								endReached={() => hasNextPage && !isFetchingNextPage && fetchNextPage()}
apps/meteor/client/views/omnichannel/contactInfo/tabs/ContactInfoHistory/ContactInfoHistoryMessages.tsx (2)

48-51: Consider destructuring isFetchingNextPage for better UX.

When fetching additional pages, users have no visual feedback that more messages are loading. Destructuring isFetchingNextPage from the query result would allow displaying a loading indicator at the bottom of the list during pagination.


110-117: Rename data parameter to avoid variable shadowing.

The data parameter shadows the outer data from the query result (line 48), which can be confusing during maintenance. Consider renaming to message for clarity and consistency with the type.

Suggested change
-								itemContent={(index, data) => {
+								itemContent={(index, message) => {
 									const lastMessage = messages[index - 1];
-									const isSequential = isMessageSequential(data, lastMessage, messageGroupingPeriod);
-									const isNewDay = isMessageNewDay(data, lastMessage);
+									const isSequential = isMessageSequential(message, lastMessage, messageGroupingPeriod);
+									const isNewDay = isMessageNewDay(message, lastMessage);
 									return (
-										<ContactHistoryMessage message={data} sequential={isSequential} isNewDay={isNewDay} showUserAvatar={showUserAvatar} />
+										<ContactHistoryMessage message={message} sequential={isSequential} isNewDay={isNewDay} showUserAvatar={showUserAvatar} />
 									);
 								}}
apps/meteor/client/providers/AppsProvider.tsx (1)

10-12: Avoid passing children using a prop.

The static analysis tool correctly identifies that children should be passed as JSX children rather than as a prop. This is the canonical React pattern.

♻️ Proposed fix
 const AppsProvider = ({ children }: AppsProviderProps) => {
-	return <AppsContext.Provider children={children} value={AppClientOrchestratorInstance} />;
+	return <AppsContext.Provider value={AppClientOrchestratorInstance}>{children}</AppsContext.Provider>;
 };
apps/meteor/client/views/marketplace/AppsPage/PrivateEmptyState.spec.tsx (1)

26-29: Consider extracting duplicated appRoot setup.

The appRoot configuration with translations is identical in both describe blocks. This could be extracted to a module-level constant.

♻️ Suggested refactor
+const appRoot = mockAppRoot().withTranslations('en', 'core', {
+	Private_apps_upgrade_empty_state_title: 'Upgrade to unlock private apps',
+	No_private_apps_installed: 'No private apps installed',
+});
+
 describe('with private apps enabled', () => {
 	beforeEach(async () => {
 		// ...mock setup
 	});
 
-	const appRoot = mockAppRoot().withTranslations('en', 'core', {
-		Private_apps_upgrade_empty_state_title: 'Upgrade to unlock private apps',
-		No_private_apps_installed: 'No private apps installed',
-	});
-
 	it('should offer to upgrade to unlock private apps', () => {
 		// ...
 	});
 });
 
 describe('without private apps enabled', () => {
 	beforeEach(async () => {
 		// ...mock setup
 	});
 
-	const appRoot = mockAppRoot().withTranslations('en', 'core', {
-		Private_apps_upgrade_empty_state_title: 'Upgrade to unlock private apps',
-		No_private_apps_installed: 'No private apps installed',
-	});
-
 	it('should offer to upgrade to unlock private apps', () => {
 		// ...
 	});
 });

Also applies to: 53-56

apps/meteor/client/lib/queryKeys.ts (1)

165-189: New marketplace and video conference query keys look good.

The structure is consistent with other query key objects. Minor observation: appsStored on line 169 uses ...args: unknown[] which is looser than other keys. Consider typing the args more specifically if the expected parameters are known.

apps/meteor/client/views/teams/contextualBar/channels/hooks/useTeamsChannelList.ts (1)

39-41: Redundant optional chaining.

The outer condition already ensures usersWaitingForE2EKeys is truthy, so the inner ?. on line 40 is unnecessary.

Suggested fix
 						...(usersWaitingForE2EKeys && {
-							usersWaitingForE2EKeys: usersWaitingForE2EKeys?.map(({ userId, ts }) => ({ userId, ts: new Date(ts) })),
+							usersWaitingForE2EKeys: usersWaitingForE2EKeys.map(({ userId, ts }) => ({ userId, ts: new Date(ts) })),
 						}),
apps/meteor/client/views/room/ImageGallery/hooks/useImagesList.ts (1)

30-46: Consider error handling for E2E decryption failures.

If e2e.decryptFileContent(file) throws, the entire query will fail. Consider whether individual file decryption failures should be handled gracefully (e.g., marking the file as undecryptable) rather than failing the whole page fetch.

💡 Optional: Graceful decryption error handling
 for await (const file of items) {
   if (file.rid && file.content) {
-    const e2eRoom = await e2e.getInstanceByRoomId(file.rid);
-    if (e2eRoom?.shouldConvertReceivedMessages()) {
-      const decrypted = await e2e.decryptFileContent(file);
-      const key = Base64.encode(
-        JSON.stringify({
-          ...decrypted.encryption,
-          name: String.fromCharCode(...new TextEncoder().encode(decrypted.name)),
-          type: decrypted.type,
-        }),
-      );
-      decrypted.path = `/file-decrypt${decrypted.path}?key=${key}`;
-      Object.assign(file, decrypted);
+    try {
+      const e2eRoom = await e2e.getInstanceByRoomId(file.rid);
+      if (e2eRoom?.shouldConvertReceivedMessages()) {
+        const decrypted = await e2e.decryptFileContent(file);
+        const key = Base64.encode(
+          JSON.stringify({
+            ...decrypted.encryption,
+            name: String.fromCharCode(...new TextEncoder().encode(decrypted.name)),
+            type: decrypted.type,
+          }),
+        );
+        decrypted.path = `/file-decrypt${decrypted.path}?key=${key}`;
+        Object.assign(file, decrypted);
+      }
+    } catch {
+      // File remains encrypted; consumer should handle display
     }
   }
 }
apps/meteor/client/views/room/contextualBar/Threads/ThreadList.tsx (1)

162-162: endReached should check for more pages before fetching.

Calling fetchNextPage() unconditionally on every endReached event may trigger unnecessary network requests when there are no more pages. React Query's hasNextPage can guard this.

♻️ Suggested improvement
-const { isPending, error, isSuccess, data, fetchNextPage } = useThreadsList(options);
+const { isPending, error, isSuccess, data, fetchNextPage, hasNextPage } = useThreadsList(options);

 ...

-endReached={() => fetchNextPage()}
+endReached={() => hasNextPage && fetchNextPage()}
apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts (1)

14-14: Simplify the parseInt call.

The template string wrapping is unnecessary. getConfig returns string | number, so you can simplify this:

Suggested simplification
-	const count = parseInt(`${getConfig('discussionListSize', 10)}`, 10);
+	const count = parseInt(String(getConfig('discussionListSize', 10)), 10);

Or if the config always returns a string:

-	const count = parseInt(`${getConfig('discussionListSize', 10)}`, 10);
+	const count = Number(getConfig('discussionListSize', 10)) || 10;
apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx (1)

85-90: Missing dependency in useEffect.

The effect depends on isRequested but createFilterStructureItems() internally uses the t translation function. While t is typically stable, ESLint's exhaustive-deps rule would flag this.

Suggested fix
 	useEffect(() => {
 		setSortFilterStructure({
 			label: t('Sort_By'),
 			items: createFilterStructureItems(),
 		});
-	}, [isRequested]);
+	}, [isRequested, t]);
📜 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 760d127 and ebff6b8.

📒 Files selected for processing (79)
  • apps/meteor/client/components/RoomIcon/OmnichannelRoomIcon/OmnichannelAppSourceRoomIcon.tsx
  • apps/meteor/client/components/RoomIcon/OmnichannelRoomIcon/context/OmnichannelRoomIconContext.tsx
  • apps/meteor/client/components/RoomIcon/OmnichannelRoomIcon/provider/OmnichannelRoomIconProvider.tsx
  • apps/meteor/client/components/message/content/location/MapView.tsx
  • apps/meteor/client/components/message/content/location/hooks/useAsyncImage.ts
  • apps/meteor/client/contexts/AppsContext.tsx
  • apps/meteor/client/contexts/hooks/useAppsReload.ts
  • apps/meteor/client/contexts/hooks/useAppsResult.ts
  • apps/meteor/client/hooks/lists/useRecordList.ts
  • apps/meteor/client/hooks/lists/useScrollableMessageList.ts
  • apps/meteor/client/hooks/lists/useScrollableRecordList.ts
  • apps/meteor/client/hooks/lists/useStreamUpdatesForMessageList.ts
  • apps/meteor/client/hooks/useAsyncState.ts
  • apps/meteor/client/hooks/useComponentDidUpdate.ts
  • apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts
  • apps/meteor/client/hooks/useRoomsList.ts
  • apps/meteor/client/lib/asyncState/AsyncState.ts
  • apps/meteor/client/lib/asyncState/AsyncStatePhase.ts
  • apps/meteor/client/lib/asyncState/functions.ts
  • apps/meteor/client/lib/asyncState/index.ts
  • apps/meteor/client/lib/lists/CannedResponseList.ts
  • apps/meteor/client/lib/lists/DiscussionsList.ts
  • apps/meteor/client/lib/lists/FilesList.ts
  • apps/meteor/client/lib/lists/ImagesList.ts
  • apps/meteor/client/lib/lists/MessageList.ts
  • apps/meteor/client/lib/lists/RecordList.spec.ts
  • apps/meteor/client/lib/lists/RecordList.ts
  • apps/meteor/client/lib/lists/ThreadsList.ts
  • apps/meteor/client/lib/queryKeys.ts
  • apps/meteor/client/providers/AppsProvider.tsx
  • apps/meteor/client/providers/AppsProvider/AppsProvider.tsx
  • apps/meteor/client/providers/AppsProvider/index.ts
  • apps/meteor/client/providers/AppsProvider/storeQueryFunction.ts
  • apps/meteor/client/views/mailer/MailerUnsubscriptionPage.tsx
  • apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppRequests/AppRequests.tsx
  • apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx
  • apps/meteor/client/views/marketplace/AppsPage/AppsPageContentBody.tsx
  • apps/meteor/client/views/marketplace/AppsPage/PrivateEmptyState.spec.tsx
  • apps/meteor/client/views/marketplace/AppsPage/UnsupportedEmptyState.spec.tsx
  • apps/meteor/client/views/marketplace/hooks/useAppInfo.ts
  • apps/meteor/client/views/marketplace/hooks/useAppInstances.ts
  • apps/meteor/client/views/marketplace/hooks/useApps.spec.ts
  • apps/meteor/client/views/marketplace/hooks/useApps.ts
  • apps/meteor/client/views/marketplace/hooks/useAppsOrchestration.ts
  • apps/meteor/client/views/marketplace/hooks/useAppsReload.ts
  • apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts
  • apps/meteor/client/views/marketplace/hooks/useInstallApp.tsx
  • apps/meteor/client/views/marketplace/hooks/useLogs.ts
  • apps/meteor/client/views/marketplace/hooks/usePrivateAppsEnabled.ts
  • apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/CannedResponse.tsx
  • apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/CannedResponseList.tsx
  • apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/Item.tsx
  • apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/WrapCannedResponse.tsx
  • apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/WrapCannedResponseList.tsx
  • apps/meteor/client/views/omnichannel/cannedResponses/modals/CreateCannedResponse/CreateCannedResponseModal.tsx
  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessagesList.tsx
  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts
  • apps/meteor/client/views/omnichannel/contactInfo/tabs/ContactInfoHistory/ContactInfoHistoryMessages.tsx
  • apps/meteor/client/views/omnichannel/departments/EditDepartment.tsx
  • apps/meteor/client/views/omnichannel/hooks/useCannedResponseList.ts
  • apps/meteor/client/views/omnichannel/hooks/useScopeDict.ts
  • apps/meteor/client/views/room/ImageGallery/ImageGalleryData.tsx
  • apps/meteor/client/views/room/ImageGallery/hooks/useImagesList.ts
  • apps/meteor/client/views/room/contextualBar/Discussions/DiscussionsListContextBar.tsx
  • apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts
  • apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx
  • apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFilesWithData.tsx
  • apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useFilesList.ts
  • apps/meteor/client/views/room/contextualBar/Threads/ThreadList.tsx
  • apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts
  • apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/VideoConfList.tsx
  • apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/VideoConfListWithData.tsx
  • apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/VideoConfRecordList.ts
  • apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/useVideoConfList.ts
  • apps/meteor/client/views/teams/contextualBar/channels/TeamsChannels.tsx
  • apps/meteor/client/views/teams/contextualBar/channels/TeamsChannelsWithData.tsx
  • apps/meteor/client/views/teams/contextualBar/channels/hooks/useTeamsChannelList.ts
  • apps/meteor/ee/server/apps/marketplace/fetchMarketplaceApps.ts
  • apps/meteor/tests/mocks/client/marketplace.tsx
💤 Files with no reviewable changes (24)
  • apps/meteor/client/contexts/hooks/useAppsResult.ts
  • apps/meteor/client/hooks/lists/useScrollableMessageList.ts
  • apps/meteor/client/providers/AppsProvider/storeQueryFunction.ts
  • apps/meteor/client/lib/lists/DiscussionsList.ts
  • apps/meteor/client/providers/AppsProvider/index.ts
  • apps/meteor/client/hooks/lists/useStreamUpdatesForMessageList.ts
  • apps/meteor/client/lib/asyncState/AsyncState.ts
  • apps/meteor/client/lib/lists/ImagesList.ts
  • apps/meteor/client/lib/lists/RecordList.spec.ts
  • apps/meteor/client/hooks/useComponentDidUpdate.ts
  • apps/meteor/client/lib/asyncState/AsyncStatePhase.ts
  • apps/meteor/client/lib/lists/RecordList.ts
  • apps/meteor/client/lib/lists/CannedResponseList.ts
  • apps/meteor/client/lib/asyncState/index.ts
  • apps/meteor/client/contexts/hooks/useAppsReload.ts
  • apps/meteor/client/hooks/lists/useScrollableRecordList.ts
  • apps/meteor/client/providers/AppsProvider/AppsProvider.tsx
  • apps/meteor/client/hooks/lists/useRecordList.ts
  • apps/meteor/client/lib/asyncState/functions.ts
  • apps/meteor/client/lib/lists/MessageList.ts
  • apps/meteor/client/lib/lists/FilesList.ts
  • apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/VideoConfRecordList.ts
  • apps/meteor/client/hooks/useAsyncState.ts
  • apps/meteor/client/lib/lists/ThreadsList.ts
🚧 Files skipped from review as they are similar to previous changes (17)
  • apps/meteor/client/views/marketplace/hooks/useInstallApp.tsx
  • apps/meteor/ee/server/apps/marketplace/fetchMarketplaceApps.ts
  • apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/CannedResponse.tsx
  • apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/WrapCannedResponse.tsx
  • apps/meteor/client/components/message/content/location/MapView.tsx
  • apps/meteor/client/views/mailer/MailerUnsubscriptionPage.tsx
  • apps/meteor/client/views/marketplace/hooks/useAppInfo.ts
  • apps/meteor/client/views/room/contextualBar/Discussions/DiscussionsListContextBar.tsx
  • apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/VideoConfListWithData.tsx
  • apps/meteor/client/views/marketplace/hooks/useAppsReload.ts
  • apps/meteor/client/views/marketplace/hooks/useAppsOrchestration.ts
  • apps/meteor/client/views/omnichannel/cannedResponses/modals/CreateCannedResponse/CreateCannedResponseModal.tsx
  • apps/meteor/client/components/RoomIcon/OmnichannelRoomIcon/context/OmnichannelRoomIconContext.tsx
  • apps/meteor/client/views/room/ImageGallery/ImageGalleryData.tsx
  • apps/meteor/client/views/marketplace/hooks/useAppInstances.ts
  • apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/Item.tsx
  • apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppRequests/AppRequests.tsx
🧰 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:

  • apps/meteor/client/components/RoomIcon/OmnichannelRoomIcon/provider/OmnichannelRoomIconProvider.tsx
  • apps/meteor/client/views/marketplace/AppsPage/PrivateEmptyState.spec.tsx
  • apps/meteor/client/hooks/useRoomsList.ts
  • apps/meteor/client/views/omnichannel/departments/EditDepartment.tsx
  • apps/meteor/client/views/marketplace/hooks/useApps.spec.ts
  • apps/meteor/client/views/omnichannel/hooks/useScopeDict.ts
  • apps/meteor/client/views/teams/contextualBar/channels/hooks/useTeamsChannelList.ts
  • apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFilesWithData.tsx
  • apps/meteor/client/components/RoomIcon/OmnichannelRoomIcon/OmnichannelAppSourceRoomIcon.tsx
  • apps/meteor/client/views/marketplace/hooks/usePrivateAppsEnabled.ts
  • apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx
  • apps/meteor/client/views/marketplace/AppsPage/AppsPageContentBody.tsx
  • apps/meteor/client/views/room/ImageGallery/hooks/useImagesList.ts
  • apps/meteor/client/providers/AppsProvider.tsx
  • apps/meteor/client/views/room/contextualBar/Threads/ThreadList.tsx
  • apps/meteor/client/views/omnichannel/hooks/useCannedResponseList.ts
  • apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/WrapCannedResponseList.tsx
  • apps/meteor/client/views/omnichannel/contactInfo/tabs/ContactInfoHistory/ContactInfoHistoryMessages.tsx
  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts
  • apps/meteor/client/views/marketplace/hooks/useLogs.ts
  • apps/meteor/tests/mocks/client/marketplace.tsx
  • apps/meteor/client/lib/queryKeys.ts
  • apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts
  • apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/VideoConfList.tsx
  • apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts
  • apps/meteor/client/views/teams/contextualBar/channels/TeamsChannels.tsx
  • apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/CannedResponseList.tsx
  • apps/meteor/client/contexts/AppsContext.tsx
  • apps/meteor/client/views/marketplace/hooks/useApps.ts
  • apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx
  • apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts
  • apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts
  • apps/meteor/client/views/teams/contextualBar/channels/TeamsChannelsWithData.tsx
  • apps/meteor/client/views/marketplace/AppsPage/UnsupportedEmptyState.spec.tsx
  • apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useFilesList.ts
  • apps/meteor/client/components/message/content/location/hooks/useAsyncImage.ts
  • apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/useVideoConfList.ts
  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessagesList.tsx
**/*.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/marketplace/hooks/useApps.spec.ts
🧠 Learnings (25)
📚 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/components/RoomIcon/OmnichannelRoomIcon/provider/OmnichannelRoomIconProvider.tsx
  • apps/meteor/client/views/teams/contextualBar/channels/hooks/useTeamsChannelList.ts
  • apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts
  • apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useFilesList.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 : Ensure tests run reliably in parallel without shared state conflicts

Applied to files:

  • apps/meteor/client/views/marketplace/AppsPage/PrivateEmptyState.spec.tsx
  • apps/meteor/client/views/marketplace/hooks/useApps.spec.ts
  • apps/meteor/tests/mocks/client/marketplace.tsx
  • apps/meteor/client/views/marketplace/AppsPage/UnsupportedEmptyState.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 : Utilize Playwright fixtures (`test`, `page`, `expect`) for consistency in test files

Applied to files:

  • apps/meteor/client/views/marketplace/AppsPage/PrivateEmptyState.spec.tsx
  • apps/meteor/client/views/marketplace/hooks/useApps.spec.ts
  • apps/meteor/tests/mocks/client/marketplace.tsx
  • apps/meteor/client/views/marketplace/AppsPage/UnsupportedEmptyState.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/marketplace/AppsPage/PrivateEmptyState.spec.tsx
  • apps/meteor/client/views/marketplace/hooks/useApps.spec.ts
  • apps/meteor/tests/mocks/client/marketplace.tsx
  • apps/meteor/client/views/marketplace/AppsPage/UnsupportedEmptyState.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/marketplace/AppsPage/PrivateEmptyState.spec.tsx
  • apps/meteor/client/views/marketplace/hooks/useApps.spec.ts
  • apps/meteor/client/views/marketplace/AppsPage/AppsPageContentBody.tsx
  • apps/meteor/tests/mocks/client/marketplace.tsx
  • apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx
  • apps/meteor/client/views/marketplace/AppsPage/UnsupportedEmptyState.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/marketplace/AppsPage/PrivateEmptyState.spec.tsx
  • apps/meteor/client/views/marketplace/hooks/useApps.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/marketplace/AppsPage/PrivateEmptyState.spec.tsx
  • apps/meteor/client/views/marketplace/hooks/useApps.spec.ts
  • apps/meteor/tests/mocks/client/marketplace.tsx
  • apps/meteor/client/views/marketplace/AppsPage/UnsupportedEmptyState.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} : Store commonly used locators in variables/constants for reuse

Applied to files:

  • apps/meteor/client/views/marketplace/AppsPage/PrivateEmptyState.spec.tsx
  • apps/meteor/client/views/marketplace/hooks/useApps.spec.ts
  • apps/meteor/tests/mocks/client/marketplace.tsx
  • apps/meteor/client/views/marketplace/AppsPage/UnsupportedEmptyState.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/marketplace/AppsPage/PrivateEmptyState.spec.tsx
  • apps/meteor/tests/mocks/client/marketplace.tsx
  • apps/meteor/client/views/marketplace/AppsPage/UnsupportedEmptyState.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/marketplace/AppsPage/PrivateEmptyState.spec.tsx
  • apps/meteor/client/views/marketplace/hooks/useApps.spec.ts
  • apps/meteor/tests/mocks/client/marketplace.tsx
  • apps/meteor/client/views/marketplace/AppsPage/UnsupportedEmptyState.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 : All test files must be created in `apps/meteor/tests/e2e/` directory

Applied to files:

  • apps/meteor/client/views/marketplace/AppsPage/PrivateEmptyState.spec.tsx
  • apps/meteor/client/views/marketplace/hooks/useApps.spec.ts
📚 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/marketplace/AppsPage/PrivateEmptyState.spec.tsx
  • apps/meteor/client/views/marketplace/hooks/useApps.spec.ts
  • apps/meteor/tests/mocks/client/marketplace.tsx
  • apps/meteor/client/views/marketplace/AppsPage/UnsupportedEmptyState.spec.tsx
📚 Learning: 2025-12-02T22:23:49.593Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 37654
File: apps/meteor/client/hooks/useAppSlashCommands.ts:32-38
Timestamp: 2025-12-02T22:23:49.593Z
Learning: In apps/meteor/client/hooks/useAppSlashCommands.ts, the `data?.forEach((command) => slashCommands.add(command))` call during render is intentional. The query is configured with `structuralSharing: false` to prevent React Query from keeping stable data references, and `slashCommands.add` is idempotent, so executing on every render is acceptable and ensures the command registry stays current.

Applied to files:

  • apps/meteor/client/hooks/useRoomsList.ts
  • apps/meteor/client/views/teams/contextualBar/channels/hooks/useTeamsChannelList.ts
  • apps/meteor/client/views/room/ImageGallery/hooks/useImagesList.ts
  • apps/meteor/client/views/omnichannel/hooks/useCannedResponseList.ts
  • apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts
  • apps/meteor/client/views/marketplace/hooks/useApps.ts
  • apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx
  • apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts
  • apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts
  • apps/meteor/client/views/teams/contextualBar/channels/TeamsChannelsWithData.tsx
📚 Learning: 2025-11-27T17:56:26.050Z
Learnt from: MartinSchoeler
Repo: RocketChat/Rocket.Chat PR: 37557
File: apps/meteor/client/views/admin/ABAC/AdminABACRooms.tsx:115-116
Timestamp: 2025-11-27T17:56:26.050Z
Learning: In Rocket.Chat, the GET /v1/abac/rooms endpoint (implemented in ee/packages/abac/src/index.ts) only returns rooms where abacAttributes exists and is not an empty array (query: { abacAttributes: { $exists: true, $ne: [] } }). Therefore, in components consuming this endpoint (like AdminABACRooms.tsx), room.abacAttributes is guaranteed to be defined for all returned rooms, and optional chaining before calling array methods like .join() is sufficient without additional null coalescing.

Applied to files:

  • apps/meteor/client/hooks/useRoomsList.ts
  • apps/meteor/client/views/omnichannel/departments/EditDepartment.tsx
  • apps/meteor/client/views/teams/contextualBar/channels/hooks/useTeamsChannelList.ts
  • apps/meteor/client/views/room/ImageGallery/hooks/useImagesList.ts
  • apps/meteor/client/lib/queryKeys.ts
  • apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts
  • apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useFilesList.ts
📚 Learning: 2025-10-06T20:30:45.540Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 37152
File: packages/apps-engine/tests/test-data/storage/storage.ts:101-122
Timestamp: 2025-10-06T20:30:45.540Z
Learning: In `packages/apps-engine/tests/test-data/storage/storage.ts`, the stub methods (updatePartialAndReturnDocument, updateStatus, updateSetting, updateAppInfo, updateMarketplaceInfo) intentionally throw "Method not implemented." Tests using these methods must stub them using `SpyOn` from the test library rather than relying on actual implementations.

Applied to files:

  • apps/meteor/client/views/marketplace/hooks/useApps.spec.ts
  • apps/meteor/tests/mocks/client/marketplace.tsx
  • apps/meteor/client/views/marketplace/AppsPage/UnsupportedEmptyState.spec.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/teams/contextualBar/channels/hooks/useTeamsChannelList.ts
  • apps/meteor/client/lib/queryKeys.ts
  • apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts
  • apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts
📚 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/teams/contextualBar/channels/hooks/useTeamsChannelList.ts
  • apps/meteor/client/lib/queryKeys.ts
  • apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts
  • apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts
📚 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/teams/contextualBar/channels/hooks/useTeamsChannelList.ts
  • apps/meteor/client/views/room/ImageGallery/hooks/useImagesList.ts
  • apps/meteor/client/lib/queryKeys.ts
📚 Learning: 2025-11-17T15:07:13.273Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 37398
File: packages/fuselage-ui-kit/src/surfaces/FuselageSurfaceRenderer.tsx:357-363
Timestamp: 2025-11-17T15:07:13.273Z
Learning: In packages/fuselage-ui-kit/src/surfaces/FuselageSurfaceRenderer.tsx, IconElement is a presentational, non-actionable element that does not require wrapping in AppIdProvider, similar to plain_text and mrkdwn renderers. Only actionable elements (those with actions, actionId, or interactive behavior) should be wrapped in AppIdProvider.

Applied to files:

  • apps/meteor/client/components/RoomIcon/OmnichannelRoomIcon/OmnichannelAppSourceRoomIcon.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/marketplace/AppsPage/AppsPageContentBody.tsx
  • apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts
  • apps/meteor/client/views/marketplace/hooks/useApps.ts
  • apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx
📚 Learning: 2025-12-18T15:18:31.688Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 37773
File: apps/meteor/client/views/mediaCallHistory/MediaCallHistoryInternal.tsx:24-34
Timestamp: 2025-12-18T15:18:31.688Z
Learning: In apps/meteor/client/views/mediaCallHistory/MediaCallHistoryInternal.tsx, for internal call history items, the item.contactId is guaranteed to always match either the caller.id or callee.id in the call data, so the contact resolution in getContact will never result in undefined.

Applied to files:

  • apps/meteor/client/views/omnichannel/contactInfo/tabs/ContactInfoHistory/ContactInfoHistoryMessages.tsx
  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts
  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessagesList.tsx
📚 Learning: 2025-11-19T18:20:37.116Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 37419
File: apps/meteor/server/services/media-call/service.ts:141-141
Timestamp: 2025-11-19T18:20:37.116Z
Learning: In apps/meteor/server/services/media-call/service.ts, the sendHistoryMessage method should use call.caller.id or call.createdBy?.id as the message author, not call.transferredBy?.id. Even for transferred calls, the message should appear in the DM between the two users who are calling each other, not sent by the person who transferred the call.

Applied to files:

  • apps/meteor/client/views/omnichannel/contactInfo/tabs/ContactInfoHistory/ContactInfoHistoryMessages.tsx
  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts
  • apps/meteor/client/views/room/contextualBar/Threads/hooks/useThreadsList.ts
  • apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessagesList.tsx
📚 Learning: 2025-11-07T14:50:33.544Z
Learnt from: KevLehman
Repo: RocketChat/Rocket.Chat PR: 37423
File: packages/i18n/src/locales/en.i18n.json:18-18
Timestamp: 2025-11-07T14:50:33.544Z
Learning: Rocket.Chat settings: in apps/meteor/ee/server/settings/abac.ts, the Abac_Cache_Decision_Time_Seconds setting uses invalidValue: 0 as the fallback when ABAC is unlicensed. With a valid license, admins can still set the value to 0 to intentionally disable the ABAC decision cache.

Applied to files:

  • apps/meteor/client/views/marketplace/hooks/useApps.ts
📚 Learning: 2025-11-19T12:32:29.696Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 37547
File: packages/i18n/src/locales/en.i18n.json:634-634
Timestamp: 2025-11-19T12:32:29.696Z
Learning: Repo: RocketChat/Rocket.Chat
Context: i18n workflow
Learning: In this repository, new translation keys should be added to packages/i18n/src/locales/en.i18n.json only; other locale files are populated via the external translation pipeline and/or fall back to English. Do not request adding the same key to all locale files in future reviews.

Applied to files:

  • apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.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/marketplace/AppsPage/UnsupportedEmptyState.spec.tsx
🧬 Code graph analysis (21)
apps/meteor/client/views/marketplace/AppsPage/PrivateEmptyState.spec.tsx (1)
packages/mock-providers/src/index.ts (1)
  • mockAppRoot (3-3)
apps/meteor/client/hooks/useRoomsList.ts (1)
apps/meteor/client/lib/queryKeys.ts (1)
  • roomsQueryKeys (13-38)
apps/meteor/client/views/omnichannel/departments/EditDepartment.tsx (2)
packages/ui-contexts/src/index.ts (1)
  • useTranslation (81-81)
apps/meteor/client/hooks/useRoomsList.ts (1)
  • useRoomsList (6-41)
apps/meteor/client/views/teams/contextualBar/channels/hooks/useTeamsChannelList.ts (1)
apps/meteor/client/lib/queryKeys.ts (1)
  • teamsQueryKeys (125-134)
apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFilesWithData.tsx (1)
apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useFilesList.ts (1)
  • useFilesList (10-80)
apps/meteor/client/components/RoomIcon/OmnichannelRoomIcon/OmnichannelAppSourceRoomIcon.tsx (1)
apps/meteor/client/components/RoomIcon/OmnichannelRoomIcon/context/OmnichannelRoomIconContext.tsx (1)
  • useOmnichannelRoomIcon (11-15)
apps/meteor/client/views/room/ImageGallery/hooks/useImagesList.ts (2)
apps/meteor/client/meteor/minimongo/Cursor.ts (1)
  • count (277-283)
apps/meteor/client/lib/queryKeys.ts (1)
  • roomsQueryKeys (13-38)
apps/meteor/client/providers/AppsProvider.tsx (1)
apps/meteor/client/contexts/AppsContext.tsx (1)
  • AppsContext (29-29)
apps/meteor/client/views/omnichannel/hooks/useCannedResponseList.ts (1)
apps/meteor/client/lib/queryKeys.ts (1)
  • cannedResponsesQueryKeys (45-48)
apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/WrapCannedResponseList.tsx (1)
apps/meteor/client/views/omnichannel/hooks/useCannedResponseList.ts (1)
  • useCannedResponseList (7-59)
apps/meteor/client/views/omnichannel/contactInfo/tabs/ContactInfoHistory/ContactInfoHistoryMessages.tsx (2)
apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts (1)
  • useHistoryMessageList (16-59)
packages/ui-client/src/components/Contextualbar/index.ts (1)
  • ContextualbarEmptyContent (34-34)
apps/meteor/client/views/marketplace/hooks/useLogs.ts (1)
apps/meteor/client/lib/queryKeys.ts (1)
  • marketplaceQueryKeys (165-184)
apps/meteor/tests/mocks/client/marketplace.tsx (1)
apps/meteor/client/contexts/AppsContext.tsx (1)
  • AppsContext (29-29)
apps/meteor/client/views/room/contextualBar/Discussions/useDiscussionsList.ts (2)
apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts (1)
  • useInfiniteMessageQueryUpdates (8-108)
apps/meteor/client/lib/queryKeys.ts (1)
  • roomsQueryKeys (13-38)
apps/meteor/client/views/teams/contextualBar/channels/TeamsChannels.tsx (1)
packages/livechat/src/store/index.tsx (1)
  • Dispatch (209-209)
apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx (3)
apps/meteor/client/contexts/AppsContext.tsx (1)
  • AppsContext (29-29)
apps/meteor/client/views/marketplace/hooks/useAppsReload.ts (1)
  • useAppsReload (6-11)
apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts (1)
  • useFilteredApps (15-130)
apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts (1)
apps/meteor/client/views/marketplace/hooks/useApps.ts (1)
  • useApps (76-180)
apps/meteor/client/views/teams/contextualBar/channels/TeamsChannelsWithData.tsx (1)
apps/meteor/client/views/teams/contextualBar/channels/hooks/useTeamsChannelList.ts (1)
  • useTeamsChannelList (15-59)
apps/meteor/client/views/marketplace/AppsPage/UnsupportedEmptyState.spec.tsx (1)
packages/mock-providers/src/index.ts (1)
  • mockAppRoot (3-3)
apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/useVideoConfList.ts (1)
apps/meteor/client/lib/queryKeys.ts (1)
  • videoConferenceQueryKeys (186-189)
apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessagesList.tsx (2)
apps/meteor/client/views/omnichannel/contactHistory/MessageList/useHistoryMessageList.ts (1)
  • useHistoryMessageList (16-59)
packages/ui-client/src/components/Contextualbar/index.ts (1)
  • ContextualbarEmptyContent (34-34)
🪛 Biome (2.1.2)
apps/meteor/client/providers/AppsProvider.tsx

[error] 11-11: Avoid passing children using a prop

The canonical way to pass children in React is to use JSX elements

(lint/correctness/noChildrenProp)

⏰ 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

@tassoevan tassoevan requested a review from gabriellsh January 15, 2026 21:48
@ggazzo ggazzo added the stat: QA assured Means it has been tested and approved by a company insider label Jan 15, 2026
@dionisio-bot dionisio-bot bot added the stat: ready to merge PR tested and approved waiting for merge label Jan 15, 2026
@ggazzo ggazzo merged commit 58cd4ce into develop Jan 15, 2026
47 checks passed
@ggazzo ggazzo deleted the chore/async-state branch January 15, 2026 21:55
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.

4 participants