Skip to content

[_]: feat/sidebar component#17

Merged
xabg2 merged 10 commits intomasterfrom
feature/mail-view
Mar 17, 2026
Merged

[_]: feat/sidebar component#17
xabg2 merged 10 commits intomasterfrom
feature/mail-view

Conversation

@xabg2
Copy link
Contributor

@xabg2 xabg2 commented Mar 16, 2026

Sidebar component has been added.

Summary by CodeRabbit

  • New Features

    • Redesigned sidebar with persistent collapse, navigation items, suite launcher and formatted storage display
    • Desktop app download flow with automatic OS detection and platform-specific redirects
    • New bytes formatting utility and public constants for service URLs
  • Bug Fixes / UX

    • Improved initialization error notifications and updated user subscription handling
  • Tests

    • Added unit/integration tests for downloads, OS detection and bytes formatting
  • Chores

    • Added AI review config and a small dependency for byte formatting

@xabg2 xabg2 requested a review from larryrider March 16, 2026 15:08
@xabg2 xabg2 self-assigned this Mar 16, 2026
@xabg2 xabg2 added the enhancement New feature or request label Mar 16, 2026
Base automatically changed from feature/setup-storage-service to master March 16, 2026 16:01
@coderabbitai
Copy link

coderabbitai bot commented Mar 16, 2026

Warning

Rate limit exceeded

@xabg2 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 5 minutes and 43 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: abad019d-0c3c-47dd-a924-d6a761ed4d24

📥 Commits

Reviewing files that changed from the base of the PR and between 9a2fb1c and c479579.

📒 Files selected for processing (4)
  • src/constants.ts
  • src/hooks/navigation/useSuiteLauncher.tsx
  • src/services/config/index.ts
  • src/store/queries/storage/storage.query.ts
📝 Walkthrough

Walkthrough

Adds AI review config and pretty-bytes; introduces SidenavWrapper and two navigation hooks; implements OS detection and downloadDesktopApp with tests and types; augments global ambient types; extends Redux to track userSubscription and updates tests and store setup.

Changes

Cohort / File(s) Summary
Configuration & Dependency
\.coderabbit.yaml, package.json
Adds AI-assisted review config and adds pretty-bytes dependency.
Global Types
src/globals.d.ts
Adds Vite reference and augments Window and Navigator with grecaptcha, opera, brave, and userAgentData typings.
Constants & Types
src/constants.ts, src/types/config/index.ts
Exports MEET_URL, SEND_URL, HUNDRED_TB; introduces OperatingSystem, PlatformMatcher, matcher arrays (PLATFORM_MATCHERS, UA_MATCHERS, APP_VERSION_MATCHERS) and ConfigKeys.
Sidenav UI & Layout
src/components/sidenav/SidenavWrapper.tsx, src/routes/layouts/SidebarAndHeaderLayout.tsx
Adds connected SidenavWrapper component (sessionStorage collapse state, storage metrics, suite launcher) and replaces hard-coded aside in layout with the component.
Navigation Hooks
src/hooks/navigation/useSidenavNavigation.tsx, src/hooks/navigation/useSuiteLauncher.tsx
Adds useSidenavNavigation to build left nav items and active state; adds useSuiteLauncher to construct suite array, open external suite URLs, and surface upgrade messages based on feature flags.
Auth & Redux
src/hooks/auth/useAuth.ts, src/store/slices/user/index.ts, src/test-utils/createTestStore.ts
Auth now dispatches userSubscription update; UserState gains userSubscription and setUserSubscription reducer; initialization failure shows a notification and routes to Welcome; test store wired to include storageQuery.
Config Service & OS Detection
src/services/config/index.ts, src/services/config/config.service.test.ts
Adds platform matchers and OS detection helpers (matchPlatform, getOperatingSystem, getDownloadAppUrl) and downloadDesktopApp(translate) which fetches platform-specific download URLs, opens them or shows translated error; adds tests covering OS detection and download URL resolution and error flows.
Utilities & Tests
src/utils/bytesToString.ts, src/utils/bytesToString.test.ts
Adds bytesToString wrapper around pretty-bytes with space/non-breaking-space options and unit tests for formatting cases.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Sidenav as SidenavWrapper
    participant Redux as Redux Store
    participant NavHook as useSidenavNavigation
    participant SuiteHook as useSuiteLauncher
    participant NavigationService

    User->>Sidenav: mount / interact
    activate Sidenav
    Sidenav->>Redux: read user, storage, features
    Redux-->>Sidenav: userState, storage metrics, feature flags
    Sidenav->>NavHook: build items
    NavHook-->>Sidenav: itemsNavigation
    Sidenav->>SuiteHook: build suiteArray
    SuiteHook-->>Sidenav: suiteArray
    Sidenav-->>User: render UI
    User->>NavHook: click item
    NavHook->>NavigationService: navigate(id)
    NavigationService-->>User: route change
    deactivate Sidenav
Loading
sequenceDiagram
    participant User
    participant Config as ConfigService
    participant OSDet as OS Detection
    participant API as Download API
    participant Notif as NotificationService
    participant Browser

    User->>Config: downloadDesktopApp(translate)
    activate Config
    Config->>OSDet: getOperatingSystem()
    OSDet->>OSDet: evaluate userAgentData / userAgent / appVersion / opera
    OSDet-->>Config: OperatingSystem
    Config->>API: getDownloadAppUrl()
    API-->>Config: platform-specific URL or null
    alt URL found
        Config->>Browser: window.open(url)
        Browser-->>User: download initiated
    else No URL / Unknown OS
        Config->>Notif: show(translated error, ToastType.Error)
        Notif-->>User: error toast
    end
    deactivate Config
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hopped in with a sidenav bright,
Suites and downloads set to flight,
Bytes made pretty, types defined,
Subscriptions tracked, OS aligned,
A tiny rabbit cheers—what a sight! ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

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.
Title check ❓ Inconclusive The title '[]: feat/sidebar component' is vague and uses a placeholder prefix '[]:' that doesn't convey meaningful information, though it does reference the sidebar component which is a real part of the changeset. Improve the title to remove the placeholder prefix and be more specific about the main change, such as 'Add sidebar navigation component with suite launcher integration' or similar.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/mail-view
📝 Coding Plan
  • Generate coding plan for human review comments

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.

@coderabbitai
Copy link

coderabbitai bot commented Mar 16, 2026

✅ Actions performed

Review triggered.

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

Copy link

@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

🧹 Nitpick comments (3)
src/hooks/navigation/useSidenavNavigation.tsx (2)

32-46: Consider swapping icons for Drafts and Sent.

PaperPlaneTiltIcon (paper airplane) semantically represents "sent" mail, while FileIcon is more neutral. Currently, Drafts uses the paper airplane and Sent uses the file icon, which seems inverted.

♻️ Suggested icon swap
       {
         isActive: isActiveButton('/drafts'),
         label: translate('sidebar.drafts'),
-        icon: PaperPlaneTiltIcon,
+        icon: FileIcon,
         iconDataCy: 'sideNavDraftsIcon',
         isVisible: true,
         onClick: () => onSidenavItemClick(AppView.Drafts),
       },
       {
         isActive: isActiveButton('/sent'),
         label: translate('sidebar.sent'),
-        icon: FileIcon,
+        icon: PaperPlaneTiltIcon,
         iconDataCy: 'sideNavSentIcon',
         isVisible: true,
         onClick: () => onSidenavItemClick(AppView.Sent),
       },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/hooks/navigation/useSidenavNavigation.tsx` around lines 32 - 46, The
Drafts and Sent items use inverted icons; swap PaperPlaneTiltIcon and FileIcon
so Drafts uses FileIcon and Sent uses PaperPlaneTiltIcon: update the objects
where isActiveButton('/drafts') and onSidenavItemClick(AppView.Drafts) to
reference FileIcon, and where isActiveButton('/sent') and
onSidenavItemClick(AppView.Sent) to reference PaperPlaneTiltIcon (leave label,
iconDataCy, isVisible, isActive and onClick unchanged).

15-20: Unnecessary singleton in dependency array.

NavigationService.instance is a singleton that doesn't change between renders. Including it in the dependency array is unnecessary and may trigger ESLint warnings.

♻️ Proposed fix
   const onSidenavItemClick = useCallback(
     (path: AppView) => {
       NavigationService.instance.navigate({ id: path });
     },
-    [NavigationService.instance],
+    [],
   );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/hooks/navigation/useSidenavNavigation.tsx` around lines 15 - 20, The
dependency array for the useCallback on onSidenavItemClick incorrectly includes
the singleton NavigationService.instance; remove it and replace with an empty
array (or capture the stable navigate function outside the callback) so the
callback does not re-create on each render—e.g., reference
NavigationService.instance.navigate inside onSidenavItemClick and change the
dependency array from [NavigationService.instance] to [] (or to [navigate] if
you first assign const navigate = NavigationService.instance.navigate).
src/services/config/index.ts (1)

63-68: Deprecated API usage is acceptable for fallback detection.

The static analysis flags navigator.vendor, navigator.appVersion, and window.opera as deprecated. However, these are used intentionally as fallbacks when userAgentData isn't available (older browsers). This is a reasonable trade-off for broader compatibility.

Consider adding a brief comment explaining the fallback chain for future maintainers.

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

In `@src/services/config/index.ts` around lines 63 - 68, Add a short explanatory
comment above the UA fallback logic describing the intentional use of deprecated
properties as legacy fallbacks: note that navigator.userAgent, navigator.vendor,
window.opera and navigator.appVersion are only used when userAgentData is
unavailable to maximize compatibility; reference the matchPlatform function and
UA_MATCHERS/APP_VERSION_MATCHERS so future maintainers understand the fallback
chain and rationale for keeping these deprecated API accesses.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/sidenav/SidenavWrapper.tsx`:
- Around line 80-83: The SidenavWrapper component already reads Redux via
useAppSelector, so remove the redundant connect HOC and its mapState mapping
(the export default connect((state: RootState) => ({ user: state.user.user,
userTier: state.user.userTier }))(SidenavWrapper);). Replace with a plain export
default SidenavWrapper and remove the unused mapped props and any now-unused
imports (e.g., connect and RootState) to avoid dead code; keep the component
body and useAppSelector calls as-is.
- Line 70: In SidenavWrapper.tsx, the percentage calculation "percentage:
Math.min((planUsage / planLimit) * 100, 100)" can produce Infinity when
planLimit is 0; replace it with a guarded calculation that uses a safe
denominator (e.g., const safeLimit = planLimit > 0 ? planLimit : 1) or an inline
ternary and then compute percentage as Math.min((planUsage / safeLimit) * 100,
100); ensure the guard uses planLimit > 0 so negative/zero limits don't divide
and the UI shows a bounded percentage.

In `@src/globals.d.ts`:
- Line 6: The execute function type in globals.d.ts uses an invalid anonymous
destructured parameter "{ action: string }"; update the signature for the
execute symbol to use a named second parameter with an object type (e.g., params
or options) so it reads something like execute: (siteKey: string, params: {
action: string }) => Promise<string>, ensuring the parameter name replaces the
invalid inline brace syntax.

In `@src/hooks/navigation/useSidenavNavigation.tsx`:
- Line 13: The isActiveButton callback is passing arguments to matchPath in the
wrong order; swap them so the pattern comes first and the pathname second.
Update the useCallback line in isActiveButton to call matchPath(path, pathname)
(keeping the !! cast and the [pathname] dependency) so route matching uses the
correct signature of matchPath(pattern, pathname).

In `@src/hooks/navigation/useSuiteLauncher.tsx`:
- Around line 30-33: The two alert() calls in useSuiteLauncher (using
suite.upgradeTitle and suite.upgradeDescription) should be replaced with your
app's upgrade modal/dialog flow: remove the alert(...) lines and instead invoke
the existing modal API or upgrade action (e.g., call openUpgradeModal /
showUpgradeDialog / dispatch openModal with a payload containing
suite.upgradeTitle and suite.upgradeDescription) so the UI shows a styled modal
with title, description and action buttons; ensure you import the modal helper
or use the existing context/hook used elsewhere for opening modals and pass the
suite data and any callbacks (confirm/cancel) to match the current upgrade UX.

In `@src/services/config/index.ts`:
- Around line 71-90: getDownloadAppUrl currently performs fetch and JSON.parse
without guards; wrap the network + parse steps in a try/catch, check
fetchDownloadResponse.ok before calling .json(), and throw a descriptive Error
(including status and response text when non-ok or when JSON parsing fails) so
callers like downloadDesktopApp can surface a notification; keep existing
fallback URLs and the getOperatingSystem switch, but return null only for
unknown OS and rethrow or propagate errors for network/parse failures so they
are handled upstream.

In `@src/utils/bytesToString.test.ts`:
- Around line 9-12: The test for bytesToString incorrectly passes only {
nonBreakingSpace: true } so no visible change occurs; update the test to enable
space as well and assert the actual non-breaking space character: call
bytesToString(1024 * 1024 * 1024, { space: true, nonBreakingSpace: true }) and
expect the result to include the U+00A0 character (e.g. '1.07\u00A0GB') to
verify the nonBreakingSpace behavior.

---

Nitpick comments:
In `@src/hooks/navigation/useSidenavNavigation.tsx`:
- Around line 32-46: The Drafts and Sent items use inverted icons; swap
PaperPlaneTiltIcon and FileIcon so Drafts uses FileIcon and Sent uses
PaperPlaneTiltIcon: update the objects where isActiveButton('/drafts') and
onSidenavItemClick(AppView.Drafts) to reference FileIcon, and where
isActiveButton('/sent') and onSidenavItemClick(AppView.Sent) to reference
PaperPlaneTiltIcon (leave label, iconDataCy, isVisible, isActive and onClick
unchanged).
- Around line 15-20: The dependency array for the useCallback on
onSidenavItemClick incorrectly includes the singleton
NavigationService.instance; remove it and replace with an empty array (or
capture the stable navigate function outside the callback) so the callback does
not re-create on each render—e.g., reference NavigationService.instance.navigate
inside onSidenavItemClick and change the dependency array from
[NavigationService.instance] to [] (or to [navigate] if you first assign const
navigate = NavigationService.instance.navigate).

In `@src/services/config/index.ts`:
- Around line 63-68: Add a short explanatory comment above the UA fallback logic
describing the intentional use of deprecated properties as legacy fallbacks:
note that navigator.userAgent, navigator.vendor, window.opera and
navigator.appVersion are only used when userAgentData is unavailable to maximize
compatibility; reference the matchPlatform function and
UA_MATCHERS/APP_VERSION_MATCHERS so future maintainers understand the fallback
chain and rationale for keeping these deprecated API accesses.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a7739db7-e09f-430e-95c1-5429265c61f3

📥 Commits

Reviewing files that changed from the base of the PR and between a3cae96 and b131b70.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (17)
  • .coderabbit.yaml
  • package.json
  • src/components/notifications/NotificationToast.tsx
  • src/components/sidenav/SidenavWrapper.tsx
  • src/constants.ts
  • src/globals.d.ts
  • src/hooks/auth/useAuth.ts
  • src/hooks/navigation/useSidenavNavigation.tsx
  • src/hooks/navigation/useSuiteLauncher.tsx
  • src/routes/layouts/SidebarAndHeaderLayout.tsx
  • src/services/config/config.service.test.ts
  • src/services/config/index.ts
  • src/store/slices/user/index.ts
  • src/test-utils/createTestStore.ts
  • src/types/config/index.ts
  • src/utils/bytesToString.test.ts
  • src/utils/bytesToString.ts

Copy link

@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

♻️ Duplicate comments (1)
src/hooks/navigation/useSuiteLauncher.tsx (1)

22-31: ⚠️ Potential issue | 🟠 Major

Locked suite clicks are silent; disabled path should trigger the upgrade flow.

openSuite is a no-op when enabled is false, so locked suites currently provide no feedback even though upgradeTitle/upgradeDescription are passed everywhere.

💡 Suggested direction
   const openSuite = (suite: {
     enabled: boolean;
     onOpenSuite: () => void;
     upgradeTitle: string;
     upgradeDescription: string;
+    onUpgrade: (payload: { title: string; description: string }) => void;
   }) => {
     if (suite.enabled) {
       suite.onOpenSuite();
+    } else {
+      suite.onUpgrade({
+        title: suite.upgradeTitle,
+        description: suite.upgradeDescription,
+      });
     }
   };

Wire onUpgrade to your existing modal/dialog action used elsewhere in the app.

Also applies to: 46-51, 72-82, 89-96, 103-110

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

In `@src/hooks/navigation/useSuiteLauncher.tsx` around lines 22 - 31, The
openSuite handler currently does nothing for disabled suites; update openSuite
to call suite.onOpenSuite() when suite.enabled is true, and when false call a
provided upgrade action (e.g., suite.onUpgrade(...) or a common
showUpgradeModal) passing suite.upgradeTitle and suite.upgradeDescription so
locked suites trigger the upgrade flow; apply the same change to the other
similar handlers in this file (the other open* functions around the file) so all
paths wire disabled suites to the upgrade modal/action instead of being silent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/hooks/navigation/useSuiteLauncher.tsx`:
- Around line 47-53: The code reads nested feature flags like
userFeatures?.[Service.Meet].enabled which can throw if the service entry is
missing; update all such accesses (e.g., in useSuiteLauncher.tsx where
userFeatures, Service.Meet and isLocked are used) to guard the property access
with optional chaining before .enabled (e.g.,
userFeatures?.[Service.Meet]?.enabled) and apply the same change to the other
occurrences called out (lines around the blocks using Service.* and .enabled) so
that enabled checks and isLocked assignments use ?.enabled rather than .enabled.

---

Duplicate comments:
In `@src/hooks/navigation/useSuiteLauncher.tsx`:
- Around line 22-31: The openSuite handler currently does nothing for disabled
suites; update openSuite to call suite.onOpenSuite() when suite.enabled is true,
and when false call a provided upgrade action (e.g., suite.onUpgrade(...) or a
common showUpgradeModal) passing suite.upgradeTitle and suite.upgradeDescription
so locked suites trigger the upgrade flow; apply the same change to the other
similar handlers in this file (the other open* functions around the file) so all
paths wire disabled suites to the upgrade modal/action instead of being silent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7c2b1fc6-bed4-4eb5-b766-553f5526d578

📥 Commits

Reviewing files that changed from the base of the PR and between b131b70 and 6930ea8.

📒 Files selected for processing (6)
  • src/components/sidenav/SidenavWrapper.tsx
  • src/globals.d.ts
  • src/hooks/navigation/useSidenavNavigation.tsx
  • src/hooks/navigation/useSuiteLauncher.tsx
  • src/services/config/index.ts
  • src/utils/bytesToString.test.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/hooks/navigation/useSidenavNavigation.tsx
  • src/services/config/index.ts
  • src/components/sidenav/SidenavWrapper.tsx
  • src/globals.d.ts
  • src/utils/bytesToString.test.ts

Copy link

@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.

♻️ Duplicate comments (2)
src/hooks/navigation/useSuiteLauncher.tsx (2)

104-104: ⚠️ Potential issue | 🟠 Major

Unsafe nested access can throw for missing Cleaner feature key.

Line 104 uses userFeatures?.[Service.Cleaner].enabled without guarding the service entry itself. If Cleaner is absent, this can throw at runtime.

Safe access fix
-          enabled: userFeatures?.[Service.Cleaner].enabled ?? false,
+          enabled: userFeatures?.[Service.Cleaner]?.enabled ?? false,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/hooks/navigation/useSuiteLauncher.tsx` at line 104, The current
expression reads the Cleaner service entry then .enabled directly, which can
throw if userFeatures lacks the Service.Cleaner key; update the initializer for
the enabled flag in useSuiteLauncher to safely guard the service object (e.g.,
check that userFeatures?.[Service.Cleaner] exists before reading .enabled) and
fall back to false — locate the enabled: assignment referencing Service.Cleaner
in useSuiteLauncher.tsx and replace it with a guarded access that uses optional
chaining or an existence check on the service entry before reading .enabled,
with a final nullish/default fallback of false.

22-31: ⚠️ Potential issue | 🟠 Major

Locked suites currently fail silently instead of triggering upgrade UX.

openSuite ignores upgradeTitle and upgradeDescription when disabled, so clicking locked suites does nothing (Line 22–31). This leaves the upgrade path unimplemented.

Suggested fix
   const openSuite = (suite: {
     enabled: boolean;
     onOpenSuite: () => void;
     upgradeTitle: string;
     upgradeDescription: string;
   }) => {
     if (suite.enabled) {
       suite.onOpenSuite();
+      return;
     }
+
+    // Wire this into the existing app modal/dialog flow.
+    // Example:
+    // openUpgradeModal({
+    //   title: suite.upgradeTitle,
+    //   description: suite.upgradeDescription,
+    // });
   };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/hooks/navigation/useSuiteLauncher.tsx` around lines 22 - 31, openSuite
currently no-ops for locked suites; change it so when suite.enabled is false it
triggers the upgrade UX using the suite.upgradeTitle and
suite.upgradeDescription instead of doing nothing. Specifically, in the
openSuite function check if (!suite.enabled) and call your app's upgrade
modal/flow (e.g., showUpgradeModal or openUpgradePrompt) passing
suite.upgradeTitle and suite.upgradeDescription; keep the existing behavior of
calling suite.onOpenSuite() when enabled. Ensure you reference openSuite,
suite.enabled, suite.onOpenSuite, suite.upgradeTitle and
suite.upgradeDescription when implementing the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/hooks/navigation/useSuiteLauncher.tsx`:
- Line 104: The current expression reads the Cleaner service entry then .enabled
directly, which can throw if userFeatures lacks the Service.Cleaner key; update
the initializer for the enabled flag in useSuiteLauncher to safely guard the
service object (e.g., check that userFeatures?.[Service.Cleaner] exists before
reading .enabled) and fall back to false — locate the enabled: assignment
referencing Service.Cleaner in useSuiteLauncher.tsx and replace it with a
guarded access that uses optional chaining or an existence check on the service
entry before reading .enabled, with a final nullish/default fallback of false.
- Around line 22-31: openSuite currently no-ops for locked suites; change it so
when suite.enabled is false it triggers the upgrade UX using the
suite.upgradeTitle and suite.upgradeDescription instead of doing nothing.
Specifically, in the openSuite function check if (!suite.enabled) and call your
app's upgrade modal/flow (e.g., showUpgradeModal or openUpgradePrompt) passing
suite.upgradeTitle and suite.upgradeDescription; keep the existing behavior of
calling suite.onOpenSuite() when enabled. Ensure you reference openSuite,
suite.enabled, suite.onOpenSuite, suite.upgradeTitle and
suite.upgradeDescription when implementing the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e92ce06b-25b7-463c-87bf-8f71671e4397

📥 Commits

Reviewing files that changed from the base of the PR and between 6930ea8 and 9a2fb1c.

📒 Files selected for processing (1)
  • src/hooks/navigation/useSuiteLauncher.tsx

@sonarqubecloud
Copy link

@xabg2 xabg2 merged commit 157b5fa into master Mar 17, 2026
5 checks passed
@xabg2 xabg2 deleted the feature/mail-view branch March 17, 2026 08:34
@coderabbitai coderabbitai bot mentioned this pull request Mar 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants