Skip to content

Conversation

@ricardogarim
Copy link
Contributor

@ricardogarim ricardogarim commented Dec 23, 2025

As per SUP-871, this PR fixes the issue where users with enterprise permissions (like view-engagement-dashboard, view-device-management, etc.) but without view-privileged-setting permission see "premium capability" popups despite the workspace having a valid Enterprise license.

Proposed changes (including videos or screenshots)

  • Updated useIsEnterprise() hook to use the existing license:isEnterprise Meteor method instead of relying on /v1/licenses.info which returns license: undefined for non-privileged users
  • Added isPending check in useUpsellActions to prevent race condition where popup shows before license data loads

Issue(s)

Steps to test or reproduce

  1. Apply an Enterprise license to your workspace with modules like engagement-dashboard, device-management, or custom-roles
  2. Create a custom role (e.g., "Manager") with enterprise permissions like view-engagement-dashboard or view-device-management
  3. Assign this role to a user, but ensure they do NOT have view-privileged-setting permission
  4. Log in as that user and navigate to /admin/engagement/users or /admin/permissions

Before: premium capability popup appears
After: no popup appears, user can access the enterprise features

Summary by CodeRabbit

  • Bug Fixes
    • Fixed premium capability popup appearing incorrectly when an enterprise license is active. The popup now properly respects license status and only displays when appropriate.

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

@changeset-bot
Copy link

changeset-bot bot commented Dec 23, 2025

🦋 Changeset detected

Latest commit: b3898fa

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 40 packages
Name Type
@rocket.chat/meteor Patch
@rocket.chat/core-typings Patch
@rocket.chat/rest-typings Patch
@rocket.chat/uikit-playground Patch
@rocket.chat/api-client Patch
@rocket.chat/apps Patch
@rocket.chat/core-services Patch
@rocket.chat/cron Patch
@rocket.chat/ddp-client Patch
@rocket.chat/fuselage-ui-kit Patch
@rocket.chat/gazzodown Patch
@rocket.chat/http-router Patch
@rocket.chat/livechat Patch
@rocket.chat/model-typings Patch
@rocket.chat/ui-avatar Patch
@rocket.chat/ui-client Patch
@rocket.chat/ui-contexts Patch
@rocket.chat/ui-voip Patch
@rocket.chat/web-ui-registration Patch
@rocket.chat/account-service Patch
@rocket.chat/authorization-service Patch
@rocket.chat/ddp-streamer Patch
@rocket.chat/omnichannel-transcript Patch
@rocket.chat/presence-service Patch
@rocket.chat/queue-worker Patch
@rocket.chat/abac Patch
@rocket.chat/federation-matrix Patch
@rocket.chat/license Patch
@rocket.chat/media-calls Patch
@rocket.chat/omnichannel-services Patch
@rocket.chat/pdf-worker Patch
@rocket.chat/presence Patch
rocketchat-services Patch
@rocket.chat/models Patch
@rocket.chat/network-broker Patch
@rocket.chat/omni-core-ee Patch
@rocket.chat/mock-providers Patch
@rocket.chat/ui-video-conf Patch
@rocket.chat/instance-status Patch
@rocket.chat/omni-core Patch

Not sure what this means? Click here to learn what changesets are.

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 23, 2025

Walkthrough

This pull request fixes a bug where the premium capability popup displayed incorrectly when an enterprise license was active. The fix refactors the enterprise status detection from direct license property access to a React Query-based method call and adds proper loading state handling to prevent premature upsell display.

Changes

Cohort / File(s) Summary
Changeset Documentation
.changeset/forty-carpets-sit.md
Added patch release entry documenting the fix for premium capability popup incorrectly displaying with active enterprise license.
Enterprise Status Detection
apps/meteor/client/hooks/useIsEnterprise.ts
Refactored data fetching from direct license property access (via useLicenseBase) to React Query-based approach using useMethod('license:isEnterprise') with stable queryKey and infinite staleTime. Public API signature unchanged.
Upsell Modal Logic
apps/meteor/client/components/GenericUpsellModal/hooks/useUpsellActions.ts
Updated shouldShowUpsell to depend on enterprise status isPending state, preventing upsell display while enterprise data is loading.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Suggested labels

stat: ready to merge, stat: QA assured

Suggested reviewers

  • aleksandernsilva
  • ggazzo

Poem

🐰 A modal that knew not when to show,
Now waits for enterprise status to flow,
With loading states checked and React Query's grace,
The upsell displays in its rightful place! ✨

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately and clearly describes the main fix: preventing the premium capability popup from appearing when an enterprise license is active.
Linked Issues check ✅ Passed The code changes directly address the bug described in SUP-871 by updating useIsEnterprise to use the license:isEnterprise method and adding isPending checks to prevent race conditions.
Out of Scope Changes check ✅ Passed All code changes (useIsEnterprise hook refactoring and useUpsellActions isPending logic) are directly focused on fixing the premium popup issue and are within the scope of the linked issue.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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 fix/upgrade-modal-window

📜 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 a8a84ab and b3898fa.

📒 Files selected for processing (3)
  • .changeset/forty-carpets-sit.md
  • apps/meteor/client/components/GenericUpsellModal/hooks/useUpsellActions.ts
  • apps/meteor/client/hooks/useIsEnterprise.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/components/GenericUpsellModal/hooks/useUpsellActions.ts
  • apps/meteor/client/hooks/useIsEnterprise.ts
🧠 Learnings (2)
📚 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/components/GenericUpsellModal/hooks/useUpsellActions.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/useIsEnterprise.ts
🧬 Code graph analysis (2)
apps/meteor/client/components/GenericUpsellModal/hooks/useUpsellActions.ts (1)
apps/meteor/client/hooks/useIsEnterprise.ts (1)
  • useIsEnterprise (5-16)
apps/meteor/client/hooks/useIsEnterprise.ts (1)
packages/ui-contexts/src/index.ts (1)
  • useMethod (55-55)
🔇 Additional comments (4)
.changeset/forty-carpets-sit.md (1)

1-5: LGTM!

The changeset format is correct and clearly documents the bug fix for the premium capability popup issue.

apps/meteor/client/components/GenericUpsellModal/hooks/useUpsellActions.ts (1)

16-17: Race condition fix looks correct.

The addition of the isPending check properly prevents the upsell modal from displaying before the enterprise license status has finished loading. The logic correctly gates the upsell on three conditions: data must be loaded (!isPending), and either the workspace is not enterprise OR the specific license module is missing.

Note: If the query fails, data will be undefined and the upsell will display (fail-safe behavior). This seems intentional but consider whether you want explicit error handling via the isError property from the query result.

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

1-3: LGTM!

Import statements are appropriate for the new React Query-based implementation.


5-16: Core fix is verified and solid.

The refactor from REST API (/v1/licenses.info) to the Meteor method license:isEnterprise correctly addresses the permission issue. The method is defined at apps/meteor/ee/app/license/server/methods.ts and simply returns License.hasValidLicense() with no explicit permission restrictions—it's callable by any authenticated user, eliminating the view-privileged-setting permission barrier.

Using React Query with staleTime: Infinity is appropriate since license status changes infrequently.


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.

@ricardogarim ricardogarim force-pushed the fix/upgrade-modal-window branch from edc1e59 to 05b90a1 Compare December 23, 2025 16:48
@codecov
Copy link

codecov bot commented Dec 23, 2025

Codecov Report

❌ Patch coverage is 92.85714% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 70.64%. Comparing base (a8a84ab) to head (b3898fa).

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop   #37950      +/-   ##
===========================================
- Coverage    70.66%   70.64%   -0.02%     
===========================================
  Files         3143     3143              
  Lines       108694   108704      +10     
  Branches     19572    19581       +9     
===========================================
- Hits         76810    76799      -11     
- Misses       29887    29902      +15     
- Partials      1997     2003       +6     
Flag Coverage Δ
e2e 60.18% <100.00%> (-0.02%) ⬇️
e2e-api 47.41% <ø> (-0.06%) ⬇️
unit 71.77% <83.33%> (-0.04%) ⬇️

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 1.1GiB 1.1GiB +11MiB
rocketchat 355MiB 345MiB +11MiB
omnichannel-transcript-service 132MiB 132MiB -2.1KiB
queue-worker-service 132MiB 132MiB -259B
ddp-streamer-service 126MiB 126MiB -1.5KiB
account-service 113MiB 113MiB -528B
authorization-service 111MiB 111MiB -134B
presence-service 111MiB 111MiB -639B

📊 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/15 22:28", "11/16 01:28", "11/17 23:50", "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/23 17:53 (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]
  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]
  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]
  line "omnichannel-transcript-service" [0.14, 0.14, 0.14, 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]
  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]
  line "queue-worker-service" [0.14, 0.14, 0.14, 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]
  line "rocketchat" [0.36, 0.36, 0.35, 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.35]
Loading

Statistics (last 28 days):

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

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

  • Tag: pr-37950
  • Baseline: develop
  • Timestamp: 2025-12-23 17:53:10 UTC
  • Historical data points: 28

Updated: Tue, 23 Dec 2025 17:53:10 GMT

@ricardogarim ricardogarim marked this pull request as ready for review December 23, 2025 18:49
@ricardogarim ricardogarim requested a review from a team as a code owner December 23, 2025 18:49
Copy link
Contributor

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

Choose a reason for hiding this comment

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

No issues found across 3 files

const { data } = useIsEnterprise();
const shouldShowUpsell = !data?.isEnterprise || !hasLicenseModule;
const { data, isPending } = useIsEnterprise();
const shouldShowUpsell = !isPending && (!data?.isEnterprise || !hasLicenseModule);
Copy link
Member

Choose a reason for hiding this comment

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

I would suggest using isSuccess instead, because it can be true with some error as well


export const useIsEnterprise = (): UseQueryResult<{ isEnterprise: boolean }> => {
return useLicenseBase({ select: (data) => ({ isEnterprise: Boolean(data?.license.license) }) });
const isEnterpriseMethod = useMethod('license:isEnterprise');
Copy link
Member

@dougfabris dougfabris Dec 23, 2025

Choose a reason for hiding this comment

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

since we're moving away from meteor I don't think this is a good move. Can't we create an api route to check if its enterprise or something like that since license.info doesn't allow non-permission access

@dougfabris dougfabris added this to the 8.1.0 milestone Dec 23, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants