Skip to content

Sentry#1631

Merged
prxt6529 merged 12 commits intomainfrom
sentry-04-12
Dec 17, 2025
Merged

Sentry#1631
prxt6529 merged 12 commits intomainfrom
sentry-04-12

Conversation

@prxt6529
Copy link
Copy Markdown
Collaborator

@prxt6529 prxt6529 commented Dec 4, 2025

Summary by CodeRabbit

Release Notes

  • New Features

    • Added resilient push notification registration for iOS with automatic retry logic and improved error handling.
  • Improvements

    • Enhanced error detection and telemetry for push notification system operations.
    • Strengthened validation of notification actions against user profiles.

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

Signed-off-by: prxt6529 <prxt@6529.io>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 4, 2025

Walkthrough

Enhanced iOS push notification registration with exponential backoff retry logic, delegate error detection, and restructured initialization orchestration. Added guarded action handling, expanded telemetry, broadened instrumentation filename exceptions, and extended error pattern detection. Updated corresponding test suite to cover new initialization flow.

Changes

Cohort / File(s) Change Summary
Notification registration & error handling
components/notifications/NotificationsContext.tsx
Added registerWithRetry() with exponential backoff and delegate-error detection; introduced DELEGATE_ERROR_PATTERNS and isDelegateError; restructured initialization flow integrating isActive state with delayed init; added guarded handlePushNotificationAction with profile validation; introduced NotificationData type and typed resolveRedirectUrl; enhanced error telemetry and Sentry captures throughout lifecycle.
Instrumentation filtering
instrumentation-client.ts
Expanded filenameExceptions array from ["inpage.js"] to ["inpage.js", "injectLeap.js", "inject.chrome"] affecting frame filtering in beforeSend logic.
Error pattern detection
utils/error-sanitizer.ts
Added regex pattern /database\s+connection\s+is\s+closing/i to IndexedDB error pattern list.
Notification initialization tests
__tests__/components/notifications/NotificationsContext.test.tsx
Introduced mockIsActive flag with getter on useCapacitor mock; added mock for stable-device-id returning deterministic value; created test suite "NotificationsContext initialization" with tests for inactive/active states verifying appropriate listener and registration calls; updated existing tests for new initialization flow.

Sequence Diagram

sequenceDiagram
    participant App as App/Provider
    participant Context as NotificationsContext
    participant Capacitor as Capacitor
    participant PushNotifications as iOS PushNotifications
    participant Sentry as Error Telemetry

    App->>Context: Mount & Initialize (isActive=true)
    
    Context->>Capacitor: Check isActive state
    Capacitor-->>Context: isActive response
    
    alt isActive === true
        Context->>PushNotifications: removeAllListeners()
        PushNotifications-->>Context: Success
        
        Context->>PushNotifications: addListener (registration, action, error)
        PushNotifications-->>Context: Listeners attached
        
        Context->>PushNotifications: requestPermissions()
        PushNotifications-->>Context: Permissions granted/denied
        
        Context->>Context: registerWithRetry(maxRetries=3)
        
        loop Retry Logic (exponential backoff)
            Context->>PushNotifications: register()
            alt Registration Success
                PushNotifications-->>Context: Registration OK
                Context->>Context: Break retry loop
            else Delegate Error Detected
                Context->>Sentry: Capture delegate error
                Sentry-->>Context: Error logged
                Context->>Context: Wait & retry (2^attempt ms)
            else Non-delegate Error
                PushNotifications-->>Context: Error
                Context->>Sentry: Capture registration error
                Sentry-->>Context: Error logged
                Context->>Context: Rethrow (exhausted retries)
            end
        end
        
        Context->>App: Initialization complete
    else isActive === false
        Context-->>App: Skip initialization
    end
    
    opt Push action received
        PushNotifications->>Context: Trigger action handler
        Context->>Context: handlePushNotificationAction (validate profile)
        Context->>Context: resolveRedirectUrl(notificationData)
        Context->>App: Navigate to resolved URL
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Retry logic with exponential backoff: Verify correct backoff calculations, retry conditions, and exhaustion handling in registerWithRetry()
  • Delegate error detection pattern: Confirm DELEGATE_ERROR_PATTERNS regex accuracy and isDelegateError() implementation across iOS error scenarios
  • Initialization state orchestration: Review integration of isActive check, delayed initialization, and interaction with Capacitor lifecycle
  • Guarded action handling: Validate profile validation logic and resolveRedirectUrl typed parameter usage in handlePushNotificationAction()
  • Test coverage: Ensure new initialization tests adequately cover both active/inactive paths and mock state transitions

Possibly related PRs

  • Packages and sentry updates #1623: Modifies the same notifications initialization and registration flow in NotificationsContext.tsx, including listener setup, iOS delegate error handling, and push-action handler — directly overlaps with this PR's core logic.

Suggested reviewers

  • simo6529

Poem

🐰 Hops through the notification warren,
Retry logic's here by tomorrow!
iOS delegates, now with finesse,
Exponential backoff—no stress!
Leap forward, dear app, no sorrow! 🚀

Pre-merge checks and finishing touches

❌ 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%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'Sentry' is vague and does not clearly describe the main changes in the pull request, which include iOS push registration resilience, delegate error handling, redirect resolution, and error telemetry. Replace with a more descriptive title that captures the primary change, such as 'Add resilient iOS push registration with error handling and telemetry' or 'Enhance push notifications with retry logic and Sentry error tracking'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ 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 sentry-04-12

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.

Copy link
Copy Markdown

@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 (3)
components/notifications/NotificationsContext.tsx (3)

46-60: New isActive gating is reasonable; registrationAttemptRef currently unused state

The added isActive check in the useEffect guard makes sense to avoid initializing push while the app is inactive, and the dependency array correctly includes isActive so initialization can occur once the app becomes active.

registrationAttemptRef is only ever reset to 0 (in the effect and on successful registration) and never read or incremented, so at the moment it does not influence behavior. If you intended it for telemetry or conditional logic (e.g. to avoid repeated registration beyond a certain count), consider wiring it into registerWithRetry or downstream logging; otherwise, it could be removed to keep state minimal.


61-72: Rethrowing initialization errors will now produce unhandled rejections

By adding throw error; in both initializeNotifications and initializePushNotifications, any failure in the push setup path will now reject the async functions. Since initializeNotifications is invoked from the effect without await or a .catch(...), these rejections will surface as unhandled promise rejections at runtime.

If this is intentional so that global handlers/Sentry can capture the failures, the change is fine. If you instead want to log/report but avoid unhandled rejections impacting the runtime, you may want to catch at the call site and handle/report there rather than rethrowing.

Also applies to: 152-155


73-100: Retry helper logic is sound; consider minor robustness improvements

The new registerWithRetry helper correctly:

  • Bounds attempts by maxRetries.
  • Retries only for known delegate-related errors with exponential backoff and a cap.
  • Resurfaces non-matching errors (or final failures) immediately.

A couple of small points you may want to refine:

  • The delegate error detection is based on substring matches in the error message. This is pragmatic, but brittle across Capacitor/iOS versions; please verify on-device that the actual error messages do contain these substrings and keep in mind they may change between versions.
  • Since maxRetries is a parameter, you might eventually want to pass an explicit value from the call site or promote it to a named constant for easier tuning/experimentation.

Overall, the retry behavior and backoff math look correct and should help with transient iOS delegate timing issues.

Also applies to: 141-149

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 70776c8 and 8884684.

📒 Files selected for processing (3)
  • components/notifications/NotificationsContext.tsx (4 hunks)
  • instrumentation-client.ts (1 hunks)
  • utils/error-sanitizer.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version

**/*.{ts,tsx,js,jsx}: Replace <img> elements with <Image /> from next/image to satisfy @next/next/no-img-element ESLint rule
Use <Link href="/path"> from Next.js for internal navigation instead of plain HTML links to satisfy @next/next/no-html-link-for-pages ESLint rule

Files:

  • instrumentation-client.ts
  • utils/error-sanitizer.ts
  • components/notifications/NotificationsContext.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks)
Use framework APIs: internal links should use <Link>, images should use next/image, and adopt Next's ESLint rules (Core Web Vitals)

Files:

  • instrumentation-client.ts
  • utils/error-sanitizer.ts
  • components/notifications/NotificationsContext.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Must pass tsc --noEmit type checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") over React. namespace usage (React.useMemo, React.useRef, etc.)
If the react-hooks/exhaustive-deps lint rule is triggered: if the Effect only derives state, remove the Effect and compute during render; if listening to an external system and needing fresh props/state, wrap non-reactive logic in useEffectEvent

Files:

  • instrumentation-client.ts
  • utils/error-sanitizer.ts
  • components/notifications/NotificationsContext.tsx
@(instrumentation-client.ts|sentry.server.config.ts|sentry.edge.config.ts)

📄 CodeRabbit inference engine (AGENTS.md)

Errors should be filtered in the Sentry beforeSend callback; client-side filtering happens in instrumentation-client.ts, server-side in sentry.server.config.ts, and edge runtime in sentry.edge.config.ts

Files:

  • instrumentation-client.ts
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always add readonly before props in React components

Files:

  • components/notifications/NotificationsContext.tsx
🧠 Learnings (1)
📚 Learning: 2025-12-03T14:52:34.255Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.255Z
Learning: Applies to @(instrumentation-client.ts|sentry.server.config.ts|sentry.edge.config.ts) : Errors should be filtered in the Sentry `beforeSend` callback; client-side filtering happens in `instrumentation-client.ts`, server-side in `sentry.server.config.ts`, and edge runtime in `sentry.edge.config.ts`

Applied to files:

  • instrumentation-client.ts
🧬 Code graph analysis (1)
components/notifications/NotificationsContext.tsx (2)
components/auth/Auth.tsx (1)
  • useAuth (95-97)
components/waves/drops/wave-drops-all/utils/delay.ts (1)
  • delay (1-4)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (3)
utils/error-sanitizer.ts (1)

102-102: LGTM! The new IndexedDB error pattern is well-formed.

The pattern correctly identifies database connection closing errors with flexible whitespace handling and case-insensitive matching, consistent with the existing patterns in the array.

instrumentation-client.ts (1)

24-24: Filename exceptions correctly filter external wallet extension errors.

The additions of "injectLeap.js" and "inject.chrome" follow the established pattern of filtering errors from browser extension injection scripts outside developer control. These represent standard extension injection patterns:

  • inpage.js: MetaMask/web3 provider injection
  • injectLeap.js: Leap wallet extension injection
  • inject.chrome: Generic Chrome extension injection

The filtering logic correctly checks both frame.filename and frame.abs_path using string matching, and the beforeSend callback is the appropriate location per Sentry guidelines for client-side error filtering.

components/notifications/NotificationsContext.tsx (1)

3-10: Capacitor/notifications imports look correct and fully used

All newly added imports (ApiIdentity, useCapacitor, commonApiPost, Device, PushNotifications, related types) are used consistently in this module; no unused or missing symbols spotted.

Signed-off-by: prxt6529 <prxt@6529.io>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (4)
__tests__/components/notifications/NotificationsContext.test.tsx (2)

78-86: Redundant mock clearing in beforeEach.

jest.clearAllMocks() on line 82 already clears all mocks. The individual mockClear() calls on lines 83-85 are redundant.

   beforeEach(() => {
     mockIsActive = true;
-    const { PushNotifications } = require("@capacitor/push-notifications");
     jest.clearAllMocks();
-    PushNotifications.removeAllListeners.mockClear();
-    PushNotifications.addListener.mockClear();
-    PushNotifications.register.mockClear();
   });

124-139: Test is orphaned outside of any describe block.

This test (removes notifications when functions called) is placed outside the describe blocks, which affects test organization and reporting. Consider moving it into an appropriate describe block.

Consider wrapping this test in a describe block or moving it into one of the existing suites for better organization:

+describe("NotificationsContext notification removal", () => {
 it("removes notifications when functions called", async () => {
   // ... test body
 });
+});
components/notifications/NotificationsContext.tsx (2)

90-92: Props should be marked as readonly.

As per coding guidelines, React component props should be prefixed with readonly.

-export const NotificationsProvider: React.FC<{ children: React.ReactNode }> = ({
+export const NotificationsProvider: React.FC<{ readonly children: React.ReactNode }> = ({
   children,
 }) => {

308-308: Consider adding type definition for notification data.

Using any type reduces type safety. Consider defining an interface for expected notification data structure.

interface NotificationData {
  redirect?: keyof typeof redirectConfig;
  [key: string]: unknown;
}

const resolveRedirectUrl = (notificationData: NotificationData) => {
  // ...
};
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8884684 and 071a65b.

📒 Files selected for processing (2)
  • __tests__/components/notifications/NotificationsContext.test.tsx (3 hunks)
  • components/notifications/NotificationsContext.tsx (3 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version

**/*.{ts,tsx,js,jsx}: Replace <img> elements with <Image /> from next/image to satisfy @next/next/no-img-element ESLint rule
Use <Link href="/path"> from Next.js for internal navigation instead of plain HTML links to satisfy @next/next/no-html-link-for-pages ESLint rule

Files:

  • components/notifications/NotificationsContext.tsx
  • __tests__/components/notifications/NotificationsContext.test.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always add readonly before props in React components

Files:

  • components/notifications/NotificationsContext.tsx
  • __tests__/components/notifications/NotificationsContext.test.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks)
Use framework APIs: internal links should use <Link>, images should use next/image, and adopt Next's ESLint rules (Core Web Vitals)

Files:

  • components/notifications/NotificationsContext.tsx
  • __tests__/components/notifications/NotificationsContext.test.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Must pass tsc --noEmit type checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") over React. namespace usage (React.useMemo, React.useRef, etc.)
If the react-hooks/exhaustive-deps lint rule is triggered: if the Effect only derives state, remove the Effect and compute during render; if listening to an external system and needing fresh props/state, wrap non-reactive logic in useEffectEvent

Files:

  • components/notifications/NotificationsContext.tsx
  • __tests__/components/notifications/NotificationsContext.test.tsx
**/@(__tests__|*.test).{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Tests should live in __tests__/ or ComponentName.test.tsx; mock external dependencies and APIs in tests

Files:

  • __tests__/components/notifications/NotificationsContext.test.tsx
🧠 Learnings (2)
📚 Learning: 2025-12-03T14:52:34.255Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.255Z
Learning: Fix with modernization (no `// eslint-disable` unless explicitly instructed); prefer refactors aligned with React 19.2, React Compiler, and Next.js 16 conventions

Applied to files:

  • components/notifications/NotificationsContext.tsx
📚 Learning: 2025-12-03T14:52:34.255Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.255Z
Learning: Applies to **/@(__tests__|*.test).{ts,tsx} : Tests should live in `__tests__/` or `ComponentName.test.tsx`; mock external dependencies and APIs in tests

Applied to files:

  • __tests__/components/notifications/NotificationsContext.test.tsx
🧬 Code graph analysis (2)
components/notifications/NotificationsContext.tsx (3)
components/auth/Auth.tsx (1)
  • useAuth (95-97)
generated/models/ApiIdentity.ts (1)
  • ApiIdentity (17-169)
components/notifications/stable-device-id.ts (1)
  • getStableDeviceId (10-27)
__tests__/components/notifications/NotificationsContext.test.tsx (1)
components/notifications/NotificationsContext.tsx (1)
  • useNotificationsContext (334-342)
🪛 GitHub Check: SonarCloud Code Analysis
components/notifications/NotificationsContext.tsx

[failure] 132-132: Remove this use of the "void" operator.

See more on https://sonarcloud.io/project/issues?id=6529-Collections_6529seize-frontend&issues=AZroxOQXoxfpafohXKYH&open=AZroxOQXoxfpafohXKYH&pullRequest=1631

__tests__/components/notifications/NotificationsContext.test.tsx

[warning] 106-106: Remove this useless assignment to variable "result".

See more on https://sonarcloud.io/project/issues?id=6529-Collections_6529seize-frontend&issues=AZroxOUaoxfpafohXKYI&open=AZroxOUaoxfpafohXKYI&pullRequest=1631

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (8)
__tests__/components/notifications/NotificationsContext.test.tsx (2)

11-18: Good pattern for dynamic mock values.

Using a getter for isActive allows the mock to return different values per test. This is a clean approach for testing conditional behavior.


50-52: Deterministic mock for stable device ID.

Good addition to ensure consistent test behavior.

components/notifications/NotificationsContext.tsx (6)

24-27: Well-defined constants for retry configuration.

Good practice to extract magic numbers into named constants with clear intent.


29-39: Delegate error detection is well-implemented.

The pattern matching approach for iOS delegate errors is clean and maintainable.


41-67: Solid exponential backoff implementation.

The retry logic with exponential backoff and max delay cap is correctly implemented. The early return on success and re-throw on final failure are handled properly.


148-213: Well-structured initialization with proper error handling.

The function properly:

  • Cleans up existing listeners before adding new ones
  • Handles iOS-specific timing issues
  • Uses Sentry for error tracking
  • Re-throws errors for caller handling

225-239: Initialization effect with proper gating.

The use of initializationRef to prevent duplicate initializations for the same profile is a good pattern. Error handling with Sentry integration is appropriate.


280-306: Good error handling with Sentry integration.

The registration function properly catches errors, logs them, and reports to Sentry with appropriate tags.

Comment thread __tests__/components/notifications/NotificationsContext.test.tsx
Comment thread components/notifications/NotificationsContext.tsx Outdated
Signed-off-by: prxt6529 <prxt@6529.io>
Copy link
Copy Markdown

@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 (4)
components/notifications/NotificationsContext.tsx (4)

98-111: Simplify notification removal error handling and guard notification.data

Two small improvements here:

  1. Unreachable .catch on removeDeliveredNotifications
    removeDeliveredNotifications already wraps PushNotifications.removeDeliveredNotifications in a try/catch and never rethrows, so the .catch attached in handlePushNotificationAction will never see a rejection. Consider either:

    • Letting removeDeliveredNotifications propagate errors (no internal try/catch) and keep the .catch at the call site, or
    • Keeping the internal try/catch (and adding Sentry if desired) and removing the .catch from handlePushNotificationAction.

    This avoids having two overlapping error-handling strategies for the same operation.

  2. Defensive default for notification.data
    To avoid a hard crash if a push arrives without data populated, you can safely default it to an empty object:

  •  const notificationData = notification.data;
    
  •  const notificationData = notification.data ?? {};
     const notificationProfileId = notificationData.profile_id;
    
    
    That keeps the logic the same when `data` is present, but prevents property access on `undefined`.  
    
    
    
    
    

Also applies to: 113-154


156-221: Await registerPushNotification inside the registration listener

Inside initializePushNotifications, the registration listener is marked async but does not await registerPushNotification, which returns a Promise. Since registerPushNotification already catches and logs/Sentrys its own errors, awaiting it is safe and avoids an unnecessary floating promise:

-        await PushNotifications.addListener("registration", async (token) => {
-          registerPushNotification(
+        await PushNotifications.addListener("registration", async (token) => {
+          await registerPushNotification(
             stableDeviceId,
             deviceInfo,
             token.value,
             profile
           );
         });

This makes the callback’s async signature meaningful and keeps the flow easier to reason about.


223-231: Allow re-initialization after a failed notifications setup

The useEffect sets initializationRef.current = profileId before calling initializeNotifications. If initialization throws, the catch logs/Sentrys the error but leaves initializationRef.current set, so you won’t automatically retry for that profile (even if the failure was transient).

Consider resetting the ref on failure so future renders can re-attempt initialization:

-    if (isCapacitor && isActive && initializationRef.current !== profileId) {
-      initializationRef.current = profileId;
-      initializeNotifications(connectedProfile ?? undefined).catch((error) => {
-        console.error("Failed to initialize push notifications", error);
-        Sentry.captureException(error, {
-          tags: {
-            component: "NotificationsProvider",
-            operation: "initializeNotifications",
-          },
-        });
-      });
-    }
+    if (isCapacitor && isActive && initializationRef.current !== profileId) {
+      initializationRef.current = profileId;
+      initializeNotifications(connectedProfile ?? undefined).catch((error) => {
+        console.error("Failed to initialize push notifications", error);
+        Sentry.captureException(error, {
+          tags: {
+            component: "NotificationsProvider",
+            operation: "initializeNotifications",
+          },
+        });
+        initializationRef.current = null;
+      });
+    }

This keeps the “once per profileId” behavior on success but makes the system more resilient to transient initialization failures.

Also applies to: 233-247


249-261: Align wave/all delivered removal with the rest of the error and Sentry strategy

The helpers for removing wave-specific and all delivered notifications are currently less defensive than the rest of the file:

  • removeWaveDeliveredNotifications doesn’t catch errors from getDeliveredNotifications or removeDeliveredNotifications.
  • removeAllDeliveredNotifications catches errors but only logs to the console, without Sentry tagging.

To make these pathways consistent with your other Sentry-instrumented operations, you could wrap both in try/catch blocks and capture exceptions with tags:

   const removeWaveDeliveredNotifications = useCallback(
     async (waveId: string) => {
       if (isIos) {
-        const deliveredNotifications =
-          await PushNotifications.getDeliveredNotifications();
-        const waveNotifications = deliveredNotifications.notifications.filter(
-          (notification) => notification.data.wave_id === waveId
-        );
-        await removeDeliveredNotifications(waveNotifications);
+        try {
+          const deliveredNotifications =
+            await PushNotifications.getDeliveredNotifications();
+          const waveNotifications =
+            deliveredNotifications.notifications.filter(
+              (notification) => notification.data.wave_id === waveId
+            );
+          await removeDeliveredNotifications(waveNotifications);
+        } catch (error) {
+          console.error("Error removing wave delivered notifications", error);
+          Sentry.captureException(error, {
+            tags: {
+              component: "NotificationsProvider",
+              operation: "removeWaveDeliveredNotifications",
+            },
+          });
+        }
       }
     },
     [isIos, removeDeliveredNotifications]
   );
   const removeAllDeliveredNotifications = useCallback(async () => {
     if (isIos) {
       try {
         await PushNotifications.removeAllDeliveredNotifications();
       } catch (error) {
-        console.error("Error removing all delivered notifications", error);
+        console.error("Error removing all delivered notifications", error);
+        Sentry.captureException(error, {
+          tags: {
+            component: "NotificationsProvider",
+            operation: "removeAllDeliveredNotifications",
+          },
+        });
       }
     }
   }, [isIos]);

This brings these paths in line with the rest of your telemetry and reduces the chance of silent failures.

Also applies to: 263-271

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 071a65b and 82894ae.

📒 Files selected for processing (2)
  • __tests__/components/notifications/NotificationsContext.test.tsx (3 hunks)
  • components/notifications/NotificationsContext.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/components/notifications/NotificationsContext.test.tsx
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version

**/*.{ts,tsx,js,jsx}: Replace <img> elements with <Image /> from next/image to satisfy @next/next/no-img-element ESLint rule
Use <Link href="/path"> from Next.js for internal navigation instead of plain HTML links to satisfy @next/next/no-html-link-for-pages ESLint rule

Files:

  • components/notifications/NotificationsContext.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always add readonly before props in React components

Files:

  • components/notifications/NotificationsContext.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks)
Use framework APIs: internal links should use <Link>, images should use next/image, and adopt Next's ESLint rules (Core Web Vitals)

Files:

  • components/notifications/NotificationsContext.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Must pass tsc --noEmit type checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") over React. namespace usage (React.useMemo, React.useRef, etc.)
If the react-hooks/exhaustive-deps lint rule is triggered: if the Effect only derives state, remove the Effect and compute during render; if listening to an external system and needing fresh props/state, wrap non-reactive logic in useEffectEvent

Files:

  • components/notifications/NotificationsContext.tsx
🧬 Code graph analysis (1)
components/notifications/NotificationsContext.tsx (4)
components/waves/drops/wave-drops-all/utils/delay.ts (1)
  • delay (1-4)
components/auth/Auth.tsx (1)
  • useAuth (95-97)
generated/models/ApiIdentity.ts (1)
  • ApiIdentity (17-169)
components/notifications/stable-device-id.ts (1)
  • getStableDeviceId (10-27)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (3)
components/notifications/NotificationsContext.tsx (3)

3-12: Imports and client-only wiring look consistent

New imports for models, Capacitor plugins, Sentry, and router are all used correctly in a client component and match the usages below; no changes needed here.


24-67: Retry/backoff helper for iOS delegate errors looks solid

The delegate-pattern detection and bounded exponential backoff in registerWithRetry are correct and avoid unbounded retries while still handling transient delegate issues; the overall control flow (retry only on delegate errors, otherwise rethrow) is sound.


307-312: Sentry capture in registerPushNotification is appropriate

Adding Sentry capture with specific component and operation tags here gives you good visibility into backend registration failures without changing the behavior of the calling code; looks good.

Signed-off-by: prxt6529 <prxt@6529.io>
Copy link
Copy Markdown

@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)
components/notifications/NotificationsContext.tsx (2)

60-61: Redundant continue statement.

The continue at line 61 is unnecessary since it's the last statement before the end of the loop iteration. Removing it simplifies the code without changing behavior.

         await new Promise((resolve) => setTimeout(resolve, delay));
-        continue;
       }

325-349: Consider adding proper typing for notificationData.

Using any bypasses TypeScript's type safety. Defining an interface for the expected notification data structure would improve maintainability and catch potential issues at compile time.

+interface NotificationData {
+  redirect?: keyof typeof redirectConfig;
+  profile_id?: string;
+  path?: string;
+  handle?: string;
+  id?: string;
+  wave_id?: string;
+  drop_id?: string;
+  [key: string]: unknown;
+}
+
-const resolveRedirectUrl = (notificationData: any) => {
+const resolveRedirectUrl = (notificationData: NotificationData) => {
   const { redirect, ...params } = notificationData;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 82894ae and ff5d4e5.

📒 Files selected for processing (1)
  • components/notifications/NotificationsContext.tsx (3 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version

**/*.{ts,tsx,js,jsx}: Replace <img> elements with <Image /> from next/image to satisfy @next/next/no-img-element ESLint rule
Use <Link href="/path"> from Next.js for internal navigation instead of plain HTML links to satisfy @next/next/no-html-link-for-pages ESLint rule

Files:

  • components/notifications/NotificationsContext.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always add readonly before props in React components

Files:

  • components/notifications/NotificationsContext.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks)
Use framework APIs: internal links should use <Link>, images should use next/image, and adopt Next's ESLint rules (Core Web Vitals)

Files:

  • components/notifications/NotificationsContext.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Must pass tsc --noEmit type checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") over React. namespace usage (React.useMemo, React.useRef, etc.)
If the react-hooks/exhaustive-deps lint rule is triggered: if the Effect only derives state, remove the Effect and compute during render; if listening to an external system and needing fresh props/state, wrap non-reactive logic in useEffectEvent

Files:

  • components/notifications/NotificationsContext.tsx
🧬 Code graph analysis (1)
components/notifications/NotificationsContext.tsx (3)
components/auth/Auth.tsx (1)
  • useAuth (95-97)
generated/models/ApiIdentity.ts (1)
  • ApiIdentity (17-169)
components/notifications/stable-device-id.ts (1)
  • getStableDeviceId (10-27)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (5)
components/notifications/NotificationsContext.tsx (5)

113-146: LGTM!

The refactor to pass routerInstance and profileInstance as parameters rather than closing over them directly addresses potential stale closure concerns. The profile validation logic correctly prevents processing notifications meant for different profiles.


148-213: LGTM!

The initialization flow is well-structured: it removes all listeners before adding new ones to prevent duplication on re-initialization, adds comprehensive event handlers with Sentry telemetry, and properly propagates errors for the caller to handle.


225-240: LGTM!

The initialization effect correctly gates on isActive to ensure Capacitor is ready, uses initializationRef to deduplicate initializations for the same profile, and properly resets the ref on failure to allow retry. Error capture to Sentry provides good observability.


297-323: LGTM!

The registration function properly handles errors by logging and capturing to Sentry. The silent failure behavior (not re-throwing) appears intentional to prevent a registration failure from disrupting the overall notification flow.


242-280: LGTM!

The notification removal functions have consistent error handling with Sentry capture, proper dependency arrays, and are correctly memoized in the context value.

Signed-off-by: prxt6529 <prxt@6529.io>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
components/notifications/NotificationsContext.tsx (1)

13-20: Align NotificationsProvider React typings with guidelines (readonly props, no React.FC).

To match the stated guidelines (named React types + readonly props), you can drop the default React import, use a ReactNode type import, and give the provider an explicit props interface with readonly:

-import React, {
-  createContext,
-  useCallback,
-  useContext,
-  useEffect,
-  useMemo,
-  useRef,
-} from "react";
+import {
+  createContext,
+  useCallback,
+  useContext,
+  useEffect,
+  useMemo,
+  useRef,
+  type ReactNode,
+} from "react";
@@
-interface NotificationData {
+interface NotificationData {
@@
-}
-
-export const NotificationsProvider: React.FC<{ children: React.ReactNode }> = ({
-  children,
-}) => {
+}
+
+interface NotificationsProviderProps {
+  readonly children: ReactNode;
+}
+
+export const NotificationsProvider = ({
+  children,
+}: NotificationsProviderProps) => {

This removes React.FC/React.ReactNode, uses named React types, and enforces readonly on the props as requested.

Also applies to: 89-105

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ff5d4e5 and f13976d.

📒 Files selected for processing (1)
  • components/notifications/NotificationsContext.tsx (4 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version

**/*.{ts,tsx,js,jsx}: Replace <img> elements with <Image /> from next/image to satisfy @next/next/no-img-element ESLint rule
Use <Link href="/path"> from Next.js for internal navigation instead of plain HTML links to satisfy @next/next/no-html-link-for-pages ESLint rule

Files:

  • components/notifications/NotificationsContext.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always add readonly before props in React components

Files:

  • components/notifications/NotificationsContext.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks)
Use framework APIs: internal links should use <Link>, images should use next/image, and adopt Next's ESLint rules (Core Web Vitals)

Files:

  • components/notifications/NotificationsContext.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Must pass tsc --noEmit type checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") over React. namespace usage (React.useMemo, React.useRef, etc.)
If the react-hooks/exhaustive-deps lint rule is triggered: if the Effect only derives state, remove the Effect and compute during render; if listening to an external system and needing fresh props/state, wrap non-reactive logic in useEffectEvent

Files:

  • components/notifications/NotificationsContext.tsx
🪛 Biome (2.1.2)
components/notifications/NotificationsContext.tsx

[error] 44-44: This code will never be reached ...

... because either this statement will return from the function ...

... or this statement will throw an exception beforehand

(lint/correctness/noUnreachable)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)

Comment thread components/notifications/NotificationsContext.tsx
Comment thread components/notifications/NotificationsContext.tsx
Signed-off-by: prxt6529 <prxt@6529.io>
Copy link
Copy Markdown

@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 (1)
components/notifications/NotificationsContext.tsx (1)

259-261: Guard against missing notification.data when filtering wave notifications.

The filter at line 260 accesses notification.data.wave_id without optional chaining. Per Capacitor docs, notification.data can be undefined, which would throw a runtime error.

           const waveNotifications = deliveredNotifications.notifications.filter(
-            (notification) => notification.data.wave_id === waveId
+            (notification) => notification.data?.wave_id === waveId
           );
🧹 Nitpick comments (1)
components/notifications/NotificationsContext.tsx (1)

78-99: Consider adding param validation in redirect resolvers.

The resolver functions expect specific params (e.g., wave_id for waves), but NotificationData marks them optional. If a required param is missing, URLs like /waves?wave=undefined would be produced without throwing.

This is low-risk if the notification server guarantees correct payloads, but defensive validation could prevent malformed redirects.

 waves: ({ wave_id, drop_id }: { wave_id: string; drop_id: string }) => {
+   if (!wave_id) return null;
    const base = `/waves?wave=${wave_id}`;
    return drop_id ? `${base}&serialNo=${drop_id}` : base;
  },
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f13976d and 7cc34e7.

📒 Files selected for processing (1)
  • components/notifications/NotificationsContext.tsx (4 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version

**/*.{ts,tsx,js,jsx}: Replace <img> elements with <Image /> from next/image to satisfy @next/next/no-img-element ESLint rule
Use <Link href="/path"> from Next.js for internal navigation instead of plain HTML links to satisfy @next/next/no-html-link-for-pages ESLint rule

Files:

  • components/notifications/NotificationsContext.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always add readonly before props in React components

Files:

  • components/notifications/NotificationsContext.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks)
Use framework APIs: internal links should use <Link>, images should use next/image, and adopt Next's ESLint rules (Core Web Vitals)

Files:

  • components/notifications/NotificationsContext.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Must pass tsc --noEmit type checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") over React. namespace usage (React.useMemo, React.useRef, etc.)
If the react-hooks/exhaustive-deps lint rule is triggered: if the Effect only derives state, remove the Effect and compute during render; if listening to an external system and needing fresh props/state, wrap non-reactive logic in useEffectEvent

Files:

  • components/notifications/NotificationsContext.tsx
🧬 Code graph analysis (1)
components/notifications/NotificationsContext.tsx (2)
generated/models/ApiIdentity.ts (1)
  • ApiIdentity (17-169)
components/notifications/stable-device-id.ts (1)
  • getStableDeviceId (10-27)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (7)
components/notifications/NotificationsContext.tsx (7)

24-39: LGTM! Well-structured retry configuration and delegate error detection.

The constants for retry logic with exponential backoff are reasonable, and the isDelegateError helper correctly identifies iOS delegate-related registration failures by pattern matching.


41-67: LGTM! Retry logic is now correctly structured.

The exponential backoff with continue at line 61 ensures the loop proceeds to the next attempt for delegate errors, while non-delegate errors or final attempts correctly throw. This addresses the previously flagged issue.


109-157: LGTM! Clean implementation with proper error handling.

The notification.data ?? {} fallback at line 131 safely handles missing data. The awaited removeDeliveredNotifications at line 143 addresses the previously flagged void operator issue. Profile validation correctly prevents actions on mismatched notifications.


159-224: LGTM! Robust push notification initialization with proper cleanup.

Good practices observed:

  • removeAllListeners() before adding new listeners prevents duplicates
  • Sentry telemetry on registration errors aids debugging
  • iOS-specific delay before registerWithRetry() handles delegate timing issues
  • Error is re-thrown for caller to handle with appropriate context

236-251: LGTM! Initialization effect properly gated on Capacitor active state.

The isActive check ensures push notification initialization only occurs when the Capacitor app is active. The initializationRef pattern prevents redundant re-initialization for the same profile, and error handling correctly resets the ref to allow retry.


277-334: LGTM! Consistent error handling and telemetry across all operations.

Both removeAllDeliveredNotifications and registerPushNotification properly capture exceptions to Sentry with appropriate tags, enabling effective error triage in production.


336-360: LGTM! Safe redirect URL resolution with comprehensive fallbacks.

The resolveRedirectUrl function gracefully handles missing redirect types, unknown types, and resolution errors, returning null in each failure case rather than throwing.

Signed-off-by: prxt6529 <prxt@6529.io>
Copy link
Copy Markdown

@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 (3)
components/notifications/NotificationsContext.tsx (3)

3-22: Align component typing with guidelines (readonly children, named React types).

NotificationsProvider still uses React.FC<{ children: React.ReactNode }> plus a default React import. To match the stated guidelines and modern React style you can:

  • import FC/ReactNode as types; and
  • mark props as readonly.

For example:

-import React, {
-  createContext,
-  useCallback,
-  useContext,
-  useEffect,
-  useMemo,
-  useRef,
-} from "react";
+import type { FC, ReactNode } from "react";
+import {
+  createContext,
+  useCallback,
+  useContext,
+  useEffect,
+  useMemo,
+  useRef,
+} from "react";
@@
-export const NotificationsProvider: React.FC<{ children: React.ReactNode }> = ({
+export const NotificationsProvider: FC<{ readonly children: ReactNode }> = ({

As per coding guidelines.

Also applies to: 104-110


93-103: Harden notification data handling and tighten typing for redirects.

Right now notificationData is taken as notification.data ?? {} without checking that it’s an object, and NotificationData is effectively wide due to the index signature plus the cast in resolveRedirectUrl. To reduce surprises from malformed payloads you could first coerce notification.data to a predictable object:

-      const notificationData = notification.data ?? {};
+      const rawData = notification.data;
+      const notificationData: NotificationData =
+        rawData && typeof rawData === "object" ? (rawData as NotificationData) : {};

This ensures the profile-id check and resolveRedirectUrl(notificationData) both operate on a consistent object shape, avoiding odd cases where a primitive payload is treated as params.

Also applies to: 127-160, 339-363


110-110: Clarify initialization semantics for logged‑out users and consider a small telemetry tweak.

  • With const initializationRef = useRef<string | null>(null); and const profileId = connectedProfile?.id ?? null;, the effect will not initialize when there is never a connectedProfile (both values stay null), so logged‑out sessions won’t call initializeNotifications even if isCapacitor && isActive is true. If you intend to register push notifications even without a profile, consider a distinct sentinel for “never initialized”, e.g.:
-  const initializationRef = useRef<string | null>(null);
+  const initializationRef = useRef<string | null | undefined>(undefined);

This preserves the “one init per profile id” behavior while allowing a first run for the null (no‑profile) case.

  • The removal helpers (removeWaveDeliveredNotifications, removeAllDeliveredNotifications) and registerPushNotification now have solid logging and tagging. For consistency, you might also route failures from removeDeliveredNotifications through the same telemetry mechanism instead of only logging to the console.

Also applies to: 162-237, 239-254, 256-278, 280-295, 311-337

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7cc34e7 and 7e21662.

📒 Files selected for processing (1)
  • components/notifications/NotificationsContext.tsx (4 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version

**/*.{ts,tsx,js,jsx}: Replace <img> elements with <Image /> from next/image to satisfy @next/next/no-img-element ESLint rule
Use <Link href="/path"> from Next.js for internal navigation instead of plain HTML links to satisfy @next/next/no-html-link-for-pages ESLint rule

Files:

  • components/notifications/NotificationsContext.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always add readonly before props in React components

Files:

  • components/notifications/NotificationsContext.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks)
Use framework APIs: internal links should use <Link>, images should use next/image, and adopt Next's ESLint rules (Core Web Vitals)

Files:

  • components/notifications/NotificationsContext.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Must pass tsc --noEmit type checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") over React. namespace usage (React.useMemo, React.useRef, etc.)
If the react-hooks/exhaustive-deps lint rule is triggered: if the Effect only derives state, remove the Effect and compute during render; if listening to an external system and needing fresh props/state, wrap non-reactive logic in useEffectEvent

Files:

  • components/notifications/NotificationsContext.tsx
🧬 Code graph analysis (1)
components/notifications/NotificationsContext.tsx (3)
components/auth/Auth.tsx (1)
  • useAuth (95-97)
generated/models/ApiIdentity.ts (1)
  • ApiIdentity (17-169)
components/notifications/stable-device-id.ts (1)
  • getStableDeviceId (10-27)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (1)
components/notifications/NotificationsContext.tsx (1)

24-40: Retry control flow around registration now correctly distinguishes delegate errors.

The updated isDelegateError + registerWithRetry logic now:

  • retries only when the error matches a delegate pattern and there are attempts left; and
  • rethrows immediately otherwise or on the final attempt.

This removes the previous “always throw” / no‑retry behavior and should also satisfy the unreachable‑code warning while keeping the exponential backoff bounded.

Also applies to: 41-70

Signed-off-by: prxt6529 <prxt@6529.io>
Signed-off-by: prxt6529 <prxt@6529.io>
Copy link
Copy Markdown

@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

Caution

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

⚠️ Outside diff range comments (5)
components/user/collected/UserPageCollected.tsx (3)

38-49: Exported API change (ProfileCollectedFilters.sznnumber) needs a strict invariant (e.g., 0 = All, positive ints only).
Right now szn can become NaN (or negative) from URL params, which violates that likely invariant and can ripple into UI and react-query keys. Consider documenting/encoding the invariant in the type (e.g., number but always normalized to 0 | positive int).


145-175: Harden szn parsing (radix + NaN/negative guard) and satisfy Sonar by using Number.parseInt.
szn ? parseInt(szn) : 0 allows NaN/negative values; also omits radix.

@@
-      szn: szn ? parseInt(szn) : 0,
+      szn: (() => {
+        if (!szn) return 0;
+        const parsed = Number.parseInt(szn, 10);
+        return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
+      })(),

332-345: setSzn should normalize/validate inputs before writing URL params.
As written, NaN would serialize to "NaN" (truthy) and get persisted into the URL.

@@
-  const setSzn = async (szn: number): Promise<void> => {
+  const setSzn = async (szn: number): Promise<void> => {
+    const normalizedSzn =
+      Number.isFinite(szn) && szn > 0 ? Math.trunc(szn) : 0;
     const items: QueryUpdateInput[] = [
       {
         name: "szn",
-        value: szn ? szn.toString() : null,
+        value: normalizedSzn ? normalizedSzn.toString() : null,
       },
@@
components/user/collected/cards/UserPageCollectedCardsNoCards.tsx (1)

39-43: Remove unnecessary Effect; compute during render.

This Effect derives state from props, which is an anti-pattern in React 19. The msg value should be computed directly during render rather than stored in state and synced via Effect.

Based on learnings, prefer refactors aligned with React 19.2 conventions.

Apply this diff to compute during render and convert to Server Component:

-"use client";
-
 import { CollectedCollectionType, CollectionSeized } from "@/entities/IProfile";
 import { assertUnreachable } from "@/helpers/AllowlistToolHelpers";
-import { useEffect, useState } from "react";
 import { ProfileCollectedFilters } from "../UserPageCollected";
 
 export default function UserPageCollectedCardsNoCards({
   filters,
 }: {
   readonly filters: ProfileCollectedFilters;
 }) {
-  const getMsg = (): string => {
+  const msg = (() => {
     if (filters.seized !== CollectionSeized.NOT_SEIZED) {
       return "No cards to display";
     }
     switch (filters.collection) {
       case null:
         return "Congratulations, full setter!";
       case CollectedCollectionType.MEMES:
         if (filters.szn === 0) {
           return "Congratulations, The Memes full setter!";
         }
         return `Congratulations, ${filters.szn} full setter!`;
       case CollectedCollectionType.GRADIENTS:
         return "Congratulations, Gradient full setter!";
       case CollectedCollectionType.MEMELAB:
         return "Congratulations, Meme Lab full setter!";
       case CollectedCollectionType.NEXTGEN:
         return "Congratulations, Next Gen full setter!";
       case CollectedCollectionType.NETWORK:
         return "No network tokens found";
       default:
         assertUnreachable(filters.collection);
         return "";
     }
-  };
-
-  const [msg, setMsg] = useState(getMsg());
-
-  useEffect(() => {
-    setMsg(getMsg());
-  }, [filters]);
+  })();
 
   return (
     <div className="tw-py-4 tw-text-sm tw-italic tw-text-iron-500">{msg}</div>
   );
 }
components/user/collected/filters/UserPageCollectedFilters.tsx (1)

44-51: Remove commented-out legacy UserPageCollectedFiltersSzn component.

Lines 196-200 contain commented JSX that violates the TSX guideline requiring self-explanatory, comment-free code. The SeasonsDropdown component is now the active implementation; the old component reference should be deleted entirely.

            {getShowSzn(filters.collection) && (
-             // <UserPageCollectedFiltersSzn
-             //   selected={filters.szn}
-             //   containerRef={containerRef}
-             //   setSelected={setSzn}
-             // />
              <SeasonsDropdown
                selectedSeason={filters.szn}
                setSelectedSeason={setSzn}
              />
            )}

The type signature setSzn: (szn: number) => void is correct and consistent with ProfileCollectedFilters.szn: number and SeasonsDropdown's contract where 0 represents "All".

🧹 Nitpick comments (2)
components/user/collected/cards/UserPageCollectedCardsNoCards.tsx (1)

21-24: Consider adding "SZN" prefix for clarity.

Line 24 interpolates the season number directly without a prefix, which may be less clear than "Congratulations, SZN ${filters.szn} full setter!" for consistency with how seasons are typically referenced.

Apply this diff for improved clarity:

-        return `Congratulations, ${filters.szn} full setter!`;
+        return `Congratulations, SZN ${filters.szn} full setter!`;
__tests__/components/seasons-dropdown/SeasonsDropdown.test.tsx (1)

39-118: Add mock cleanup to prevent cross-test leakage.

Recommend adding afterEach(() => jest.clearAllMocks()); (or mockCommonApiFetch.mockReset()), since each test relies on fresh mock resolution. Based on learnings, keep tests independent/deterministic.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7e21662 and 2d15bb1.

📒 Files selected for processing (8)
  • __tests__/components/seasons-dropdown/SeasonsDropdown.test.tsx (1 hunks)
  • components/seasons-dropdown/SeasonsDropdown.module.scss (1 hunks)
  • components/seasons-dropdown/SeasonsDropdown.tsx (1 hunks)
  • components/the-memes/TheMemes.tsx (1 hunks)
  • components/user/collected/UserPageCollected.tsx (4 hunks)
  • components/user/collected/cards/UserPageCollectedCardsNoCards.tsx (2 hunks)
  • components/user/collected/filters/UserPageCollectedFilters.tsx (6 hunks)
  • components/user/collected/filters/UserPageCollectedFiltersSzn.tsx (0 hunks)
💤 Files with no reviewable changes (1)
  • components/user/collected/filters/UserPageCollectedFiltersSzn.tsx
🧰 Additional context used
📓 Path-based instructions (11)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version

**/*.{ts,tsx,js,jsx}: Replace <img> elements with <Image /> from next/image to satisfy @next/next/no-img-element ESLint rule
Use <Link href="/path"> from Next.js for internal navigation instead of plain HTML links to satisfy @next/next/no-html-link-for-pages ESLint rule

Files:

  • components/user/collected/cards/UserPageCollectedCardsNoCards.tsx
  • components/the-memes/TheMemes.tsx
  • components/seasons-dropdown/SeasonsDropdown.tsx
  • __tests__/components/seasons-dropdown/SeasonsDropdown.test.tsx
  • components/user/collected/filters/UserPageCollectedFilters.tsx
  • components/user/collected/UserPageCollected.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always add readonly before props in React components

Files:

  • components/user/collected/cards/UserPageCollectedCardsNoCards.tsx
  • components/the-memes/TheMemes.tsx
  • components/seasons-dropdown/SeasonsDropdown.tsx
  • __tests__/components/seasons-dropdown/SeasonsDropdown.test.tsx
  • components/user/collected/filters/UserPageCollectedFilters.tsx
  • components/user/collected/UserPageCollected.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks)
Use framework APIs: internal links should use <Link>, images should use next/image, and adopt Next's ESLint rules (Core Web Vitals)

**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks rules)
Follow existing code style and naming conventions; maintain clean code standards (measured by SonarQube)

Files:

  • components/user/collected/cards/UserPageCollectedCardsNoCards.tsx
  • components/the-memes/TheMemes.tsx
  • components/seasons-dropdown/SeasonsDropdown.tsx
  • __tests__/components/seasons-dropdown/SeasonsDropdown.test.tsx
  • components/user/collected/filters/UserPageCollectedFilters.tsx
  • components/user/collected/UserPageCollected.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Must pass tsc --noEmit type checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") over React. namespace usage (React.useMemo, React.useRef, etc.)
If the react-hooks/exhaustive-deps lint rule is triggered: if the Effect only derives state, remove the Effect and compute during render; if listening to an external system and needing fresh props/state, wrap non-reactive logic in useEffectEvent

**/*.{ts,tsx}: Must pass tsc --noEmit for TypeScript type checking
Prefer Server Components over Client Components; use Server Functions/Server Actions ('use server') for mutations
Remove unnecessary Effects; if Effect only derives state, compute during render instead
Use useEffectEvent for non-reactive logic inside Effects to avoid unnecessary re-runs
Use framework APIs: <Link> for internal links, next/image for images, adopt Next's ESLint rules
Use 'use cache' directive and Cache Components features for explicit opt-in caching in Next.js 16
Use TypeScript and React functional components with hooks
When parsing Seize URLs or similar, fail fast if base origin is unavailable; do not fall back to placeholder origins
Replace <img> elements with <Image /> from next/image
Use <Link href="/path"> for internal navigation instead of plain HTML links
Move data fetches to Server Components; handle mutations through Server Functions/Server Actions with 'use server' directive

Files:

  • components/user/collected/cards/UserPageCollectedCardsNoCards.tsx
  • components/the-memes/TheMemes.tsx
  • components/seasons-dropdown/SeasonsDropdown.tsx
  • __tests__/components/seasons-dropdown/SeasonsDropdown.test.tsx
  • components/user/collected/filters/UserPageCollectedFilters.tsx
  • components/user/collected/UserPageCollected.tsx
**/@(__tests__|*.test).{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Tests should live in __tests__/ or ComponentName.test.tsx; mock external dependencies and APIs in tests

Files:

  • __tests__/components/seasons-dropdown/SeasonsDropdown.test.tsx
**/__tests__/**/*.{ts,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

Place tests in __tests__/ directory or as ComponentName.test.tsx alongside components

Files:

  • __tests__/components/seasons-dropdown/SeasonsDropdown.test.tsx
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

Mock external dependencies and APIs in tests

Files:

  • __tests__/components/seasons-dropdown/SeasonsDropdown.test.tsx
__tests__/**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (tests/AGENTS.md)

Use Jest + ts-jest for TypeScript testing

Files:

  • __tests__/components/seasons-dropdown/SeasonsDropdown.test.tsx
__tests__/**/*.{ts,tsx}

📄 CodeRabbit inference engine (tests/AGENTS.md)

Functions must have ≤ 15 cognitive complexity; extract deep ternaries (>3 levels) and break down complex logic

Files:

  • __tests__/components/seasons-dropdown/SeasonsDropdown.test.tsx
__tests__/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (tests/AGENTS.md)

__tests__/**/*.{ts,tsx,js}: Prefer for...of loops over forEach as it allows break/continue and works with async/await
Use array.at(-1) and array.at(-2) instead of index-based array access for negative indexing
Use String.prototype.replaceAll() instead of replace() for global string replacements
Use globalThis.fetch() instead of direct fetch() calls
Organize imports with one import per module in order: external → internal → types, with no duplicates
Use element.remove() instead of parent.removeChild(element) for DOM manipulation
Catch errors only when meaningful; no empty catch blocks; log errors with context
Avoid double negatives in code; prefer explicit logic and remove redundant annotations; use optional chaining (?.)

Files:

  • __tests__/components/seasons-dropdown/SeasonsDropdown.test.tsx
__tests__/**/{components,contexts,hooks}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (tests/AGENTS.md)

Use semantic HTML elements (<label>, <output>) over ARIA attributes when possible; every form control must have a label

Files:

  • __tests__/components/seasons-dropdown/SeasonsDropdown.test.tsx
🧠 Learnings (12)
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Fix with modernization (no `// eslint-disable` unless explicitly instructed); prefer refactors aligned with React 19.2, React Compiler, and Next.js 16 conventions

Applied to files:

  • components/user/collected/cards/UserPageCollectedCardsNoCards.tsx
  • components/the-memes/TheMemes.tsx
  • components/seasons-dropdown/SeasonsDropdown.tsx
  • components/user/collected/filters/UserPageCollectedFilters.tsx
  • components/user/collected/UserPageCollected.tsx
📚 Learning: 2025-12-05T10:55:30.871Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-05T10:55:30.871Z
Learning: Applies to **/*.{ts,tsx} : Remove unnecessary Effects; if Effect only derives state, compute during render instead

Applied to files:

  • components/the-memes/TheMemes.tsx
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Applies to **/@(__tests__|*.test).{ts,tsx} : Tests should live in `__tests__/` or `ComponentName.test.tsx`; mock external dependencies and APIs in tests

Applied to files:

  • __tests__/components/seasons-dropdown/SeasonsDropdown.test.tsx
📚 Learning: 2025-12-05T10:55:30.871Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-05T10:55:30.871Z
Learning: Applies to **/*.test.{ts,tsx} : Mock external dependencies and APIs in tests

Applied to files:

  • __tests__/components/seasons-dropdown/SeasonsDropdown.test.tsx
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/*.{ts,tsx,js} : Avoid double negatives in code; prefer explicit logic and remove redundant annotations; use optional chaining (`?.`)

Applied to files:

  • __tests__/components/seasons-dropdown/SeasonsDropdown.test.tsx
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/__tests__/**/*.test.{ts,tsx,js} : Test high-risk areas including happy path workflows, invalid input errors, edge cases/boundaries, component & API interactions, and performance/security when relevant

Applied to files:

  • __tests__/components/seasons-dropdown/SeasonsDropdown.test.tsx
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/__tests__/**/components/**/*.test.{ts,tsx} : Test accessibility with keyboard navigation and screen reader compatibility

Applied to files:

  • __tests__/components/seasons-dropdown/SeasonsDropdown.test.tsx
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/__tests__/**/*.test.{ts,tsx,js} : Keep tests independent, deterministic, and fast with production-like data

Applied to files:

  • __tests__/components/seasons-dropdown/SeasonsDropdown.test.tsx
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/__tests__/**/components/**/*.test.{ts,tsx} : Use testing-library/react + user-event for React component tests

Applied to files:

  • __tests__/components/seasons-dropdown/SeasonsDropdown.test.tsx
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/{components,contexts,hooks}/**/*.{ts,tsx} : Use semantic HTML elements (`<label>`, `<output>`) over ARIA attributes when possible; every form control must have a label

Applied to files:

  • __tests__/components/seasons-dropdown/SeasonsDropdown.test.tsx
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/app/api/**/__tests__/**/*.test.{ts,tsx,js} : API integration tests should be located in `app/api`

Applied to files:

  • __tests__/components/seasons-dropdown/SeasonsDropdown.test.tsx
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/*.{ts,tsx,js} : Use `globalThis.fetch()` instead of direct `fetch()` calls

Applied to files:

  • __tests__/components/seasons-dropdown/SeasonsDropdown.test.tsx
🧬 Code graph analysis (3)
components/seasons-dropdown/SeasonsDropdown.tsx (1)
entities/ISeason.ts (1)
  • MemeSeason (1-8)
__tests__/components/seasons-dropdown/SeasonsDropdown.test.tsx (1)
components/seasons-dropdown/SeasonsDropdown.tsx (1)
  • SeasonsDropdown (19-163)
components/user/collected/filters/UserPageCollectedFilters.tsx (1)
components/seasons-dropdown/SeasonsDropdown.tsx (1)
  • SeasonsDropdown (19-163)
🪛 GitHub Check: SonarCloud Code Analysis
components/seasons-dropdown/SeasonsDropdown.module.scss

[warning] 50-50: Text does not meet the minimal contrast requirement with its background.

See more on https://sonarcloud.io/project/issues?id=6529-Collections_6529seize-frontend&issues=AZsR0MP45h2v2pacZCif&open=AZsR0MP45h2v2pacZCif&pullRequest=1631

components/user/collected/UserPageCollected.tsx

[warning] 166-166: Prefer Number.parseInt over parseInt.

See more on https://sonarcloud.io/project/issues?id=6529-Collections_6529seize-frontend&issues=AZsR0MLq5h2v2pacZCie&open=AZsR0MLq5h2v2pacZCie&pullRequest=1631

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (3)
components/user/collected/UserPageCollected.tsx (1)

403-405: API param write is consistent with 0 = All—keep it that way after normalization.
if (filters.szn) correctly omits season when szn === 0; just make sure filters.szn can’t become NaN (see parsing/normalization comments).

components/the-memes/TheMemes.tsx (1)

451-456: Header change looks fine.

__tests__/components/seasons-dropdown/SeasonsDropdown.test.tsx (1)

1-37: Tests likely failing: toggle label expects SZN: SZN2 but component renders SZN: 2.

Either update expectations to SZN: 2, or (better) update components/seasons-dropdown/SeasonsDropdown.tsx to use MemeSeason.display / SZN${id} so the UI matches the intended “SZN2” label. Based on learnings, tests should remain deterministic and aligned to real UI contracts.

⛔ Skipped due to learnings
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Applies to **/@(__tests__|*.test).{ts,tsx} : Tests should live in `__tests__/` or `ComponentName.test.tsx`; mock external dependencies and APIs in tests
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/*.{ts,tsx,js} : Avoid double negatives in code; prefer explicit logic and remove redundant annotations; use optional chaining (`?.`)
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/{components,contexts,hooks}/**/*.{ts,tsx} : Use semantic HTML elements (`<label>`, `<output>`) over ARIA attributes when possible; every form control must have a label
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/__tests__/**/*.test.{ts,tsx,js} : Test high-risk areas including happy path workflows, invalid input errors, edge cases/boundaries, component & API interactions, and performance/security when relevant
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __mocks__/AGENTS.md:0-0
Timestamp: 2025-11-25T08:37:21.058Z
Learning: Applies to __mocks__/**/__mocks__/** : Document expected behaviour in the mock file when it is not obvious
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/__tests__/**/*.test.{ts,tsx,js} : Write tests following Arrange – Act – Assert pattern with one behaviour per test and clear, descriptive names
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-05T10:55:30.871Z
Learning: Applies to **/*.test.{ts,tsx} : Mock external dependencies and APIs in tests
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/__tests__/**/components/**/*.test.{ts,tsx} : Use testing-library/react + user-event for React component tests
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/*.{ts,tsx} : Functions must have ≤ 15 cognitive complexity; extract deep ternaries (>3 levels) and break down complex logic

Comment thread components/seasons-dropdown/SeasonsDropdown.module.scss Outdated
Comment thread components/seasons-dropdown/SeasonsDropdown.tsx Outdated
Comment thread components/seasons-dropdown/SeasonsDropdown.tsx Outdated
Signed-off-by: prxt6529 <prxt@6529.io>
Signed-off-by: prxt6529 <prxt@6529.io>
Signed-off-by: prxt6529 <prxt@6529.io>
@sonarqubecloud
Copy link
Copy Markdown

Copy link
Copy Markdown

@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)
__tests__/components/notifications/NotificationsContext.test.tsx (2)

78-122: Excellent test coverage for the new initialization flow.

The tests comprehensively verify the isActive-gated initialization behavior. The 2000ms timeout at line 119 properly accounts for the exponential backoff retry logic mentioned in the PR objectives.

Note: Lines 83-85 are redundant since jest.clearAllMocks() on line 82 already clears all mocks.

Apply this diff to remove redundant mock clearing:

   beforeEach(() => {
     mockIsActive = true;
     const { PushNotifications } = require("@capacitor/push-notifications");
     jest.clearAllMocks();
-    PushNotifications.removeAllListeners.mockClear();
-    PushNotifications.addListener.mockClear();
-    PushNotifications.register.mockClear();
   });

54-54: Consider adding readonly modifier for consistency.

The coding guideline specifies that props should be marked readonly in React components. While this is a test helper, applying the guideline consistently improves type safety.

As per coding guidelines, apply this diff:

-const wrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => (
+const wrapper: React.FC<{ readonly children: React.ReactNode }> = ({ children }) => (
   <NotificationsProvider>{children}</NotificationsProvider>
 );
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2d15bb1 and 6cde0ee.

📒 Files selected for processing (1)
  • __tests__/components/notifications/NotificationsContext.test.tsx (4 hunks)
🧰 Additional context used
📓 Path-based instructions (11)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version

**/*.{ts,tsx,js,jsx}: Replace <img> elements with <Image /> from next/image to satisfy @next/next/no-img-element ESLint rule
Use <Link href="/path"> from Next.js for internal navigation instead of plain HTML links to satisfy @next/next/no-html-link-for-pages ESLint rule

Files:

  • __tests__/components/notifications/NotificationsContext.test.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always add readonly before props in React components

Files:

  • __tests__/components/notifications/NotificationsContext.test.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks)
Use framework APIs: internal links should use <Link>, images should use next/image, and adopt Next's ESLint rules (Core Web Vitals)

**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks rules)
Follow existing code style and naming conventions; maintain clean code standards (measured by SonarQube)

Files:

  • __tests__/components/notifications/NotificationsContext.test.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Must pass tsc --noEmit type checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") over React. namespace usage (React.useMemo, React.useRef, etc.)
If the react-hooks/exhaustive-deps lint rule is triggered: if the Effect only derives state, remove the Effect and compute during render; if listening to an external system and needing fresh props/state, wrap non-reactive logic in useEffectEvent

**/*.{ts,tsx}: Must pass tsc --noEmit for TypeScript type checking
Prefer Server Components over Client Components; use Server Functions/Server Actions ('use server') for mutations
Remove unnecessary Effects; if Effect only derives state, compute during render instead
Use useEffectEvent for non-reactive logic inside Effects to avoid unnecessary re-runs
Use framework APIs: <Link> for internal links, next/image for images, adopt Next's ESLint rules
Use 'use cache' directive and Cache Components features for explicit opt-in caching in Next.js 16
Use TypeScript and React functional components with hooks
When parsing Seize URLs or similar, fail fast if base origin is unavailable; do not fall back to placeholder origins
Replace <img> elements with <Image /> from next/image
Use <Link href="/path"> for internal navigation instead of plain HTML links
Move data fetches to Server Components; handle mutations through Server Functions/Server Actions with 'use server' directive

Files:

  • __tests__/components/notifications/NotificationsContext.test.tsx
**/@(__tests__|*.test).{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Tests should live in __tests__/ or ComponentName.test.tsx; mock external dependencies and APIs in tests

Files:

  • __tests__/components/notifications/NotificationsContext.test.tsx
**/__tests__/**/*.{ts,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

Place tests in __tests__/ directory or as ComponentName.test.tsx alongside components

Files:

  • __tests__/components/notifications/NotificationsContext.test.tsx
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

Mock external dependencies and APIs in tests

Files:

  • __tests__/components/notifications/NotificationsContext.test.tsx
__tests__/**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (tests/AGENTS.md)

Use Jest + ts-jest for TypeScript testing

Files:

  • __tests__/components/notifications/NotificationsContext.test.tsx
__tests__/**/*.{ts,tsx}

📄 CodeRabbit inference engine (tests/AGENTS.md)

Functions must have ≤ 15 cognitive complexity; extract deep ternaries (>3 levels) and break down complex logic

Files:

  • __tests__/components/notifications/NotificationsContext.test.tsx
__tests__/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (tests/AGENTS.md)

__tests__/**/*.{ts,tsx,js}: Prefer for...of loops over forEach as it allows break/continue and works with async/await
Use array.at(-1) and array.at(-2) instead of index-based array access for negative indexing
Use String.prototype.replaceAll() instead of replace() for global string replacements
Use globalThis.fetch() instead of direct fetch() calls
Organize imports with one import per module in order: external → internal → types, with no duplicates
Use element.remove() instead of parent.removeChild(element) for DOM manipulation
Catch errors only when meaningful; no empty catch blocks; log errors with context
Avoid double negatives in code; prefer explicit logic and remove redundant annotations; use optional chaining (?.)

Files:

  • __tests__/components/notifications/NotificationsContext.test.tsx
__tests__/**/{components,contexts,hooks}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (tests/AGENTS.md)

Use semantic HTML elements (<label>, <output>) over ARIA attributes when possible; every form control must have a label

Files:

  • __tests__/components/notifications/NotificationsContext.test.tsx
🧠 Learnings (4)
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Applies to **/@(__tests__|*.test).{ts,tsx} : Tests should live in `__tests__/` or `ComponentName.test.tsx`; mock external dependencies and APIs in tests

Applied to files:

  • __tests__/components/notifications/NotificationsContext.test.tsx
📚 Learning: 2025-12-05T10:55:30.871Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-05T10:55:30.871Z
Learning: Applies to **/*.test.{ts,tsx} : Mock external dependencies and APIs in tests

Applied to files:

  • __tests__/components/notifications/NotificationsContext.test.tsx
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/*.{ts,tsx,js} : Avoid double negatives in code; prefer explicit logic and remove redundant annotations; use optional chaining (`?.`)

Applied to files:

  • __tests__/components/notifications/NotificationsContext.test.tsx
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Fix with modernization (no `// eslint-disable` unless explicitly instructed); prefer refactors aligned with React 19.2, React Compiler, and Next.js 16 conventions

Applied to files:

  • __tests__/components/notifications/NotificationsContext.test.tsx
🧬 Code graph analysis (1)
__tests__/components/notifications/NotificationsContext.test.tsx (1)
components/notifications/NotificationsContext.tsx (1)
  • useNotificationsContext (365-373)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (3)
__tests__/components/notifications/NotificationsContext.test.tsx (3)

11-18: LGTM! Dynamic mock state pattern is well-implemented.

The getter pattern correctly enables tests to toggle isActive dynamically, which is essential for testing the isActive-gated initialization logic introduced in the PR.


50-52: LGTM! Deterministic device ID mock.

Properly mocks the stable device ID for deterministic test behavior.


106-106: Past review feedback addressed.

The unused result variable has been correctly removed from both initialization tests. These calls to renderHook only trigger initialization side effects and don't need to access result.current.

Also applies to: 151-151

@prxt6529 prxt6529 merged commit 0e9672c into main Dec 17, 2025
9 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Feb 27, 2026
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.

2 participants