Skip to content

Conversation

@ucswift
Copy link
Member

@ucswift ucswift commented Aug 15, 2025

Summary by CodeRabbit

  • New Features

    • Server URL can be managed from the Login screen via a new bottom sheet.
    • Added an iOS-specific app icon.
  • Refactor

    • Real-time connections now use a centralized Eventing URL with improved reconnection and token handling.
    • App initialization now loads configuration first and surfaces config fetch errors earlier.
  • Chores

    • CI/CD extracts PR release notes and publishes Android releases automatically.
  • Tests

    • Added extensive tests for login, real-time connectivity, stores, and location search.

@ucswift
Copy link
Member Author

ucswift commented Aug 15, 2025

Task linked: CU-868f4q2qr Fix issues with Call Images

@gitguardian
Copy link

gitguardian bot commented Aug 15, 2025

⚠️ GitGuardian has uncovered 1 secret following the scan of your pull request.

Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components.

🔎 Detected hardcoded secret in your pull request
GitGuardian id GitGuardian status Secret Commit Filename
20215389 Triggered Bearer Token 83bb851 src/services/tests/signalr.service.test.ts View secret
🛠 Guidelines to remediate hardcoded secrets
  1. Understand the implications of revoking this secret by investigating where it is used in your code.
  2. Replace and store your secret safely. Learn here the best practices.
  3. Revoke and rotate this secret.
  4. If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.

To avoid such incidents in the future consider


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

@coderabbitai
Copy link

coderabbitai bot commented Aug 15, 2025

Walkthrough

Adds Android PR-body-driven release steps to CI, an iOS icon, removes CHANNEL_API_URL from public env, expands GetConfigResultData with eight fields, makes CoreStore fetch config during init, implements EventingUrl-based SignalR connections with reconnection, adds Login server-URL UI and many tests/mocks.

Changes

Cohort / File(s) Summary
CI/CD Release Automation
.github/workflows/react-native-cicd.yml
Adds Android-only steps to extract "## Release Notes" from PR body, create RELEASE_NOTES.md with version header, and create a GitHub Release attaching ResgridUnit-prod.apk.
App Config / Icon
app.config.ts
Adds iOS-specific icon: ios.icon: './assets/ios-icon.png'.
Env Schema Update
env.js
Removes CHANNEL_API_URL from public client schema and runtime mapping.
Test Setup (Zod Mock)
jest-setup.ts
Adds global Jest mock for zod exposing minimal parse/safeParse behavior for tests.
Config Model Expansion
src/models/v4/configs/getConfigResultData.ts
Adds 8 fields to GetConfigResultData: EventingUrl, DirectionsMapKey, PersonnelLocationStaleSeconds, UnitLocationStaleSeconds, PersonnelLocationMinMeters, UnitLocationMinMeters, AnalyticsApiKey, AnalyticsHost (with defaults).
Login UI, Prop & Schema
src/app/login/index.tsx, src/app/login/login-form.tsx
Adds ServerUrlBottomSheet integration, showServerUrl state, onServerUrlPress prop on LoginForm, renders "Server URL" button when provided, and refactors schema creation into createLoginFormSchema.
Login Tests
src/app/login/__tests__/index.test.tsx, src/app/login/__tests__/login-form.test.tsx
Adds comprehensive tests and mocks covering Login screen, LoginForm behavior, server-URL sheet, error modal, navigation, analytics, loading/password toggle, and form submission.
SignalR Service (API + Reconnect)
src/services/signalr.service.ts
Adds SignalRHubConnectConfig and connectToHubWithEventingUrl(config); stores hub configs; builds hub URL from eventingUrl+hubName; uses token-in-URL for geolocation hub or tokenFactory header for others; adds reconnection that refreshes token and reconnects using stored config; redacts tokens in logs.
SignalR Service Tests
src/services/__tests__/signalr.service.test.ts
Adds extensive tests for connection paths, token encoding, auth modes, event handlers, invoke/on/off, reconnection/backoff, and error handling.
SignalR Store + Tests
src/stores/signalr/signalr-store.ts, src/stores/signalr/__tests__/signalr-store.test.ts
Store now reads EventingUrl from core config, guards missing EventingUrl with error and log, and calls connectToHubWithEventingUrl; tests added for connect/disconnect/error scenarios.
Core Store + Tests
src/stores/app/core-store.ts, src/stores/app/__tests__/core-store.test.ts
CoreStore.init now calls fetchConfig before continuing; fetchConfig sets config on success and clears error, or sets error and rethrows on failure; tests updated to exercise these paths.
Call Search Tests (fixtures updated)
src/app/call/new/__tests__/*
Test fixtures updated (address/coordinates/plus-code/what3words) to include the 8 new config fields; no runtime logic changes.

Sequence Diagram(s)

sequenceDiagram
  participant UI as Caller
  participant Store as SignalR Store
  participant Core as Core Store
  participant Svc as SignalR Service
  participant Hub as SignalR Hub

  UI->>Store: connectUpdateHub()
  Store->>Core: getState().config.EventingUrl
  alt EventingUrl missing
    Store-->>UI: set error, log, abort
  else EventingUrl present
    Store->>Svc: connectToHubWithEventingUrl({ name, eventingUrl, hubName, methods })
    Svc->>Svc: build full hub URL (append hubName)
    alt hubName == REALTIME_GEO_HUB_NAME
      Svc->>Hub: start with access_token in URL
    else
      Svc->>Hub: start with access token via header/tokenFactory
    end
    Hub-->>Svc: connected
    Svc-->>Store: mark connected
  end
  Hub-->>Svc: onclose
  Svc->>Svc: refresh token -> reconnect using stored hubConfig (delay/backoff)
Loading
sequenceDiagram
  participant App as App
  participant Core as CoreStore
  participant API as getConfig API

  App->>Core: init()
  Core->>Core: fetchConfig()
  Core->>API: getConfig()
  alt success
    API-->>Core: config data
    Core-->>App: continue init
  else error
    API-->>Core: error
    Core-->>App: throw/reject "Failed to fetch config"
  end
Loading
sequenceDiagram
  participant User as User
  participant Login as Login Screen
  participant Sheet as ServerUrlBottomSheet

  User->>Login: tap "Server URL"
  Login->>Sheet: open (isOpen=true)
  User->>Sheet: close
  Sheet->>Login: onClose()
  Login->>Sheet: isOpen=false
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

A rabbit drums the CI with glee,
Release notes pulled from PRs we see.
Eventing hubs leap, tokens tucked away,
Eight new config leaves brighten the day.
Login burrows open — server URL on display. 🐇✨

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch develop

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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

🧹 Nitpick comments (20)
.github/workflows/react-native-cicd.yml (1)

279-299: Consider a dedicated release job to aggregate artifacts from both platforms

Current approach limits artifacts to Android and couples release creation to one matrix leg. A separate job can download both artifacts and publish a single release consistently.

Example pattern:

  create-release:
    needs: build-and-deploy
    if: ${{ github.event_name != 'pull_request' }} # or your desired trigger
    runs-on: ubuntu-latest
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: app-builds-android
          path: dist/android
      - uses: actions/download-artifact@v4
        with:
          name: app-builds-ios
          path: dist/ios
      - name: 📦 Create GitHub Release
        uses: ncipollo/release-action@v1
        with:
          tag: "7.${{ github.run_number }}"
          commit: ${{ github.sha }}
          makeLatest: true
          allowUpdates: true
          name: "7.${{ github.run_number }}"
          artifacts: |
            dist/android/ResgridUnit-prod.apk
            dist/android/ResgridUnit-prod.aab
            dist/ios/ResgridUnit-ios-adhoc.ipa
            dist/ios/ResgridUnit-ios-prod.ipa
          bodyFile: "RELEASE_NOTES.md"
jest-setup.ts (1)

159-176: Consider using a more complete zod mock implementation.

While the current mock works for basic cases, it doesn't handle many zod features like email(), optional(), nullable(), refine(), or schema composition. This could lead to tests passing when they shouldn't if the actual validation logic relies on these features.

Consider enhancing the mock to support more zod features:

 jest.mock('zod', () => ({
   z: {
     object: jest.fn(() => ({
       parse: jest.fn((data) => data),
       safeParse: jest.fn((data) => ({ success: true, data })),
+      shape: {},
+      partial: jest.fn(() => ({
+        parse: jest.fn((data) => data),
+        safeParse: jest.fn((data) => ({ success: true, data })),
+      })),
     })),
     string: jest.fn(() => ({
       min: jest.fn(() => ({
         parse: jest.fn((data) => data),
         safeParse: jest.fn((data) => ({ success: true, data })),
       })),
+      email: jest.fn(() => ({
+        parse: jest.fn((data) => data),
+        safeParse: jest.fn((data) => ({ success: true, data })),
+      })),
+      optional: jest.fn(() => ({
+        parse: jest.fn((data) => data),
+        safeParse: jest.fn((data) => ({ success: true, data })),
+      })),
       parse: jest.fn((data) => data),
       safeParse: jest.fn((data) => ({ success: true, data })),
     })),
+    number: jest.fn(() => ({
+      parse: jest.fn((data) => data),
+      safeParse: jest.fn((data) => ({ success: true, data })),
+    })),
+    boolean: jest.fn(() => ({
+      parse: jest.fn((data) => data),
+      safeParse: jest.fn((data) => ({ success: true, data })),
+    })),
+    infer: jest.fn(),
   },
   __esModule: true,
 }));
src/models/v4/configs/getConfigResultData.ts (2)

4-4: Consider making EventingUrl a required field with validation.

The EventingUrl field is crucial for SignalR connectivity. Having it default to an empty string could lead to runtime failures if not properly configured.

Consider adding runtime validation or making this field required to fail early:

-  public EventingUrl: string = '';
+  public EventingUrl: string; // Make required
+  
+  constructor(data?: Partial<GetConfigResultData>) {
+    // Validate EventingUrl is provided
+    if (!data?.EventingUrl) {
+      throw new Error('EventingUrl is required for SignalR connectivity');
+    }
+    Object.assign(this, data);
+  }

10-13: Consider using more descriptive names for location tracking parameters.

The parameter names could be clearer about their purpose and units.

Consider renaming for clarity:

-  public PersonnelLocationStaleSeconds: number = 300;
-  public UnitLocationStaleSeconds: number = 300;
-  public PersonnelLocationMinMeters: number = 15;
-  public UnitLocationMinMeters: number = 15;
+  public PersonnelLocationStaleTimeoutSeconds: number = 300;
+  public UnitLocationStaleTimeoutSeconds: number = 300;
+  public PersonnelLocationMinDistanceMeters: number = 15;
+  public UnitLocationMinDistanceMeters: number = 15;
src/services/signalr.service.ts (1)

67-71: Consider extracting the token parameter logic for maintainability.

The special handling for the geolocation hub could be made more maintainable by extracting it into a separate method.

Consider extracting this logic:

+  private buildHubUrl(eventingUrl: string, hubName: string, token: string): string {
+    const normalizedEventingUrl = eventingUrl.endsWith('/') ? eventingUrl : `${eventingUrl}/`;
+    let fullUrl = `${normalizedEventingUrl}${hubName}`;
+    
+    // For geolocation hub, add token as URL parameter instead of header
+    const isGeolocationHub = hubName === Env.REALTIME_GEO_HUB_NAME;
+    if (isGeolocationHub) {
+      const separator = fullUrl.includes('?') ? '&' : '?';
+      fullUrl = `${fullUrl}${separator}access_token=${encodeURIComponent(token)}`;
+    }
+    
+    return fullUrl;
+  }

   public async connectToHubWithEventingUrl(config: SignalRHubConnectConfig): Promise<void> {
     // ... validation code ...
     
-    // Ensure EventingUrl has a trailing slash
-    const normalizedEventingUrl = config.eventingUrl.endsWith('/') ? config.eventingUrl : `${config.eventingUrl}/`;
-
-    let fullUrl = `${normalizedEventingUrl}${config.hubName}`;
-
-    // For geolocation hub, add token as URL parameter instead of header
-    const isGeolocationHub = config.hubName === Env.REALTIME_GEO_HUB_NAME;
-    if (isGeolocationHub) {
-      const separator = fullUrl.includes('?') ? '&' : '?';
-      fullUrl = `${fullUrl}${separator}access_token=${encodeURIComponent(token)}`;
-    }
+    const fullUrl = this.buildHubUrl(config.eventingUrl, config.hubName, token);
+    const isGeolocationHub = config.hubName === Env.REALTIME_GEO_HUB_NAME;
src/app/call/new/__tests__/address-search.test.ts (1)

151-169: Consistent fixture shape for “no key” path.

Mirrors the added fields in mockConfig, ensuring both presence/absence scenarios stay in sync with the model. Looks good.

To reduce duplication across tests (address/plus-code/coordinates/what3words), consider a small test factory:

// e.g., src/test-utils/config.ts
import { type GetConfigResultData } from '@/models/v4/configs/getConfigResultData';

export const createConfig = (overrides?: Partial<GetConfigResultData>): GetConfigResultData => ({
  GoogleMapsKey: '',
  W3WKey: '',
  EventingUrl: '',
  LoggingKey: '',
  MapUrl: '',
  MapAttribution: '',
  OpenWeatherApiKey: '',
  DirectionsMapKey: '',
  PersonnelLocationStaleSeconds: 300,
  UnitLocationStaleSeconds: 300,
  PersonnelLocationMinMeters: 15,
  UnitLocationMinMeters: 15,
  NovuBackendApiUrl: '',
  NovuSocketUrl: '',
  NovuApplicationId: '',
  AnalyticsApiKey: '',
  AnalyticsHost: '',
  ...overrides,
});

Then in tests: const mockConfig = createConfig({ GoogleMapsKey: 'test-api-key' });

src/app/call/new/__tests__/what3words.test.tsx (1)

10-28: Updated config fixture fields align with GetConfigResultData expansion.

EventingUrl, DirectionsMapKey, PersonnelLocationStaleSeconds, UnitLocationStaleSeconds, PersonnelLocationMinMeters, UnitLocationMinMeters, AnalyticsApiKey, AnalyticsHost are correctly included with expected defaults. Good parity with other test suites.

Consider reusing a shared test factory for config objects (see suggestion in address-search tests) to DRY up fixtures across the suite.

src/app/login/index.tsx (1)

63-63: Wiring Server URL button to open the bottom sheet looks good; consider stabilizing the handler.

Passing an inline arrow function is fine here; to avoid re-renders you could wrap it with useCallback.

Example:

// Add to imports
import React, { useEffect, useState, useCallback } from 'react';

// Inside component:
const handleServerUrlPress = useCallback(() => setShowServerUrl(true), []);

// Usage:
<LoginForm onSubmit={onSubmit} isLoading={status === 'loading'} error={error ?? undefined} onServerUrlPress={handleServerUrlPress} />
src/app/call/new/__tests__/plus-code-search.test.ts (1)

124-142: Consistent field additions for the “missing key” config.

Good to see both positive and negative config paths sharing the same shape.

Same DRY suggestion as in address-search: extract a small createConfig factory for reuse across tests.

src/stores/signalr/signalr-store.ts (4)

54-61: Connection via EventingUrl is in place; verify name vs hubName semantics and handler idempotency.

  • Double-check that name and hubName should be identical. If name is a local alias and hubName is the server hub identifier, equality is fine only if they’re meant to be identical. Otherwise, pass distinct values accordingly.
  • Ensure event handlers aren’t registered multiple times across reconnect attempts. If connectToHubWithEventingUrl or disconnectFromHub doesn’t clear handlers, consider removing existing listeners before re-registering.

If signalRService.off is available, remove prior handlers before adding:

// Before registering handlers (example names)
signalRService.off('personnelStatusUpdated');
signalRService.off('personnelStaffingUpdated');
signalRService.off('unitStatusUpdated');
signalRService.off('callsUpdated');
signalRService.off('callAdded');
signalRService.off('callClosed');
signalRService.off('onConnected');

Alternatively, guard with a local “isConnecting” flag to avoid concurrent connect calls adding duplicate listeners.


62-62: Specify radix in parseInt to avoid edge cases.

Apply this diff:

-      await signalRService.invoke(Env.CHANNEL_HUB_NAME, 'connect', parseInt(securityStore.getState().rights?.DepartmentId ?? '0'));
+      await signalRService.invoke(
+        Env.CHANNEL_HUB_NAME,
+        'connect',
+        parseInt(securityStore.getState().rights?.DepartmentId ?? '0', 10)
+      );

120-127: Clarify error message context.

This catch pertains to the update hub specifically; naming it helps when scanning logs.

-        message: 'Failed to connect to SignalR hubs',
+        message: 'Failed to connect to update SignalR hub',

187-194: Clarify geolocation hub error message.

-        message: 'Failed to connect to SignalR hubs',
+        message: 'Failed to connect to geolocation SignalR hub',
src/stores/app/__tests__/core-store.test.ts (2)

182-206: Add more comprehensive test coverage for fetchConfig.

While the success path test covers the basic scenario, consider adding tests for:

  • Config data with missing fields
  • Config data with invalid types
  • Verifying that all expected fields are correctly stored

Consider adding these additional test cases:

+    it('should handle config with missing fields gracefully', async () => {
+      mockGetActiveUnitId.mockReturnValue(null);
+      mockGetActiveCallId.mockReturnValue(null);
+      
+      const partialConfigData = {
+        EventingUrl: 'https://eventing.example.com/',
+        // Missing other fields
+      } as GetConfigResultData;
+
+      mockGetConfig.mockResolvedValue({
+        Data: partialConfigData,
+      } as any);
+
+      const { result } = renderHook(() => useCoreStore());
+
+      await act(async () => {
+        await result.current.init();
+      });
+
+      expect(result.current.config).toEqual(partialConfigData);
+      expect(result.current.isInitialized).toBe(true);
+    });

145-156: Improve test reliability by avoiding mock clearing mid-test.

Clearing all mocks in the middle of a test (Line 146) could lead to unexpected behavior if other mocks are being used. Consider using more specific mock clearing or resetting.

-      // Clear mock to verify second call doesn't happen
-      jest.clearAllMocks();
+      // Reset specific mock to verify second call doesn't happen
+      mockGetConfig.mockClear();
src/stores/app/core-store.ts (1)

86-92: Consider more specific error handling for config fetch failures.

The current implementation throws a generic error when config fetch fails. Consider preserving the original error for better debugging.

-          // If config fetch failed, don't continue initialization
-          if (get().error) {
-            throw new Error('Config fetch failed, cannot continue initialization');
-          }
+          // If config fetch failed, don't continue initialization
+          const currentError = get().error;
+          if (currentError) {
+            throw new Error(`Config fetch failed: ${currentError}`);
+          }
src/services/__tests__/signalr.service.test.ts (1)

474-499: Consider using waitFor instead of manually advancing timers.

While the test correctly validates reconnection behavior, using waitFor from testing-library would make the test more maintainable and less brittle to timing changes.

-      // Use fake timers for setTimeout
-      jest.useFakeTimers();
-      
-      // Trigger connection close
-      onCloseCallback();
-      
-      // Fast-forward time to trigger reconnection
-      jest.advanceTimersByTime(5000);
-      
-      // Should have called connectToHubWithEventingUrl for reconnection
-      expect(connectSpy).toHaveBeenCalledWith(mockConfig);
-      
-      jest.useRealTimers();
+      // Trigger connection close
+      onCloseCallback();
+      
+      // Wait for reconnection attempt
+      await waitFor(() => {
+        expect(connectSpy).toHaveBeenCalledWith(mockConfig);
+      }, { timeout: 6000 });
src/app/login/__tests__/login-form.test.tsx (3)

6-61: Consider extracting the mock LoginForm to a shared test utility.

The mock LoginForm implementation is quite substantial and might be useful for other test files. Consider moving it to a shared test utilities file for better maintainability and reusability.

Would you like me to help create a shared test utilities file for form component mocks that could be reused across multiple test files?


10-10: Fix the empty destructuring pattern in function parameters.

The mock function has an empty destructuring pattern { } in the default value for onSubmit.

-  const MockLoginForm = ({ onSubmit = () => { }, isLoading = false, error = undefined, onServerUrlPress }: any) => {
+  const MockLoginForm = ({ onSubmit = () => {}, isLoading = false, error = undefined, onServerUrlPress }: any) => {

287-292: Simplify the conditional check for parent element.

The conditional check for the parent element can be simplified since fireEvent.press will handle undefined gracefully.

-    const serverUrlButton = screen.getByText('Server URL').parent;
-    if (serverUrlButton) {
-      fireEvent.press(serverUrlButton);
-      expect(onServerUrlPress).toHaveBeenCalledTimes(1);
-    }
+    const serverUrlButton = screen.getByText('Server URL');
+    fireEvent.press(serverUrlButton.parent!);
+    expect(onServerUrlPress).toHaveBeenCalledTimes(1);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 4b827a8 and 16f1260.

⛔ Files ignored due to path filters (3)
  • .DS_Store is excluded by !**/.DS_Store
  • assets/ios-icon.png is excluded by !**/*.png
  • assets/splash-icon.png is excluded by !**/*.png
📒 Files selected for processing (19)
  • .github/workflows/react-native-cicd.yml (1 hunks)
  • app.config.ts (1 hunks)
  • env.js (0 hunks)
  • jest-setup.ts (1 hunks)
  • src/app/call/new/__tests__/address-search.test.ts (2 hunks)
  • src/app/call/new/__tests__/coordinates-search.test.tsx (2 hunks)
  • src/app/call/new/__tests__/plus-code-search.test.ts (2 hunks)
  • src/app/call/new/__tests__/what3words.test.tsx (1 hunks)
  • src/app/login/__tests__/index.test.tsx (1 hunks)
  • src/app/login/__tests__/login-form.test.tsx (1 hunks)
  • src/app/login/index.tsx (4 hunks)
  • src/app/login/login-form.tsx (2 hunks)
  • src/models/v4/configs/getConfigResultData.ts (1 hunks)
  • src/services/__tests__/signalr.service.test.ts (1 hunks)
  • src/services/signalr.service.ts (5 hunks)
  • src/stores/app/__tests__/core-store.test.ts (4 hunks)
  • src/stores/app/core-store.ts (2 hunks)
  • src/stores/signalr/__tests__/signalr-store.test.ts (1 hunks)
  • src/stores/signalr/signalr-store.ts (2 hunks)
💤 Files with no reviewable changes (1)
  • env.js
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursorrules)

**/*.{ts,tsx}: Write concise, type-safe TypeScript code
Use camelCase for variable and function names (e.g., isFetchingData, handleUserInput)
Use TypeScript for all components, favoring interfaces for props and state
Avoid using any; strive for precise types
Use Expo SecureStore for sensitive data
Use zustand for state management
Use react-query for data fetching and caching
Use react-i18next for internationalization
Use react-native-mmkv for local storage
Use axios for API requests

**/*.{ts,tsx}: Write concise, type-safe TypeScript code
Use camelCase for variable and function names
Use TypeScript for all components, favoring interfaces for props and state
Avoid using any; strive for precise types
Use React Navigation for handling navigation and deep linking
Handle errors gracefully and provide user feedback
Use Expo's SecureStore for sensitive data
Use zustand for state management
Use react-query for data fetching
Use react-i18next for internationalization
Use react-native-mmkv for local storage
Use axios for API requests

Files:

  • app.config.ts
  • src/app/call/new/__tests__/address-search.test.ts
  • src/stores/app/core-store.ts
  • jest-setup.ts
  • src/models/v4/configs/getConfigResultData.ts
  • src/app/call/new/__tests__/what3words.test.tsx
  • src/services/__tests__/signalr.service.test.ts
  • src/app/call/new/__tests__/plus-code-search.test.ts
  • src/stores/signalr/signalr-store.ts
  • src/stores/signalr/__tests__/signalr-store.test.ts
  • src/app/login/__tests__/index.test.tsx
  • src/stores/app/__tests__/core-store.test.ts
  • src/app/login/login-form.tsx
  • src/app/login/index.tsx
  • src/services/signalr.service.ts
  • src/app/login/__tests__/login-form.test.tsx
  • src/app/call/new/__tests__/coordinates-search.test.tsx
**/*

📄 CodeRabbit Inference Engine (.cursorrules)

Directory and file names should be lowercase and hyphenated (e.g., user-profile, chat-screen)

Files:

  • app.config.ts
  • src/app/call/new/__tests__/address-search.test.ts
  • src/stores/app/core-store.ts
  • jest-setup.ts
  • src/models/v4/configs/getConfigResultData.ts
  • src/app/call/new/__tests__/what3words.test.tsx
  • src/services/__tests__/signalr.service.test.ts
  • src/app/call/new/__tests__/plus-code-search.test.ts
  • src/stores/signalr/signalr-store.ts
  • src/stores/signalr/__tests__/signalr-store.test.ts
  • src/app/login/__tests__/index.test.tsx
  • src/stores/app/__tests__/core-store.test.ts
  • src/app/login/login-form.tsx
  • src/app/login/index.tsx
  • src/services/signalr.service.ts
  • src/app/login/__tests__/login-form.test.tsx
  • src/app/call/new/__tests__/coordinates-search.test.tsx
**/*.{test.ts,test.tsx,spec.ts,spec.tsx}

📄 CodeRabbit Inference Engine (.cursorrules)

Create and use Jest tests to validate all generated components

Files:

  • src/app/call/new/__tests__/address-search.test.ts
  • src/app/call/new/__tests__/what3words.test.tsx
  • src/services/__tests__/signalr.service.test.ts
  • src/app/call/new/__tests__/plus-code-search.test.ts
  • src/stores/signalr/__tests__/signalr-store.test.ts
  • src/app/login/__tests__/index.test.tsx
  • src/stores/app/__tests__/core-store.test.ts
  • src/app/login/__tests__/login-form.test.tsx
  • src/app/call/new/__tests__/coordinates-search.test.tsx
**/__tests__/**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursorrules)

Generate tests for all components, services, and logic; ensure tests run without errors

Files:

  • src/app/call/new/__tests__/address-search.test.ts
  • src/app/call/new/__tests__/what3words.test.tsx
  • src/services/__tests__/signalr.service.test.ts
  • src/app/call/new/__tests__/plus-code-search.test.ts
  • src/stores/signalr/__tests__/signalr-store.test.ts
  • src/app/login/__tests__/index.test.tsx
  • src/stores/app/__tests__/core-store.test.ts
  • src/app/login/__tests__/login-form.test.tsx
  • src/app/call/new/__tests__/coordinates-search.test.tsx
**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)

Create and use Jest tests for all generated components

Files:

  • src/app/call/new/__tests__/address-search.test.ts
  • src/app/call/new/__tests__/what3words.test.tsx
  • src/services/__tests__/signalr.service.test.ts
  • src/app/call/new/__tests__/plus-code-search.test.ts
  • src/stores/signalr/__tests__/signalr-store.test.ts
  • src/app/login/__tests__/index.test.tsx
  • src/stores/app/__tests__/core-store.test.ts
  • src/app/login/__tests__/login-form.test.tsx
  • src/app/call/new/__tests__/coordinates-search.test.tsx
**/*.tsx

📄 CodeRabbit Inference Engine (.cursorrules)

**/*.tsx: Use functional components and hooks over class components
Use PascalCase for React component names (e.g., UserProfile, ChatScreen)
Utilize React.FC for defining functional components with props
Minimize useEffect, useState, and heavy computations inside render
Use React.memo() for components with static props to prevent unnecessary re-renders
Optimize FlatList with removeClippedSubviews, maxToRenderPerBatch, windowSize
Use getItemLayout for FlatList when items have consistent size
Avoid anonymous functions in renderItem or event handlers to prevent re-renders
Use gluestack-ui for styling; if no component exists in src/components/ui, style via StyleSheet.create or styled-components
Optimize images using react-native-fast-image
Use React Navigation for navigation and deep linking with best practices
Wrap all user-facing text in t() from react-i18next
Use react-hook-form for form handling
Use @rnmapbox/maps for maps or vehicle navigation
Use lucide-react-native for icons directly in markup; do not use the gluestack-ui icon component
Use the conditional operator (?:) for conditional rendering instead of &&

**/*.tsx: Use functional components and hooks over class components
Use PascalCase for component names
Utilize React.FC for defining functional components with props
Minimize useEffect/useState and heavy computations inside render
Use React.memo for components with static props to prevent unnecessary re-renders
Optimize FlatList with removeClippedSubviews, maxToRenderPerBatch, and windowSize
Use getItemLayout for FlatList when items have consistent size
Avoid anonymous functions in renderItem or event handlers
Optimize image handling using react-native-fast-image
Wrap all user-facing text in t() from react-i18next
Ensure support for dark mode and light mode
Ensure the app is accessible following WCAG for mobile
Use react-hook-form for form handling
Use @rnmapbox/maps for maps and navigation
Use lucide-react-native for icons directly in markup; do not us...

Files:

  • src/app/call/new/__tests__/what3words.test.tsx
  • src/app/login/__tests__/index.test.tsx
  • src/app/login/login-form.tsx
  • src/app/login/index.tsx
  • src/app/login/__tests__/login-form.test.tsx
  • src/app/call/new/__tests__/coordinates-search.test.tsx
{components/ui/**/*.{ts,tsx},**/*.tsx}

📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)

Use gluestack-ui consistently; if no component exists in components/ui, style via StyleSheet.create or styled-components

Files:

  • src/app/call/new/__tests__/what3words.test.tsx
  • src/app/login/__tests__/index.test.tsx
  • src/app/login/login-form.tsx
  • src/app/login/index.tsx
  • src/app/login/__tests__/login-form.test.tsx
  • src/app/call/new/__tests__/coordinates-search.test.tsx
🧠 Learnings (3)
📚 Learning: 2025-08-12T03:33:40.227Z
Learnt from: CR
PR: Resgrid/Unit#0
File: .cursorrules:0-0
Timestamp: 2025-08-12T03:33:40.227Z
Learning: Applies to **/*.{test.ts,test.tsx,spec.ts,spec.tsx} : Create and use Jest tests to validate all generated components

Applied to files:

  • src/app/login/__tests__/index.test.tsx
  • src/app/login/__tests__/login-form.test.tsx
📚 Learning: 2025-08-12T03:33:40.227Z
Learnt from: CR
PR: Resgrid/Unit#0
File: .cursorrules:0-0
Timestamp: 2025-08-12T03:33:40.227Z
Learning: Applies to **/__tests__/**/*.{ts,tsx} : Generate tests for all components, services, and logic; ensure tests run without errors

Applied to files:

  • src/app/login/__tests__/index.test.tsx
📚 Learning: 2025-08-12T03:34:25.963Z
Learnt from: CR
PR: Resgrid/Unit#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-12T03:34:25.963Z
Learning: Applies to **/*.{test,spec}.{ts,tsx} : Create and use Jest tests for all generated components

Applied to files:

  • src/app/login/__tests__/index.test.tsx
🧬 Code Graph Analysis (8)
src/stores/app/core-store.ts (1)
src/lib/logging/index.tsx (2)
  • error (74-76)
  • logger (80-80)
src/services/__tests__/signalr.service.test.ts (2)
src/lib/logging/index.tsx (2)
  • logger (80-80)
  • error (74-76)
src/services/signalr.service.ts (3)
  • signalRService (317-317)
  • SignalRHubConnectConfig (13-18)
  • SignalRHubConfig (7-11)
src/app/login/__tests__/index.test.tsx (3)
src/components/ui/index.tsx (2)
  • View (18-18)
  • TouchableOpacity (18-18)
src/lib/logging/index.tsx (1)
  • error (74-76)
src/app/login/index.tsx (1)
  • Login (17-98)
src/stores/app/__tests__/core-store.test.ts (3)
src/lib/storage/app.tsx (2)
  • getActiveUnitId (24-30)
  • getActiveCallId (35-41)
src/api/config/config.ts (1)
  • getConfig (10-15)
src/models/v4/configs/getConfigResultData.ts (1)
  • GetConfigResultData (1-19)
src/app/login/login-form.tsx (1)
src/components/ui/button/index.tsx (2)
  • Button (334-334)
  • ButtonText (334-334)
src/app/login/index.tsx (3)
src/app/login/login-form.tsx (1)
  • LoginForm (45-184)
src/lib/logging/index.tsx (1)
  • error (74-76)
src/components/settings/server-url-bottom-sheet.tsx (1)
  • ServerUrlBottomSheet (29-120)
src/services/signalr.service.ts (2)
src/lib/logging/index.tsx (2)
  • logger (80-80)
  • error (74-76)
env.js (1)
  • Env (166-166)
src/app/login/__tests__/login-form.test.tsx (2)
src/components/ui/index.tsx (3)
  • View (18-18)
  • TouchableOpacity (18-18)
  • ActivityIndicator (18-18)
src/app/login/login-form.tsx (1)
  • LoginForm (45-184)
🪛 actionlint (1.7.7)
.github/workflows/react-native-cicd.yml

280-280: "github.event.pull_request.body" is potentially untrusted. avoid using it directly in inline scripts. instead, pass it through an environment variable. see https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions for more details

(expression)

⏰ 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: test
🔇 Additional comments (21)
app.config.ts (1)

41-41: iOS-specific icon addition looks good.

The addition of a separate iOS icon configuration is appropriate for platform-specific requirements.

src/app/login/login-form.tsx (2)

19-34: Good practice extracting schema creation into a factory function.

Making the schema creation testable through a factory function is a good testing practice.


176-180: Server URL button implementation looks good.

The conditional rendering and use of the translation key for the button text follows the codebase conventions properly.

src/services/signalr.service.ts (3)

13-18: Good addition of the eventing-based configuration interface.

The new SignalRHubConnectConfig interface properly encapsulates the eventing URL configuration requirements.


62-62: Good URL normalization practice.

Ensuring the EventingUrl has a trailing slash prevents URL concatenation issues.


79-79: Good practice storing configuration for reconnection.

Storing the hub configuration enables robust reconnection with the same settings.

src/app/call/new/__tests__/address-search.test.ts (1)

102-121: Config fixture extended correctly; defaults align with the updated model.

The added fields (EventingUrl, DirectionsMapKey, PersonnelLocationStaleSeconds, UnitLocationStaleSeconds, PersonnelLocationMinMeters, UnitLocationMinMeters, AnalyticsApiKey, AnalyticsHost) on mockConfig match the expanded GetConfigResultData shape and expected default values. Good consistency.

src/app/login/index.tsx (2)

19-19: State for bottom sheet visibility is well-scoped.

Local state showServerUrl is appropriate for the modal’s open/close lifecycle.


95-95: Bottom sheet integration is correct and closes cleanly.

onClose={() => setShowServerUrl(false)} properly resets local state. Nice and contained.

src/app/call/new/__tests__/plus-code-search.test.ts (1)

76-95: Config fixture extended with new fields — matches model defaults.

The added EventingUrl/DirectionsMapKey/…/AnalyticsHost fields and their default values look correct.

src/stores/signalr/signalr-store.ts (3)

41-53: Solid guard for EventingUrl presence; clearer failure mode.

Fetching EventingUrl from core store config and short-circuiting with a logged error is a good move and prevents malformed connections.


150-162: Good: geolocation hub also guarded by EventingUrl presence.

Consistent error handling with the update hub path. Looks solid.


164-169: EventingUrl-based geolocation connection: verify alias vs hub identifiers.

As with the update hub, confirm name and hubName are intended to be equal for geolocation. If not, pass appropriate distinct values.

src/stores/app/core-store.ts (1)

244-255: Good error handling pattern in fetchConfig.

The implementation correctly:

  • Resets the error state on success (Line 247)
  • Sets a descriptive error message (Line 249)
  • Logs the error with context (Lines 250-253)
  • Re-throws the error for upstream handling (Line 254)

This allows callers to handle the error appropriately while maintaining proper error state in the store.

src/app/login/__tests__/index.test.tsx (2)

72-93: Clean mock implementation for LoginForm.

The mock properly exposes test IDs for all interactive elements and correctly handles the optional onServerUrlPress prop. This enables deterministic testing of the login flow and server URL functionality.


127-241: Comprehensive test coverage for the Login component.

The test suite thoroughly covers:

  • Rendering of UI components
  • Server URL bottom sheet open/close flow
  • Error modal display
  • Authentication redirect
  • Analytics tracking
  • Form submission

All tests properly use waitFor for async operations and appropriately mock dependencies.

src/app/call/new/__tests__/coordinates-search.test.tsx (1)

116-131: Config fixtures correctly updated with new fields.

Both mockConfig and configWithoutKey have been properly updated to include all the new fields from GetConfigResultData. The default values match those in the model definition.

Also applies to: 260-275

src/stores/signalr/__tests__/signalr-store.test.ts (2)

152-196: Good test coverage for EventingUrl validation.

The tests properly verify that:

  • Missing EventingUrl prevents connection attempts
  • Appropriate error messages are set and logged
  • The SignalR service is not called when preconditions aren't met

197-212: Proper error handling test for connection failures.

The test correctly validates that connection errors are properly caught, stored in the error state, and logged with appropriate context.

src/services/__tests__/signalr.service.test.ts (2)

72-279: LGTM! Comprehensive test coverage for the new EventingUrl-based connection flow.

The tests thoroughly validate the new connectToHubWithEventingUrl functionality, including:

  • Successful connection setup with proper URL construction
  • Method registration and event handler setup
  • Error handling for missing tokens and EventingUrl
  • Special handling for geolocation hub authentication
  • URL encoding for tokens with special characters
  • Edge cases with trailing slashes and existing query parameters

172-190: LGTM! Excellent coverage of URL encoding edge cases.

The test properly validates that tokens with special characters are correctly encoded for the geolocation hub URL parameter, which is critical for preventing authentication failures.

Comment on lines 212 to 227
it('should handle URL with existing query parameters for geolocation hub', async () => {
const geoConfig: SignalRHubConnectConfig = {
name: 'geoHub',
eventingUrl: 'https://api.example.com/path?existing=param',
hubName: 'geolocationHub', // This should match REALTIME_GEO_HUB_NAME from env
methods: ['onPersonnelLocationUpdated'],
};

await signalRService.connectToHubWithEventingUrl(geoConfig);

// Should append the token parameter correctly
expect(mockBuilderInstance.withUrl).toHaveBeenCalledWith(
'https://api.example.com/path?existing=param/geolocationHub&access_token=mock-token',
{}
);
});
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify the expected URL construction for existing query parameters.

The test expects the URL to be constructed as https://api.example.com/path?existing=param/geolocationHub&access_token=mock-token (line 224), which appears incorrect. When EventingUrl already contains query parameters, the hub name should be appended to the path before the query string, not after.

The expected URL should likely be:

-      'https://api.example.com/path?existing=param/geolocationHub&access_token=mock-token',
+      'https://api.example.com/path/geolocationHub?existing=param&access_token=mock-token',

🏁 Script executed:

#!/bin/bash
# Check how the SignalRService constructs URLs with existing query parameters
ast-grep --pattern 'connectToHubWithEventingUrl' src/services/signalr.service.ts -A 50 | head -100

Length of output: 98


🏁 Script executed:

#!/bin/bash
# Locate the connectToHubWithEventingUrl method in the service files
rg -nP "connectToHubWithEventingUrl" -C 5 src/services

# Inspect how the URL is constructed within that method
rg -nP "withUrl" -C 5 src/services/signalr.service.ts

Length of output: 26959


🏁 Script executed:

#!/bin/bash
# Locate where fullUrl is defined and constructed
rg -nP "fullUrl" -C10 src/services/signalr.service.ts

Length of output: 1642


Fix URL construction for existing query parameters.

The current implementation appends the hub name into the query string (treating the trailing slash as part of the query) rather than inserting it into the path before the query. We should split out the query parameters, append the hub name to the path, then reassemble the query string.

Locations to update:

  • src/services/signalr.service.ts (lines 61–71): Adjust normalizedEventingUrl/fullUrl logic.
  • src/services/__tests__/signalr.service.test.ts (lines 212–227): Update the expected URL.

Suggested diff:

--- a/src/services/signalr.service.ts
+++ b/src/services/signalr.service.ts
@@ 61,8c61,13
-      // Ensure EventingUrl has a trailing slash
-      const normalizedEventingUrl = config.eventingUrl.endsWith('/') ? config.eventingUrl : `${config.eventingUrl}/`;
-
-      let fullUrl = `${normalizedEventingUrl}${config.hubName}`;
+      // Split base path and existing query parameters
+      const [baseUrl, existingQuery] = config.eventingUrl.split('?');
+      const normalizedPath = baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`;
+      // Build URL: always insert hubName into path, then reattach query
+      let fullUrl = existingQuery
+        ? `${normalizedPath}${config.hubName}?${existingQuery}`
+        : `${normalizedPath}${config.hubName}`;

       // For geolocation hub, add token as URL parameter instead of header
       const isGeolocationHub = config.hubName === Env.REALTIME_GEO_HUB_NAME;
@@ 67,7c72,7
-      if (isGeolocationHub) {
+      if (isGeolocationHub) {
         const separator = fullUrl.includes('?') ? '&' : '?';
         fullUrl = `${fullUrl}${separator}access_token=${encodeURIComponent(token)}`;
       }
--- a/src/services/__tests__/signalr.service.test.ts
+++ b/src/services/__tests__/signalr.service.test.ts
@@ it('should handle URL with existing query parameters for geolocation hub', async () => {
-      expect(mockBuilderInstance.withUrl).toHaveBeenCalledWith(
-        'https://api.example.com/path?existing=param/geolocationHub&access_token=mock-token',
-        {}
-      );
+      expect(mockBuilderInstance.withUrl).toHaveBeenCalledWith(
+        'https://api.example.com/path/geolocationHub?existing=param&access_token=mock-token',
+        {}
+      );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('should handle URL with existing query parameters for geolocation hub', async () => {
const geoConfig: SignalRHubConnectConfig = {
name: 'geoHub',
eventingUrl: 'https://api.example.com/path?existing=param',
hubName: 'geolocationHub', // This should match REALTIME_GEO_HUB_NAME from env
methods: ['onPersonnelLocationUpdated'],
};
await signalRService.connectToHubWithEventingUrl(geoConfig);
// Should append the token parameter correctly
expect(mockBuilderInstance.withUrl).toHaveBeenCalledWith(
'https://api.example.com/path?existing=param/geolocationHub&access_token=mock-token',
{}
);
});
it('should handle URL with existing query parameters for geolocation hub', async () => {
const geoConfig: SignalRHubConnectConfig = {
name: 'geoHub',
eventingUrl: 'https://api.example.com/path?existing=param',
hubName: 'geolocationHub', // This should match REALTIME_GEO_HUB_NAME from env
methods: ['onPersonnelLocationUpdated'],
};
await signalRService.connectToHubWithEventingUrl(geoConfig);
// Should append the token parameter correctly
expect(mockBuilderInstance.withUrl).toHaveBeenCalledWith(
'https://api.example.com/path/geolocationHub?existing=param&access_token=mock-token',
{}
);
});
🤖 Prompt for AI Agents
In src/services/signalr.service.ts (lines ~61–71) and the test
src/services/__tests__/signalr.service.test.ts (lines 212–227), the code
currently inserts the hub name into the query string when the eventingUrl
already contains query parameters; instead, parse the incoming eventingUrl into
path and query components, append the hub name to the path (ensuring a single
slash), then reassemble the query string and finally add access_token as an
additional query parameter (merging with existing ones); update the test
expectation to assert the rebuilt URL where the hub segment is in the path and
access_token is appended to the query.

Comment on lines 231 to 240
const hubConfig = this.hubConfigs.get(hubName);
if (hubConfig) {
setTimeout(() => {
this.connectToHubWithEventingUrl(hubConfig);
}, this.RECONNECT_INTERVAL);
} else {
logger.error({
message: `No stored config found for hub: ${hubName}`,
});
}, this.RECONNECT_INTERVAL);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Potential issue with reconnection after authentication token expiry.

The reconnection logic uses the stored configuration but doesn't refresh the authentication token. If the connection closes due to token expiry, the reconnection will fail with the same expired token.

The token should be refreshed before reconnection attempts:

   private handleConnectionClose(hubName: string): void {
     const attempts = this.reconnectAttempts.get(hubName) || 0;
     if (attempts < this.MAX_RECONNECT_ATTEMPTS) {
       this.reconnectAttempts.set(hubName, attempts + 1);
 
       const hubConfig = this.hubConfigs.get(hubName);
       if (hubConfig) {
         setTimeout(() => {
+          // Ensure we have a fresh token before reconnecting
+          const token = useAuthStore.getState().accessToken;
+          if (!token) {
+            logger.error({
+              message: `No authentication token available for reconnection to hub: ${hubName}`,
+            });
+            return;
+          }
           this.connectToHubWithEventingUrl(hubConfig);
         }, this.RECONNECT_INTERVAL);
       } else {
         logger.error({
           message: `No stored config found for hub: ${hubName}`,
         });
       }
     } else {

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/services/signalr.service.ts around lines 231 to 240, the reconnection
branch uses the stored hubConfig as-is which can contain an expired auth token;
before calling connectToHubWithEventingUrl on reconnect, refresh or re-acquire a
current authentication token (or regenerate the auth headers) and update
hubConfig with the new token, then proceed with connectToHubWithEventingUrl; if
token refresh fails, log the failure and avoid attempting the reconnect until a
valid token is obtained.

Comment on lines 19 to 96
// Mock all dependencies before importing anything
jest.mock('@/services/signalr.service', () => {
const mockInstance = {
connectToHubWithEventingUrl: jest.fn().mockResolvedValue(undefined),
disconnectFromHub: jest.fn().mockResolvedValue(undefined),
invoke: jest.fn().mockResolvedValue(undefined),
on: jest.fn(),
connectToHub: jest.fn().mockResolvedValue(undefined),
disconnectAll: jest.fn().mockResolvedValue(undefined),
};
return {
signalRService: mockInstance,
default: mockInstance,
};
});

jest.mock('@/stores/app/core-store', () => ({
useCoreStore: {
getState: jest.fn(() => ({
config: {
EventingUrl: 'https://eventing.example.com/',
},
})),
subscribe: jest.fn(),
setState: jest.fn(),
destroy: jest.fn(),
},
}));

jest.mock('../../app/core-store', () => {
return {
useCoreStore: {
getState: jest.fn(() => ({
config: {
EventingUrl: 'https://eventing.example.com/',
},
})),
subscribe: jest.fn(),
setState: jest.fn(),
destroy: jest.fn(),
},
};
});

jest.mock('@/stores/security/store', () => ({
securityStore: mockSecurityStore,
}));

jest.mock('../../security/store', () => ({
securityStore: mockSecurityStore,
useSecurityStore: mockSecurityStore,
}));

jest.mock('@/lib/logging', () => ({
logger: {
info: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
debug: jest.fn(),
trace: jest.fn(),
fatal: jest.fn(),
},
}));

jest.mock('@/lib/env', () => ({
Env: {
CHANNEL_HUB_NAME: 'eventingHub',
REALTIME_GEO_HUB_NAME: 'geolocationHub',
},
}));

jest.mock('@/lib', () => ({
useAuthStore: {
getState: jest.fn(() => ({
accessToken: 'mock-token',
})),
},
}));
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider consolidating duplicate mock definitions.

There are duplicate mock definitions for useCoreStore (lines 35-46 and 48-61) and securityStore (lines 63-65 and 67-70). This could lead to confusion and maintenance issues.

Remove the duplicate mock definitions:

-jest.mock('../../app/core-store', () => {
-  return {
-    useCoreStore: {
-      getState: jest.fn(() => ({
-        config: {
-          EventingUrl: 'https://eventing.example.com/',
-        },
-      })),
-      subscribe: jest.fn(),
-      setState: jest.fn(),
-      destroy: jest.fn(),
-    },
-  };
-});

-jest.mock('../../security/store', () => ({
-  securityStore: mockSecurityStore,
-  useSecurityStore: mockSecurityStore,
-}));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Mock all dependencies before importing anything
jest.mock('@/services/signalr.service', () => {
const mockInstance = {
connectToHubWithEventingUrl: jest.fn().mockResolvedValue(undefined),
disconnectFromHub: jest.fn().mockResolvedValue(undefined),
invoke: jest.fn().mockResolvedValue(undefined),
on: jest.fn(),
connectToHub: jest.fn().mockResolvedValue(undefined),
disconnectAll: jest.fn().mockResolvedValue(undefined),
};
return {
signalRService: mockInstance,
default: mockInstance,
};
});
jest.mock('@/stores/app/core-store', () => ({
useCoreStore: {
getState: jest.fn(() => ({
config: {
EventingUrl: 'https://eventing.example.com/',
},
})),
subscribe: jest.fn(),
setState: jest.fn(),
destroy: jest.fn(),
},
}));
jest.mock('../../app/core-store', () => {
return {
useCoreStore: {
getState: jest.fn(() => ({
config: {
EventingUrl: 'https://eventing.example.com/',
},
})),
subscribe: jest.fn(),
setState: jest.fn(),
destroy: jest.fn(),
},
};
});
jest.mock('@/stores/security/store', () => ({
securityStore: mockSecurityStore,
}));
jest.mock('../../security/store', () => ({
securityStore: mockSecurityStore,
useSecurityStore: mockSecurityStore,
}));
jest.mock('@/lib/logging', () => ({
logger: {
info: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
debug: jest.fn(),
trace: jest.fn(),
fatal: jest.fn(),
},
}));
jest.mock('@/lib/env', () => ({
Env: {
CHANNEL_HUB_NAME: 'eventingHub',
REALTIME_GEO_HUB_NAME: 'geolocationHub',
},
}));
jest.mock('@/lib', () => ({
useAuthStore: {
getState: jest.fn(() => ({
accessToken: 'mock-token',
})),
},
}));
// Mock all dependencies before importing anything
jest.mock('@/services/signalr.service', () => {
const mockInstance = {
connectToHubWithEventingUrl: jest.fn().mockResolvedValue(undefined),
disconnectFromHub: jest.fn().mockResolvedValue(undefined),
invoke: jest.fn().mockResolvedValue(undefined),
on: jest.fn(),
connectToHub: jest.fn().mockResolvedValue(undefined),
disconnectAll: jest.fn().mockResolvedValue(undefined),
};
return {
signalRService: mockInstance,
default: mockInstance,
};
});
jest.mock('@/stores/app/core-store', () => ({
useCoreStore: {
getState: jest.fn(() => ({
config: {
EventingUrl: 'https://eventing.example.com/',
},
})),
subscribe: jest.fn(),
setState: jest.fn(),
destroy: jest.fn(),
},
}));
jest.mock('@/stores/security/store', () => ({
securityStore: mockSecurityStore,
}));
jest.mock('@/lib/logging', () => ({
logger: {
info: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
debug: jest.fn(),
trace: jest.fn(),
fatal: jest.fn(),
},
}));
jest.mock('@/lib/env', () => ({
Env: {
CHANNEL_HUB_NAME: 'eventingHub',
REALTIME_GEO_HUB_NAME: 'geolocationHub',
},
}));
jest.mock('@/lib', () => ({
useAuthStore: {
getState: jest.fn(() => ({
accessToken: 'mock-token',
})),
},
}));
🤖 Prompt for AI Agents
In src/stores/signalr/__tests__/signalr-store.test.ts around lines 19 to 96,
there are duplicate mock definitions for useCoreStore and securityStore;
consolidate them by keeping a single mock implementation for each module and
remove the duplicate blocks, export that single mock for both import paths used
in the file (e.g., the alias and relative path) or move the mock into a shared
variable and reuse it in both jest.mock calls, ensuring the mock includes
getState, subscribe, setState, destroy (for useCoreStore) and the same
mockSecurityStore reference for securityStore so tests still run and all imports
receive the same mocked instance.

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

♻️ Duplicate comments (2)
src/stores/signalr/__tests__/signalr-store.test.ts (1)

34-50: Remove duplicate mock definitions for better maintainability.

There are two jest.mock calls for the security store module (lines 52-54 and 56-59). This duplication could lead to confusion and maintenance issues. The second mock for ../../security/store appears to be unnecessary since both imports likely resolve to the same module.

Remove the duplicate mock:

 jest.mock('@/stores/security/store', () => ({
   securityStore: mockSecurityStore,
 }));
-
-jest.mock('../../security/store', () => ({
-  securityStore: mockSecurityStore,
-  useSecurityStore: mockSecurityStore,
-}));

If both import paths are actually needed, consolidate them into a single mock registration that handles both paths.

Also applies to: 52-59

src/services/__tests__/signalr.service.test.ts (1)

233-248: Fix URL construction for existing query parameters.

The test expects the URL to be constructed incorrectly when EventingUrl already contains query parameters. The hub name should be appended to the path before the query string, not within it.

The expected URL on line 245 should be:

-        'https://api.example.com/path/geolocationHub?existing=param&access_token=mock-token',
+        'https://api.example.com/path/geolocationHub?existing=param&access_token=mock-token',

Wait, the expected URL actually looks correct. Let me verify the implementation...

#!/bin/bash
# Check how the SignalRService constructs URLs with existing query parameters
rg -nP "const url = new URL" -A20 src/services/signalr.service.ts
🧹 Nitpick comments (1)
.github/workflows/react-native-cicd.yml (1)

296-304: LGTM: Release notes file generation is sound and idempotent

Scoped to the Android leg and falls back cleanly when RELEASE_NOTES is empty. No issues.

If you’d like the header to match the GitHub Release title, consider echoing “Resgrid Unit 7.${{ github.run_number }}” (or similar) instead of “Version …”, purely for consistency.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 16f1260 and 83bb851.

📒 Files selected for processing (5)
  • .github/workflows/react-native-cicd.yml (1 hunks)
  • src/app/login/__tests__/login-form.test.tsx (1 hunks)
  • src/services/__tests__/signalr.service.test.ts (1 hunks)
  • src/services/signalr.service.ts (5 hunks)
  • src/stores/signalr/__tests__/signalr-store.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/app/login/tests/login-form.test.tsx
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursorrules)

**/*.{ts,tsx}: Write concise, type-safe TypeScript code
Use camelCase for variable and function names (e.g., isFetchingData, handleUserInput)
Use TypeScript for all components, favoring interfaces for props and state
Avoid using any; strive for precise types
Use Expo SecureStore for sensitive data
Use zustand for state management
Use react-query for data fetching and caching
Use react-i18next for internationalization
Use react-native-mmkv for local storage
Use axios for API requests

**/*.{ts,tsx}: Write concise, type-safe TypeScript code
Use camelCase for variable and function names
Use TypeScript for all components, favoring interfaces for props and state
Avoid using any; strive for precise types
Use React Navigation for handling navigation and deep linking
Handle errors gracefully and provide user feedback
Use Expo's SecureStore for sensitive data
Use zustand for state management
Use react-query for data fetching
Use react-i18next for internationalization
Use react-native-mmkv for local storage
Use axios for API requests

Files:

  • src/services/__tests__/signalr.service.test.ts
  • src/stores/signalr/__tests__/signalr-store.test.ts
  • src/services/signalr.service.ts
**/*

📄 CodeRabbit Inference Engine (.cursorrules)

Directory and file names should be lowercase and hyphenated (e.g., user-profile, chat-screen)

Files:

  • src/services/__tests__/signalr.service.test.ts
  • src/stores/signalr/__tests__/signalr-store.test.ts
  • src/services/signalr.service.ts
**/*.{test.ts,test.tsx,spec.ts,spec.tsx}

📄 CodeRabbit Inference Engine (.cursorrules)

Create and use Jest tests to validate all generated components

Files:

  • src/services/__tests__/signalr.service.test.ts
  • src/stores/signalr/__tests__/signalr-store.test.ts
**/__tests__/**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursorrules)

Generate tests for all components, services, and logic; ensure tests run without errors

Files:

  • src/services/__tests__/signalr.service.test.ts
  • src/stores/signalr/__tests__/signalr-store.test.ts
**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)

Create and use Jest tests for all generated components

Files:

  • src/services/__tests__/signalr.service.test.ts
  • src/stores/signalr/__tests__/signalr-store.test.ts
🧬 Code Graph Analysis (3)
src/services/__tests__/signalr.service.test.ts (2)
src/lib/logging/index.tsx (2)
  • logger (80-80)
  • error (74-76)
src/services/signalr.service.ts (3)
  • signalRService (359-359)
  • SignalRHubConnectConfig (13-18)
  • SignalRHubConfig (7-11)
src/stores/signalr/__tests__/signalr-store.test.ts (3)
src/services/signalr.service.ts (1)
  • signalRService (359-359)
src/stores/signalr/signalr-store.ts (1)
  • useSignalRStore (25-208)
src/lib/logging/index.tsx (1)
  • logger (80-80)
src/services/signalr.service.ts (2)
src/lib/logging/index.tsx (2)
  • logger (80-80)
  • error (74-76)
env.js (1)
  • Env (166-166)
⏰ 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: test
🔇 Additional comments (9)
src/stores/signalr/__tests__/signalr-store.test.ts (3)

140-163: Test coverage looks comprehensive for missing EventingUrl scenario.

The test properly validates that:

  1. No connection attempt is made when EventingUrl is missing
  2. Error state is set with appropriate message
  3. Error is logged with the correct message

183-198: Good error handling test coverage.

The test effectively validates error propagation and logging when connection fails. The error context is properly included in the log message.


569-607: Excellent test coverage for token refresh failure scenario.

The test thoroughly validates that:

  1. Token refresh is attempted during reconnection
  2. Connection is not attempted when token refresh fails
  3. Error is properly logged with context
  4. The spy correctly verifies that connectToHubWithEventingUrl is not called

This aligns well with the implementation in signalr.service.ts that handles token refresh before reconnection.

src/services/__tests__/signalr.service.test.ts (3)

210-231: Comprehensive test coverage for URL encoding special characters.

The test properly validates that complex access tokens with special characters are correctly URL-encoded when used as query parameters for the geolocation hub. This is crucial for security token handling.


495-526: Well-structured reconnection test with proper timer handling.

The test effectively validates the reconnection flow:

  1. Uses fake timers to control async timing
  2. Verifies token refresh before reconnection
  3. Confirms reconnection attempt with stored config
  4. Properly restores timers and spies

569-607: Robust test for token refresh failure during reconnection.

The test thoroughly validates error handling when token refresh fails:

  1. Mocks token refresh to fail
  2. Verifies no reconnection attempt is made
  3. Confirms error is logged with appropriate context
  4. Uses spy to ensure connectToHubWithEventingUrl is not called

This aligns well with the service implementation's token refresh logic.

src/services/signalr.service.ts (3)

61-84: URL construction correctly handles existing query parameters.

The implementation properly:

  1. Parses the EventingUrl using the URL API
  2. Appends the hub name to the path (not the query)
  3. Preserves existing query parameters
  4. Adds the access_token for geolocation hubs
  5. Manually encodes spaces as %20 instead of +

This matches the test expectations and handles all edge cases correctly.


247-277: Excellent implementation of token refresh during reconnection.

The reconnection logic properly addresses the authentication token expiry issue:

  1. Refreshes the token before attempting reconnection
  2. Validates token availability after refresh
  3. Logs the refresh process for debugging
  4. Handles refresh failures gracefully without attempting reconnection
  5. Preserves the original config for reconnection

This implementation effectively prevents reconnection attempts with expired tokens.


86-89: Good security practice: Token redaction in logs.

The implementation properly redacts the access token in log messages for geolocation hubs using regex replacement. This prevents sensitive token exposure in logs.

Comment on lines 279 to 295
- name: 📋 Extract Release Notes from PR Body
if: ${{ github.event_name == 'pull_request' && matrix.platform == 'android' }}
env:
PR_BODY: ${{ github.event.pull_request.body }}
run: |
set -eo pipefail
# Grab lines after "## Release Notes" until the next header
RELEASE_NOTES="$(printf '%s\n' "$PR_BODY" \
| awk 'f && /^## /{f=0} /^## Release Notes/{f=1; next} f')"
# Use a unique delimiter to write multiline into GITHUB_ENV
delimiter="EOF_$(date +%s)_$RANDOM"
{
echo "RELEASE_NOTES<<$delimiter"
printf '%s\n' "${RELEASE_NOTES:-No release notes provided.}"
echo "$delimiter"
} >> "$GITHUB_ENV"
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Release notes extraction step will never run due to job-level event gating

The build-and-deploy job only runs on push/workflow_dispatch, but this step is gated to pull_request events. As a result, RELEASE_NOTES is never populated from the PR body, and downstream steps will always use the fallback.

Fix by removing the event gate and restricting to the android matrix leg. Also make the header match case-insensitive and more whitespace-tolerant.

-      - name: 📋 Extract Release Notes from PR Body
-        if: ${{ github.event_name == 'pull_request' && matrix.platform == 'android' }}
+      - name: 📋 Extract Release Notes from PR Body
+        if: ${{ matrix.platform == 'android' }}
         env:
           PR_BODY: ${{ github.event.pull_request.body }}
         run: |
-          set -eo pipefail
-          # Grab lines after "## Release Notes" until the next header
-          RELEASE_NOTES="$(printf '%s\n' "$PR_BODY" \
-            | awk 'f && /^## /{f=0} /^## Release Notes/{f=1; next} f')"
+          set -eo pipefail
+          # Grab lines after "## Release Notes" (case-insensitive) until the next "## " header
+          RELEASE_NOTES="$(printf '%s\n' "$PR_BODY" \
+            | awk 'BEGIN{IGNORECASE=1} f && /^##[[:space:]]/{f=0} /^##[[:space:]]*Release Notes[[:space:]]*$/ {f=1; next} f')"
           # Use a unique delimiter to write multiline into GITHUB_ENV
           delimiter="EOF_$(date +%s)_$RANDOM"
           {
             echo "RELEASE_NOTES<<$delimiter"
             printf '%s\n' "${RELEASE_NOTES:-No release notes provided.}"
             echo "$delimiter"
           } >> "$GITHUB_ENV"

If you truly need PR-body-driven notes on push (post-merge), consider fetching the merged PR’s body via the GitHub API (gh or curl) in push/workflow_dispatch runs.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: 📋 Extract Release Notes from PR Body
if: ${{ github.event_name == 'pull_request' && matrix.platform == 'android' }}
env:
PR_BODY: ${{ github.event.pull_request.body }}
run: |
set -eo pipefail
# Grab lines after "## Release Notes" until the next header
RELEASE_NOTES="$(printf '%s\n' "$PR_BODY" \
| awk 'f && /^## /{f=0} /^## Release Notes/{f=1; next} f')"
# Use a unique delimiter to write multiline into GITHUB_ENV
delimiter="EOF_$(date +%s)_$RANDOM"
{
echo "RELEASE_NOTES<<$delimiter"
printf '%s\n' "${RELEASE_NOTES:-No release notes provided.}"
echo "$delimiter"
} >> "$GITHUB_ENV"
- name: 📋 Extract Release Notes from PR Body
if: ${{ matrix.platform == 'android' }}
env:
PR_BODY: ${{ github.event.pull_request.body }}
run: |
- set -eo pipefail
- # Grab lines after "## Release Notes" until the next header
- RELEASE_NOTES="$(printf '%s\n' "$PR_BODY" \
set -eo pipefail
# Grab lines after "## Release Notes" (case-insensitive) until the next "## " header
RELEASE_NOTES="$(printf '%s\n' "$PR_BODY" \
| awk 'BEGIN{IGNORECASE=1} f && /^##[[:space:]]/{f=0} /^##[[:space:]]*Release Notes[[:space:]]*$/ {f=1; next} f')"
# Use a unique delimiter to write multiline into GITHUB_ENV
delimiter="EOF_$(date +%s)_$RANDOM"
{
echo "RELEASE_NOTES<<$delimiter"
printf '%s\n' "${RELEASE_NOTES:-No release notes provided.}"
echo "$delimiter"
} >> "$GITHUB_ENV"

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

♻️ Duplicate comments (1)
.github/workflows/react-native-cicd.yml (1)

279-295: Make Release Notes extraction resilient on push/workflow_dispatch by fetching the PR body

Currently this job never runs on pull_request (job-level if), so PR_BODY is empty on push/workflow_dispatch and the notes always fall back. Fetch the associated PR body via the GitHub API when not a PR event. Also make the header match case-insensitive and whitespace-tolerant to be more robust.

Apply:

       - name: 📋 Extract Release Notes from PR Body
-        if: ${{ matrix.platform == 'android' }}
+        if: ${{ matrix.platform == 'android' }}
         env:
           PR_BODY: ${{ github.event.pull_request.body }}
+          GITHUB_TOKEN: ${{ github.token }}
         run: |
-          set -eo pipefail
-          # Grab lines after "## Release Notes" until the next header
-          RELEASE_NOTES="$(printf '%s\n' "$PR_BODY" \
-            | awk 'f && /^## /{f=0} /^## Release Notes/{f=1; next} f')"
+          set -euo pipefail
+          BODY="${PR_BODY:-}"
+          # On push/workflow_dispatch, pull_request.body is unavailable; try to resolve the PR for this commit
+          if [ -z "$BODY" ]; then
+            PRS_JSON="$(curl -sS -H "Authorization: Bearer $GITHUB_TOKEN" -H "Accept: application/vnd.github+json" \
+              "https://api.github.com/repos/${{ github.repository }}/commits/${{ github.sha }}/pulls")"
+            PR_NUMBER="$(printf '%s' "$PRS_JSON" | jq -r '.[0].number // empty')"
+            if [ -n "$PR_NUMBER" ]; then
+              BODY="$(curl -sS -H "Authorization: Bearer $GITHUB_TOKEN" -H "Accept: application/vnd.github+json" \
+                "https://api.github.com/repos/${{ github.repository }}/pulls/$PR_NUMBER" | jq -r '.body // empty')"
+            fi
+          fi
+          # Grab lines after "## Release Notes" (case-insensitive) until the next "## " header
+          RELEASE_NOTES="$(printf '%s\n' "$BODY" \
+            | awk 'BEGIN{IGNORECASE=1} f && /^##[[:space:]]/{f=0} /^##[[:space:]]*Release Notes[[:space:]]*$/ {f=1; next} f')"
           # Use a unique delimiter to write multiline into GITHUB_ENV
           delimiter="EOF_$(date +%s)_$RANDOM"
           {
             echo "RELEASE_NOTES<<$delimiter"
             printf '%s\n' "${RELEASE_NOTES:-No release notes provided.}"
             echo "$delimiter"
           } >> "$GITHUB_ENV"

Verification:

  • On push/workflow_dispatch: step resolves the PR for the commit and populates RELEASE_NOTES when available.
  • On pull_request (if you ever enable this job for PRs): it uses the provided PR_BODY safely (no shell expansion).
🧹 Nitpick comments (1)
.github/workflows/react-native-cicd.yml (1)

305-315: Optional: Avoid overwriting body on reruns; keep release notes stable

On reruns with allowUpdates: true, you may want to keep the existing body intact. Add omitBodyDuringUpdate: true to prevent overwriting notes.

       uses: ncipollo/release-action@v1
       with:
         tag: "7.${{ github.run_number }}"
         commit: ${{ github.sha }}
         makeLatest: true
         allowUpdates: true
+        omitBodyDuringUpdate: true
         name: "7.${{ github.run_number }}"
         artifacts: "./ResgridUnit-prod.apk"
         bodyFile: "RELEASE_NOTES.md"
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 83bb851 and 174bb40.

📒 Files selected for processing (1)
  • .github/workflows/react-native-cicd.yml (1 hunks)
⏰ 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: test
🔇 Additional comments (1)
.github/workflows/react-native-cicd.yml (1)

296-304: LGTM: Scoped release notes file avoids mutating CHANGELOG

Good move to generate a per-run RELEASE_NOTES.md with a fallback. This keeps artifacts clean and avoids repo mutations during CI.

@ucswift
Copy link
Member Author

ucswift commented Aug 15, 2025

Approve

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

This PR is approved.

@ucswift ucswift merged commit 7557abd into master Aug 15, 2025
5 of 7 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Nov 1, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants