Skip to content

RD-T43 Expo 56, Livekit 2.10 and Gluestack v5 update - #121

Merged
ucswift merged 2 commits into
masterfrom
develop
Jul 31, 2026
Merged

RD-T43 Expo 56, Livekit 2.10 and Gluestack v5 update#121
ucswift merged 2 commits into
masterfrom
develop

Conversation

@ucswift

@ucswift ucswift commented Jul 31, 2026

Copy link
Copy Markdown
Member

Pull Request Description

RD-T43: Expo SDK 56, NativeWind v5, LiveKit 2.10, and Gluestack UI v5 Upgrade

This PR is a major framework upgrade that migrates the Resgrid Dispatch app to Expo SDK 56, NativeWind v5 (Tailwind CSS v4), Gluestack UI v5, and LiveKit 2.10, along with significant dispatch console performance improvements, Electron security hardening, and authentication enhancements.


Framework & Library Upgrades

NativeWind v4 → v5 (Tailwind CSS v3 → v4)

  • Rewrote global.css to Tailwind v4 syntax (@import, @theme, @custom-variant) with full light/dark design token scales defined as CSS custom properties
  • Dark mode now driven by class-based @custom-variant and Appearance.setColorScheme instead of NativeWind's colorScheme.set()
  • Removed NativeWind babel preset; styling wired through Metro/PostCSS
  • Replaced all cssInterop() calls with NativeWind v5's styled() wrapper across all Gluestack UI components
  • Updated shadow/rounded utility classes (shadow-smshadow-xs, rounded-smrounded-xs)

Gluestack UI v3 → v5

  • All UI component imports migrated to @gluestack-ui/core/* creator paths
  • Utils sourced from @gluestack-ui/utils/nativewind-utils
  • Shared UIIcon from core replaces per-component PrimitiveIcon definitions
  • Platform-specific code paths (withStyleContextAndStates, withStates) unified
  • GluestackUIProvider simplified — removed inline CSS variable injection, now uses class-based theming
  • Forward refs updated to React.ComponentRef; context values memoized
  • Patched Actionsheet to use useWindowDimensions for reactive height

Animation Migration (Legend Motion → Reanimated v4)

  • Modal, Drawer, Popover, AlertDialog, Menu, and Tooltip animations migrated from @legendapp/motion to react-native-reanimated entering/exiting patterns
  • Custom Jest resolver added for Reanimated v4 worklets compatibility
  • Web skeleton loading animation switched to CSS keyframes

Dispatch Console Performance & Features

Performance

  • Store hooks refactored to use fine-grained selectors, eliminating whole-store subscriptions that caused unnecessary re-renders
  • Call, unit, and personnel lists migrated from ScrollView.map() to FlatList with virtualization (removeClippedSubviews, windowing)
  • List items wrapped in React.memo with stable, useCallback-derived handlers
  • SignalR-triggered data refetches debounced by 2 seconds to prevent burst requests
  • Check-in timer polling keyed by sorted call-ID signature instead of array reference
  • Web map markers now diffed by signature instead of fully recreated on every update
  • Web animated refresh icon replaced with pure CSS keyframe animation
  • Video player and call form HTML memoized

Features

  • Card collapse/expand state persisted to device storage via Zustand persist middleware
  • DashboardViewToggles moved into the app header for all responsive layouts
  • New single-list layouts for tablet portrait and landscape orientations
  • Weather alerts surface as auto-dismissing toasts with tap-to-navigate; extreme/severe counts shown in the stats header bar
  • Toast notifications support optional onPress callback and custom duration
  • Active calls panel fetch dispatches in bounded concurrency batches (4 parallel)

Electron Desktop Improvements

  • Deep linking: Registered ResgridDispatch:// URL scheme with single-instance lock, open-url (macOS) and second-instance (Windows/Linux) handlers, and renderer bridge via preload IPC
  • Security hardening: Window-open handler denies all popups (http(s) sent to OS browser), in-window navigation restricted to app origin, and custom protocol handler validates against path traversal (backslash/.. escape)
  • CSP updates: Added Mapbox GL stylesheet, blob workers, child-src blob:, and plain HTTP/WebSocket for self-hosted Resgrid servers
  • Application menu: Minimal edit menu for copy/paste keyboard shortcuts (especially macOS)

Authentication & Security

  • Lockscreen verification: Password now verified against a stored salted SHA-256 hash (cached after successful password-grant login); SSO/OIDC sessions without a stored hash require re-login
  • Log sanitization: Auth API and SAML deep-link error logging stripped of credentials, tokens, and response bodies; auth response logging replaced with boolean flags
  • URL validation: Server URL field now requires HTTPS, except for localhost/127.0.0.1

Bug Fixes & Robustness

  • invertColor() handles empty, alpha-channel, and invalid hex without throwing
  • parseDateISOString() returns null instead of throwing on invalid input
  • getTimeAgo()/getTimeAgoUtc() handle non-numeric and NaN timestamps
  • Countly analytics on web now sends events via direct HTTP POST instead of no-op
  • Web AudioContext created lazily and auto-resumed on first user gesture (autoplay policy)
  • Bluetooth audio scanning stops when the modal closes or component unmounts
  • SignalR event listener exceptions caught and logged rather than crashing
  • SignalR logging level reduced from Information to Warning
  • SignalR visibility-based reconnect logic skips Electron (where minimize hides the window)
  • SignalR lastUpdateMessage set to null instead of stringifying potentially large payloads
  • Call images FlatList key extractor falls back to index/URL instead of random IDs
  • Call form renderer validates and neutralizes JSON schema before injecting into WebView
  • Web file download implemented via blob URL + anchor click
  • Map geolocate control set to low accuracy to reduce battery and permission friction

Testing & Infrastructure

  • All test mocks updated for NativeWind v5 (styled added to nativewind mocks)
  • Gesture handler mock rewritten for RNGH 2.28 (no longer ships src/mocks.js)
  • Notifee web mock added for Metro resolution
  • Jest transformIgnorePatterns updated to include nativewind and react-native-css
  • CI release notes generation replaces "PR" with "Release" in output text

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Too many files!

This PR contains 189 files, which is 89 over the limit of 100.

To get a review, narrow the scope:
• coderabbit review --committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

Upgrade to a paid plan to raise the limit.

This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7d7720e6-33ec-4817-8c81-16cb17207f89

📥 Commits

Reviewing files that changed from the base of the PR and between d133a48 and 3e4f3aa.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (189)
  • .github/workflows/react-native-cicd.yml
  • .vscode/settings.json
  • __mocks__/@notifee/react-native.web.js
  • __mocks__/react-native-gesture-handler.ts
  • babel.config.js
  • electron/main.js
  • electron/preload.js
  • global.css
  • jest-setup.ts
  • jest.config.js
  • jest.resolver.js
  • metro.config.js
  • package.json
  • patches/@gluestack-ui+core+5.0.15.patch
  • patches/react-native-css-interop+0.1.22.patch
  • postcss.config.mjs
  • react-native-css-env.d.ts
  • src/__tests__/app/call/[id].security.test.tsx
  • src/__tests__/app/call/[id].test.tsx
  • src/__tests__/app/call/new/what3words.test.tsx
  • src/__tests__/app/calls.test.tsx
  • src/__tests__/app/index.test.tsx
  • src/__tests__/app/login/index.web.test.tsx
  • src/__tests__/app/login/login-form.test.tsx
  • src/__tests__/app/root/lockscreen.test.tsx
  • src/app/(app)/_layout.tsx
  • src/app/(app)/home.tsx
  • src/app/(app)/home.web.tsx
  • src/app/(app)/map.tsx
  • src/app/(app)/map.web.tsx
  • src/app/call/[id].tsx
  • src/app/lockscreen.tsx
  • src/app/login/index.web.tsx
  • src/app/maintenance.tsx
  • src/app/weather-alerts/[id].tsx
  • src/components/bluetooth/bluetooth-audio-modal.tsx
  • src/components/callVideoFeeds/video-player.tsx
  • src/components/calls/__tests__/call-notes-modal-new.test.tsx
  • src/components/calls/__tests__/close-call-bottom-sheet.test.tsx
  • src/components/calls/__tests__/dispatch-selection-basic.test.tsx
  • src/components/calls/__tests__/dispatch-selection-modal.test.tsx
  • src/components/calls/call-card.web.tsx
  • src/components/calls/call-files-modal.tsx
  • src/components/calls/call-form-renderer.tsx
  • src/components/calls/call-images-modal.tsx
  • src/components/calls/call-notes-modal.tsx
  • src/components/checkIn/__tests__/check-in-bottom-sheet.test.tsx
  • src/components/checkIn/__tests__/check-in-history-list.test.tsx
  • src/components/checkIn/__tests__/check-in-tab.test.tsx
  • src/components/checkIn/__tests__/check-in-timer-card.test.tsx
  • src/components/common/loading.tsx
  • src/components/contacts/contact-card.tsx
  • src/components/contacts/contact-details-sheet.tsx
  • src/components/dispatch-console/__tests__/add-note-bottom-sheet.test.tsx
  • src/components/dispatch-console/__tests__/animated-refresh-icon.test.tsx
  • src/components/dispatch-console/active-calls-panel.tsx
  • src/components/dispatch-console/activity-log-panel.tsx
  • src/components/dispatch-console/animated-refresh-icon.web.tsx
  • src/components/dispatch-console/dashboard-view-toggles.tsx
  • src/components/dispatch-console/map-widget.tsx
  • src/components/dispatch-console/notes-panel.tsx
  • src/components/dispatch-console/panel-header.tsx
  • src/components/dispatch-console/personnel-actions-panel.tsx
  • src/components/dispatch-console/personnel-panel.tsx
  • src/components/dispatch-console/resources-panel.tsx
  • src/components/dispatch-console/stats-header.tsx
  • src/components/dispatch-console/unit-actions-panel.tsx
  • src/components/dispatch-console/units-panel.tsx
  • src/components/incident-command/command-map.tsx
  • src/components/maps/__tests__/pin-actions.test.tsx
  • src/components/maps/map-pins.tsx
  • src/components/maps/pin-marker.tsx
  • src/components/maps/unified-map-view.tsx
  • src/components/maps/unified-map-view.web.tsx
  • src/components/notes/__tests__/note-details-sheet.test.tsx
  • src/components/notes/note-card.tsx
  • src/components/notifications/NotificationDetail.tsx
  • src/components/notifications/NotificationInbox.tsx
  • src/components/protocols/__tests__/protocol-details-sheet.test.tsx
  • src/components/protocols/protocol-card.tsx
  • src/components/roles/__tests__/roles-bottom-sheet.test.tsx
  • src/components/settings/__tests__/login-info-bottom-sheet-simple.test.tsx
  • src/components/settings/__tests__/login-info-bottom-sheet.test.tsx
  • src/components/settings/__tests__/server-url-bottom-sheet-simple.test.tsx
  • src/components/status/__tests__/status-bottom-sheet.test.tsx
  • src/components/toast/toast.tsx
  • src/components/ui/__tests__/bottom-sheet.test.tsx
  • src/components/ui/accordion/index.tsx
  • src/components/ui/actionsheet/index.tsx
  • src/components/ui/alert-dialog/index.tsx
  • src/components/ui/alert/index.tsx
  • src/components/ui/avatar/index.tsx
  • src/components/ui/badge/index.tsx
  • src/components/ui/bottomsheet/index.tsx
  • src/components/ui/box/index.tsx
  • src/components/ui/box/index.web.tsx
  • src/components/ui/box/styles.tsx
  • src/components/ui/button/index.tsx
  • src/components/ui/card/index.tsx
  • src/components/ui/card/index.web.tsx
  • src/components/ui/card/styles.tsx
  • src/components/ui/center/index.tsx
  • src/components/ui/center/index.web.tsx
  • src/components/ui/center/styles.tsx
  • src/components/ui/checkbox/index.tsx
  • src/components/ui/divider/index.tsx
  • src/components/ui/drawer/index.tsx
  • src/components/ui/fab/index.tsx
  • src/components/ui/focus-aware-status-bar.tsx
  • src/components/ui/form-control/index.tsx
  • src/components/ui/gluestack-ui-provider/config.ts
  • src/components/ui/gluestack-ui-provider/index.tsx
  • src/components/ui/gluestack-ui-provider/index.web.tsx
  • src/components/ui/grid/index.tsx
  • src/components/ui/grid/index.web.tsx
  • src/components/ui/grid/styles.tsx
  • src/components/ui/heading/index.tsx
  • src/components/ui/heading/index.web.tsx
  • src/components/ui/heading/styles.tsx
  • src/components/ui/hstack/index.tsx
  • src/components/ui/hstack/index.web.tsx
  • src/components/ui/hstack/styles.tsx
  • src/components/ui/icon/index.tsx
  • src/components/ui/icon/index.web.tsx
  • src/components/ui/image-background/index.tsx
  • src/components/ui/image.tsx
  • src/components/ui/image/index.tsx
  • src/components/ui/index.tsx
  • src/components/ui/input/index.tsx
  • src/components/ui/link/index.tsx
  • src/components/ui/menu/index.tsx
  • src/components/ui/modal/index.tsx
  • src/components/ui/popover/index.tsx
  • src/components/ui/portal/index.tsx
  • src/components/ui/pressable/index.tsx
  • src/components/ui/progress/index.tsx
  • src/components/ui/radio/index.tsx
  • src/components/ui/select/index.tsx
  • src/components/ui/select/select-actionsheet.tsx
  • src/components/ui/skeleton/index.tsx
  • src/components/ui/skeleton/index.web.tsx
  • src/components/ui/skeleton/styles.tsx
  • src/components/ui/slider/index.tsx
  • src/components/ui/spinner/index.tsx
  • src/components/ui/switch/index.tsx
  • src/components/ui/text/index.tsx
  • src/components/ui/text/index.web.tsx
  • src/components/ui/text/styles.tsx
  • src/components/ui/textarea/index.tsx
  • src/components/ui/toast/index.tsx
  • src/components/ui/tooltip/index.tsx
  • src/components/ui/utils/use-break-point-value.ts
  • src/components/ui/vstack/index.tsx
  • src/components/ui/vstack/index.web.tsx
  • src/components/ui/vstack/styles.tsx
  • src/hooks/use-saml-login.ts
  • src/lib/auth/api.tsx
  • src/lib/countly.ts
  • src/lib/dispatch-helpers.ts
  • src/lib/hooks/__tests__/use-selected-theme.test.ts
  • src/lib/hooks/use-selected-theme.tsx
  • src/lib/hooks/use-selected-theme.web.tsx
  • src/lib/i18n/utils.web.tsx
  • src/lib/server-url.ts
  • src/lib/storage/index.tsx
  • src/lib/storage/index.web.tsx
  • src/lib/utils.ts
  • src/services/__tests__/signalr.service.test.ts
  • src/services/audio.service.web.ts
  • src/services/signalr.service.ts
  • src/stores/app/audio-stream-store.ts
  • src/stores/app/livekit-store.ts
  • src/stores/auth/store.tsx
  • src/stores/dispatch/dashboard-view-store.ts
  • src/stores/signalr/signalr-store.ts
  • src/stores/toast/store.ts
  • src/stores/weatherAlerts/store.ts
  • src/translations/ar.json
  • src/translations/de.json
  • src/translations/en.json
  • src/translations/es.json
  • src/translations/fr.json
  • src/translations/it.json
  • src/translations/pl.json
  • src/translations/sv.json
  • src/translations/uk.json
  • tailwind.config.js
  • tsconfig.json
  • webpack.config.js

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


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.

@Resgrid-Bot

This comment has been minimized.

Comment thread electron/main.js
// Deep-link scheme matching the app's linking scheme (env.js SCHEME).
// URL schemes are case-insensitive; argv/open-url matching is done lowercased.
const deepLinkScheme = 'ResgridDispatch';
const deepLinkPrefix = deepLinkScheme.toLowerCase() + '://';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

String concatenation operator used to assemble the prefix string. Refactor to use a template literal (e.g., ${deepLinkScheme.toLowerCase()}://) for improved readability and fewer errors.

Kody rule violation: Use Template Literals Instead of String Concatenation

Prompt for LLM

File electron/main.js:

Line 32:

String concatenation operator used to assemble the prefix string. Refactor to use a template literal (e.g., `${deepLinkScheme.toLowerCase()}://`) for improved readability and fewer errors.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Comment thread electron/preload.js
try {
callback(deepLinkUrl);
} catch (err) {
console.error('Error in deep-link callback:', err);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Unstructured error logging detected in the deep-link catch block. Replace console.error with a structured logger.error call, including operation names and relevant identifiers for searchability and correlation.

Kody rule violation: Include error context in structured logs

Prompt for LLM

File electron/preload.js:

Line 66:

Unstructured error logging detected in the `deep-link` catch block. Replace `console.error` with a structured `logger.error` call, including operation names and relevant identifiers for searchability and correlation.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Comment thread electron/preload.js Outdated
Comment on lines +62 to +68
ipcRenderer.on('deep-link', (_event, deepLinkUrl) => {
try {
callback(deepLinkUrl);
} catch (err) {
console.error('Error in deep-link callback:', err);
}
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Memory leak vulnerability identified in onDeepLink due to a missing unsubscribe function for ipcRenderer.on. Return a deterministic cleanup function (e.g., () => ipcRenderer.removeListener('deep-link', handler)) to prevent duplicate listener stacking.

Kody rule violation: Provide error handlers to subscription/listener APIs

Prompt for LLM

File electron/preload.js:

Line 62 to 68:

Memory leak vulnerability identified in `onDeepLink` due to a missing unsubscribe function for `ipcRenderer.on`. Return a deterministic cleanup function (e.g., `() => ipcRenderer.removeListener('deep-link', handler)`) to prevent duplicate listener stacking.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Comment thread electron/preload.js Outdated
Comment on lines +62 to +68
ipcRenderer.on('deep-link', (_event, deepLinkUrl) => {
try {
callback(deepLinkUrl);
} catch (err) {
console.error('Error in deep-link callback:', err);
}
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

Resource leak identified where ipcRenderer.on event listeners are never removed. Store the listener reference and expose a removal function to allow proper cleanup on unmount.

Kody rule violation: Proper memory management in event listeners

Prompt for LLM

File electron/preload.js:

Line 62 to 68:

Resource leak identified where `ipcRenderer.on` event listeners are never removed. Store the listener reference and expose a removal function to allow proper cleanup on unmount.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Comment thread src/app/(app)/_layout.tsx
});
})();

// Swallow post-timeout rejections so they don't surface as unhandled, and

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Bug critical

Deadlock state in initialization logic permanently blocks retries if initPromise never settles. Reset isInitializing.current = false in the catch block after the 30-second timeout rejection to unblock the initializeApp guard at line 101.

  initPromise
    .catch((error) => {
      if (initTimedOut.current) {
        logger.error({
          message: 'App initialization failed after initialization timeout',
          context: { error, platform: Platform.OS },
        });
      }
    })
    .finally(() => {
      isInitializing.current = false;
    });

  await Promise.race([initPromise, initTimeout]);
} catch (error) {
  logger.error({
    message: 'Failed to initialize app',
    context: { error, platform: Platform.OS },
  });
  // Reset initialization state on error so it can be retried
  hasInitialized.current = false;
  // If the init promise is still hanging, clear the guard so a retry is possible
  isInitializing.current = false;
}
Prompt for LLM

File src/app/(app)/_layout.tsx:

Line 198:

Deadlock state in initialization logic permanently blocks retries if `initPromise` never settles. Reset `isInitializing.current = false` in the catch block after the 30-second timeout rejection to unblock the `initializeApp` guard at line 101.

Suggested Code:

      initPromise
        .catch((error) => {
          if (initTimedOut.current) {
            logger.error({
              message: 'App initialization failed after initialization timeout',
              context: { error, platform: Platform.OS },
            });
          }
        })
        .finally(() => {
          isInitializing.current = false;
        });

      await Promise.race([initPromise, initTimeout]);
    } catch (error) {
      logger.error({
        message: 'Failed to initialize app',
        context: { error, platform: Platform.OS },
      });
      // Reset initialization state on error so it can be retried
      hasInitialized.current = false;
      // If the init promise is still hanging, clear the guard so a retry is possible
      isInitializing.current = false;
    }

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Comment thread src/app/(app)/home.tsx Outdated
weatherLongitude={mapCenterLongitude}
extremeAlerts={extremeAlertCount}
severeAlerts={severeAlertCount}
onWeatherAlertsPress={() => router.push('/(app)/weather-alerts' as Href)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Performance degradation caused by inline arrow functions within JSX props. Move function definitions outside the render method to prevent unnecessary memory allocations and re-renders.

Kody rule violation: Avoid using .bind() or arrow functions in JSX props

Prompt for LLM

File src/app/(app)/home.tsx:

Line 950:

Performance degradation caused by inline arrow functions within JSX props. Move function definitions outside the render method to prevent unnecessary memory allocations and re-renders.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

const setMapCenter = useDispatchConsoleStore((s) => s.setMapCenter);

// Local state
const [currentTime, setCurrentTime] = useState(new Date().toLocaleTimeString('en-US', { hour12: false }));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Performance high

Unnecessary state re-render detected in DispatchConsoleWeb due to an unused currentTime setInterval. Remove the currentTime useState and the setInterval useEffect (lines 140 and 152-159) to eliminate redundant renders.

// Removed: currentTime is no longer passed to StatsHeader (it manages its own clock)
// const [currentTime, setCurrentTime] = useState(...);
// useEffect(() => { ... setInterval ... }, []);
Prompt for LLM

File src/app/(app)/home.web.tsx:

Line 140:

Unnecessary state re-render detected in `DispatchConsoleWeb` due to an unused `currentTime` `setInterval`. Remove the `currentTime` useState and the `setInterval` useEffect (lines 140 and 152-159) to eliminate redundant renders.

Suggested Code:

// Removed: currentTime is no longer passed to StatsHeader (it manages its own clock)
// const [currentTime, setCurrentTime] = useState(...);
// useEffect(() => { ... setInterval ... }, []);

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Comment thread src/app/(app)/map.tsx Outdated
logger.info({
message: 'Location updated and map is ready',
context: {
latitude: location.latitude,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

PII exposure risk identified by logging raw geolocation latitude. Hash or bucket the value (e.g., Math.round(location.latitude * 10) / 10) to comply with GDPR/LGPD data minimization requirements.

Kody rule violation: Mask PII and secrets in logs

Prompt for LLM

File src/app/(app)/map.tsx:

Line 173:

PII exposure risk identified by logging raw geolocation latitude. Hash or bucket the value (e.g., `Math.round(location.latitude * 10) / 10`) to comply with GDPR/LGPD data minimization requirements.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Comment thread src/app/(app)/map.tsx Outdated
logger.info({
message: 'Location updated and map is ready',
context: {
latitude: location.latitude,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

GDPR compliance violation detected from emitting verbatim latitude in diagnostic logs. Replace raw values with bucketed/hashed coordinates and append gdpr: { purpose: 'map_tracking', lawful_basis: 'consent' } to the context payload.

Kody rule violation: Redact PII in logs and metrics by default

Prompt for LLM

File src/app/(app)/map.tsx:

Line 173:

GDPR compliance violation detected from emitting verbatim latitude in diagnostic logs. Replace raw values with bucketed/hashed coordinates and append `gdpr: { purpose: 'map_tracking', lawful_basis: 'consent' }` to the context payload.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

<html><head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
<script src="https://cdn.jsdelivr.net/npm/hls.js@1"></script>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

Unsafe third-party script injection detected for hls.js. Utilize the Next.js <Script> component with an appropriate loading strategy and onLoad callbacks instead of raw inline <script> tags.

Kody rule violation: Use next/script safely and avoid inline scripts

Prompt for LLM

File src/components/callVideoFeeds/video-player.tsx:

Line 51:

Unsafe third-party script injection detected for `hls.js`. Utilize the Next.js `<Script>` component with an appropriate loading strategy and `onLoad` callbacks instead of raw inline `<script>` tags.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

const fileName = file.FileName || file.Name || `file_${file.Id}`;

if (Platform.OS === 'web') {
const objectUrl = URL.createObjectURL(fileData);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Unhandled exception risk found in the web-platform download path during URL.createObjectURL execution. Wrap the DOM API operations in a try/catch block and utilize finally to clear the downloadingFiles state during failures.

Kody rule violation: Add try-catch blocks for external calls

Prompt for LLM

File src/components/calls/call-files-modal.tsx:

Line 111:

Unhandled exception risk found in the web-platform download path during `URL.createObjectURL` execution. Wrap the DOM API operations in a try/catch block and utilize `finally` to clear the `downloadingFiles` state during failures.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

chunk.map((callId) =>
getCallExtraData(callId)
.then((res) => ({ callId, dispatches: res?.Data?.Dispatches ?? ([] as DispatchedEventResultData[]) }))
.catch(() => ({ callId, dispatches: null as DispatchedEventResultData[] | null }))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Silent exception swallowing detected in the getCallExtraData catch handler. Implement structured logging with context (e.g., logger.error('getCallExtraData failed', { callId, err })) before returning default values.

Kody rule violation: Avoid empty catch blocks

Prompt for LLM

File src/components/dispatch-console/active-calls-panel.tsx:

Line 418:

Silent exception swallowing detected in the `getCallExtraData` catch handler. Implement structured logging with context (e.g., `logger.error('getCallExtraData failed', { callId, err })`) before returning default values.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

const CONCURRENCY = 4;
for (let i = 0; i < toFetch.length; i += CONCURRENCY) {
const chunk = toFetch.slice(i, i + CONCURRENCY);
const results = await Promise.all(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

Batch execution failure risk identified using Promise.all for independent API calls. Replace with Promise.allSettled to isolate individual rejections and inspect each settlement's status explicitly.

Kody rule violation: Use Promise.allSettled for batch operations with partial failures

Prompt for LLM

File src/components/dispatch-console/active-calls-panel.tsx:

Line 414:

Batch execution failure risk identified using `Promise.all` for independent API calls. Replace with `Promise.allSettled` to isolate individual rejections and inspect each settlement's status explicitly.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

animation: isLoading ? 'rg-refresh-spin 1s linear infinite' : undefined,
}}
>
<style>{'@keyframes rg-refresh-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }'}</style>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

CSS scope violation caused by a raw <style> tag injecting global @keyframes on every component render. Move the keyframes into a CSS Module (e.g., animated-refresh-icon.module.css) to prevent style collisions.

Kody rule violation: Use component-scoped styling

Prompt for LLM

File src/components/dispatch-console/animated-refresh-icon.web.tsx:

Line 21:

CSS scope violation caused by a raw `<style>` tag injecting global `@keyframes` on every component render. Move the keyframes into a CSS Module (e.g., `animated-refresh-icon.module.css`) to prevent style collisions.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

export const NotesPanel: React.FC<NotesPanelProps> = ({ notes, isLoading, onRefresh, onSelectNote, onNewNote, isCallFilterActive, callNotes, onAddCallNote, isAddingNote, flexWeight }) => {
const { t } = useTranslation();
const [isCollapsed, setIsCollapsed] = useState(false);
const isCollapsed = useDashboardViewStore(selectCardCollapsed('notes'));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

Magic string vulnerability detected using the literal 'notes' as a card-identifier key. Extract shared keys into a centralized constant (e.g., NOTES_CARD_KEY) or an enum to prevent typos and streamline refactoring.

Kody rule violation: Centralize string constants

Prompt for LLM

File src/components/dispatch-console/notes-panel.tsx:

Line 85:

Magic string vulnerability detected using the literal `'notes'` as a card-identifier key. Extract shared keys into a centralized constant (e.g., `NOTES_CARD_KEY`) or an enum to prevent typos and streamline refactoring.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

</Pressable>
) : null}
{onToggleCollapse ? <Icon as={isCollapsed ? ChevronDown : ChevronUp} size="sm" className="text-gray-500 dark:text-gray-400" /> : null}
{onToggleCollapse ? <Icon as={isCollapsed ? ChevronUp : ChevronDown} size="sm" className="text-gray-500 dark:text-gray-400" /> : null}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Bug medium

Inverted UI logic causes collapsed panels to display ChevronUp instead of ChevronDown, misleading user perception of state. Swap the conditional back to isCollapsed ? ChevronDown : ChevronUp to accurately reflect expansion state.

{onToggleCollapse ? <Icon as={isCollapsed ? ChevronDown : ChevronUp} size="sm" className="text-gray-500 dark:text-gray-400" /> : null}
Prompt for LLM

File src/components/dispatch-console/panel-header.tsx:

Line 42:

Inverted UI logic causes collapsed panels to display ChevronUp instead of ChevronDown, misleading user perception of state. Swap the conditional back to `isCollapsed ? ChevronDown : ChevronUp` to accurately reflect expansion state.

Suggested Code:

{onToggleCollapse ? <Icon as={isCollapsed ? ChevronDown : ChevronUp} size="sm" className="text-gray-500 dark:text-gray-400" /> : null}

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.


const buildPopupHtml = (pin: MapMakerInfoData) =>
`<div style="padding: 8px;">
<h3 style="margin: 0 0 8px 0; font-weight: 600;">${pin.Title}</h3>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules critical

Cross-Site Scripting (XSS) vulnerability identified by interpolating pin.Title directly into mapboxgl.Popup.setHTML(). Sanitize the value using escapeHtml() or DOMPurify before rendering untrusted remote data.

Kody rule violation: Always sanitize user inputs

Prompt for LLM

File src/components/maps/unified-map-view.web.tsx:

Line 229:

Cross-Site Scripting (XSS) vulnerability identified by interpolating `pin.Title` directly into `mapboxgl.Popup.setHTML()`. Sanitize the value using `escapeHtml()` or `DOMPurify` before rendering untrusted remote data.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.


const initialCenter: [number, number] = location.longitude && location.latitude ? [location.longitude, location.latitude] : [-98.5795, 39.8283];
const { latitude, longitude } = useLocationStore.getState();
const initialCenter: [number, number] = longitude && latitude ? [longitude, latitude] : [-98.5795, 39.8283];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

Magic numbers detected for default map center coordinates (-98.5795, 39.8283). Extract these values into a named constant (e.g., DEFAULT_CENTER) at module scope or a configuration file to improve maintainability.

Kody rule violation: Replace magic numbers with named constants

Prompt for LLM

File src/components/maps/unified-map-view.web.tsx:

Line 93:

Magic numbers detected for default map center coordinates (`-98.5795, 39.8283`). Extract these values into a named constant (e.g., `DEFAULT_CENTER`) at module scope or a configuration file to improve maintainability.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

height: '100%',
backgroundColor: colorScheme.get() === 'dark' ? '#171717' : '#fff',
shadowColor: colorScheme.get() === 'dark' ? '#262626' : '#e5e5e5',
backgroundColor: Appearance.getColorScheme() === 'dark' ? '#171717' : '#fff',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

Redundant calculation detected where Appearance.getColorScheme() is invoked independently in multiple style rules. Compute const isDark once at the module level and reference it in each ternary operation to optimize execution.

Kody rule violation: Use computed/derived properties for repeated calculations

Prompt for LLM

File src/components/notifications/NotificationDetail.tsx:

Line 210:

Redundant calculation detected where `Appearance.getColorScheme()` is invoked independently in multiple style rules. Compute `const isDark` once at the module level and reference it in each ternary operation to optimize execution.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

}));

jest.mock('nativewind', () => ({
styled: jest.fn((Component: any) => Component),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

Naming convention violation detected in the styled arrow-function parameter Component. Rename the parameter to camelCase (e.g., component) to comply with standard naming conventions for local variables.

Kody rule violation: Use proper naming conventions

Prompt for LLM

File src/components/settings/__tests__/login-info-bottom-sheet-simple.test.tsx:

Line 20:

Naming convention violation detected in the `styled` arrow-function parameter `Component`. Rename the parameter to camelCase (e.g., `component`) to comply with standard naming conventions for local variables.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.


type IAnimatedPressableProps = React.ComponentProps<typeof Pressable> & MotionComponentProps<typeof Pressable, ViewStyle, unknown, unknown, unknown>;

const AnimatedPressable = createMotionAnimatedComponent(Pressable) as React.ComponentType<IAnimatedPressableProps>;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

Type safety violation found in createMotionAnimatedComponent casting. Utilize the as operator or pattern matching for safe casts and explicitly guard null results prior to usage.

Kody rule violation: Use safe type casting with as operator

Prompt for LLM

File src/components/ui/actionsheet/index.tsx:

Line 23:

Type safety violation found in `createMotionAnimatedComponent` casting. Utilize the `as` operator or pattern matching for safe casts and explicitly guard null results prior to usage.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

: SlideInDown.duration(200);

const customClass = isHorizontal ? `top-0 ${parentAnchor === 'left' ? 'left-0' : 'right-0'}` : `left-0 ${parentAnchor === 'top' ? 'top-0' : 'bottom-0'}`;
const exitingAnimation = parentAnchor === 'left' ? SlideOutLeft.duration(200) : parentAnchor === 'right' ? SlideOutRight.duration(200) : parentAnchor === 'top' ? SlideOutUp.duration(200) : SlideOutDown.duration(200);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

Code duplication identified in the parallel ternary chains of exitingAnimation and enteringAnimation. Extract a single lookup map (e.g., animMap) to synchronize anchor branching logic and simplify future maintenance.

Kody rule violation: Extract duplicated logic into functions

Prompt for LLM

File src/components/ui/drawer/index.tsx:

Line 171:

Code duplication identified in the parallel ternary chains of `exitingAnimation` and `enteringAnimation`. Extract a single lookup map (e.g., `animMap`) to synchronize anchor branching logic and simplify future maintenance.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.


// Only render SystemBars when focused and on supported platforms
return isFocused && (Platform.OS === 'android' || Platform.OS === 'ios') ? <SystemBars style={colorScheme} hidden={{ statusBar: hidden, navigationBar: true }} /> : null;
return isFocused && (Platform.OS === 'android' || Platform.OS === 'ios') ? <SystemBars style={colorScheme === 'dark' ? 'dark' : 'light'} hidden={{ statusBar: hidden, navigationBar: true }} /> : null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

Magic strings detected hardcoding 'dark' and 'light' inline for SystemBars styling. Define a centralized enum (e.g., ThemeStyle) to replace these fragile string literals and prevent mistyping.

Kody rule violation: Use enums instead of magic strings

Prompt for LLM

File src/components/ui/focus-aware-status-bar.tsx:

Line 67:

Magic strings detected hardcoding `'dark'` and `'light'` inline for `SystemBars` styling. Define a centralized enum (e.g., `ThemeStyle`) to replace these fragile string literals and prevent mistyping.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Comment thread src/lib/auth/api.tsx
};

export const clearPasswordVerificationHash = async (): Promise<void> => {
await removeItem(PASSWORD_VERIFICATION_HASH_KEY);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Unguarded asynchronous operation in clearPasswordVerificationHash allows unhandled promise rejection if removeItem fails. Wrap the awaited removeItem call in a try/catch block and log the error context via logger.error(...) to match sibling function patterns.

Kody rule violation: Handle async operations with proper error handling

Prompt for LLM

File src/lib/auth/api.tsx:

Line 75:

Unguarded asynchronous operation in `clearPasswordVerificationHash` allows unhandled promise rejection if `removeItem` fails. Wrap the awaited `removeItem` call in a try/catch block and log the error context via `logger.error(...)` to match sibling function patterns.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Comment thread src/stores/toast/store.ts
toasts: state.toasts.filter((toast) => toast.id !== id),
}));
}, 3000);
}, options?.duration ?? 3000);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Memory leak risk caused by uncaptured setTimeout return values in the toast store. Capture the timer ID and invoke clearTimeout(timerId) during manual removal to prevent state-update-on-unmounted warnings.

Kody rule violation: Clear timers on teardown/unmount

Prompt for LLM

File src/stores/toast/store.ts:

Line 39:

Memory leak risk caused by uncaptured `setTimeout` return values in the toast store. Capture the timer ID and invoke `clearTimeout(timerId)` during manual removal to prevent state-update-on-unmounted warnings.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

@Resgrid-Bot

Resgrid-Bot commented Jul 31, 2026

Copy link
Copy Markdown

Code Review Completed! 🔥

The code review was successfully completed based on your current configurations.

Kody Guide: Usage and Configuration
Interacting with Kody
  • Request a Review: Ask Kody to review your PR manually by adding a comment with the @kody start-review command at the root of your PR.

  • Validate Business Logic: Ask Kody to validate your code against business rules by adding a comment with the @kody -v business-logic command.

  • Provide Feedback: Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

Current Kody Configuration
Review Options

The following review options are enabled or disabled:

Options Enabled
Bug
Performance
Security
Business Logic

Access your configuration settings here.

Comment thread electron/preload.js
try {
callback(deepLinkUrl);
} catch (err) {
console.error('Error in deep-link callback:', err);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Unstructured logging in the catch block bypasses centralized logging and violates Rule [3] by omitting required structured fields. Replace console.error with a structured logger call, such as logger.error('deep-link callback failed', { op: 'onDeepLink', err }), to include operation names and relevant identifiers.

Kody rule violation: Include error context in structured logs

Prompt for LLM

File electron/preload.js:

Line 67:

Unstructured logging in the catch block bypasses centralized logging and violates Rule [3] by omitting required structured fields. Replace `console.error` with a structured logger call, such as `logger.error('deep-link callback failed', { op: 'onDeepLink', err })`, to include operation names and relevant identifiers.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Comment thread src/app/(app)/_layout.tsx
Comment on lines +221 to 222
// If the init promise is still hanging, clear the guard so a retry is possible
isInitializing.current = false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Bug high

Race condition triggered when the timeout catch block sets isInitializing.current = false and allows a retry while the previous init promise is still pending. The old promise's .finally block (lines 209-211) clears the guard early, causing duplicate SignalR hub connections (connectUpdateHub) and store inits; making the .finally conditional on an epoch counter resolves this.

// capture an instance id so only the CURRENT init's settle clears the guard
const initInstanceId = ++initInstanceRef.current;

initPromise
  .catch((error) => { /* ... */ })
  .finally(() => {
    if (initInstanceRef.current === initInstanceId) {
      isInitializing.current = false;
    }
  });

await Promise.race([initPromise, initTimeout]);
} catch (error) {
  // ...
  hasInitialized.current = false;
  // Only clear the retry guard if we are still the active instance
  if (initInstanceRef.current === initInstanceId) {
    isInitializing.current = false;
  }
}
Prompt for LLM

File src/app/(app)/_layout.tsx:

Line 221 to 222:

Race condition triggered when the timeout catch block sets `isInitializing.current = false` and allows a retry while the previous init promise is still pending. The old promise's `.finally` block (lines 209-211) clears the guard early, causing duplicate SignalR hub connections (`connectUpdateHub`) and store inits; making the `.finally` conditional on an epoch counter resolves this.

Suggested Code:

// capture an instance id so only the CURRENT init's settle clears the guard
const initInstanceId = ++initInstanceRef.current;

initPromise
  .catch((error) => { /* ... */ })
  .finally(() => {
    if (initInstanceRef.current === initInstanceId) {
      isInitializing.current = false;
    }
  });

await Promise.race([initPromise, initTimeout]);
} catch (error) {
  // ...
  hasInitialized.current = false;
  // Only clear the retry guard if we are still the active instance
  if (initInstanceRef.current === initInstanceId) {
    isInitializing.current = false;
  }
}

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Comment thread src/app/(app)/map.tsx
Comment on lines +157 to +158
latitudeBucket: Math.round(location.latitude * 10) / 10,
longitudeBucket: Math.round(location.longitude * 10) / 10,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

Code duplication identified: the rounding and bucketing logic is repeated across multiple call sites (src/app/(app)/map.tsx:221-222, 176-177). Duplicated logic drifts and complicates GDPR compliance auditing; extract a utility function like bucketCoordinate(v: number, scale = COORDINATE_BUCKET_SCALE) to centralize it.

Kody rule violation: Extract duplicated logic into functions

Prompt for LLM

File src/app/(app)/map.tsx:

Line 157 to 158:

Code duplication identified: the rounding and bucketing logic is repeated across multiple call sites (`src/app/(app)/map.tsx:221-222`, `176-177`). Duplicated logic drifts and complicates GDPR compliance auditing; extract a utility function like `bucketCoordinate(v: number, scale = COORDINATE_BUCKET_SCALE)` to centralize it.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

}
}
</script>
<script src="https://cdn.jsdelivr.net/npm/hls.js@1.6.16/dist/hls.min.js"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

Inline script violation: using a raw <script> tag for the third-party library hls.js ignores next/script loading strategies. Replace it with next/script using an appropriate strategy and onLoad callbacks.

Kody rule violation: Use next/script safely and avoid inline scripts

Prompt for LLM

File src/components/callVideoFeeds/video-player.tsx:

Line 74:

Inline script violation: using a raw `<script>` tag for the third-party library `hls.js` ignores `next/script` loading strategies. Replace it with `next/script` using an appropriate strategy and `onLoad` callbacks.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

window.__hlsInitDone = true;
var url = ${JSON.stringify(feed.Url)};
if (url.includes('.m3u8') && window.Hls && Hls.isSupported()) {
var hls = new Hls();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

Improper variable declaration: var hls is function-scoped and prone to accidental redeclaration, whereas const provides block-scoping and prevents reassignment. Replace var with const across all instances (src/components/callVideoFeeds/video-player.tsx:56, 64), as the Hls instance is never reassigned.

Kody rule violation: Always use const and let

Prompt for LLM

File src/components/callVideoFeeds/video-player.tsx:

Line 66:

Improper variable declaration: `var hls` is function-scoped and prone to accidental redeclaration, whereas `const` provides block-scoping and prevents reassignment. Replace `var` with `const` across all instances (`src/components/callVideoFeeds/video-player.tsx:56`, `64`), as the `Hls` instance is never reassigned.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Comment thread src/stores/toast/store.ts
toasts: state.toasts.filter((toast) => toast.id !== id),
}));
}, 3000);
}, options?.duration ?? 3000);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

Magic number identified: the literal 3000 represents the default toast auto-dismiss duration but reduces readability and makes future tuning error-prone. Extract a named constant such as DEFAULT_TOAST_DURATION_MS = 3000 and apply it in the ?? fallback across all occurrences in src/app/(app)/map.tsx:157-158, 176-177, and 221-222.

Kody rule violation: Replace magic numbers with named constants

Prompt for LLM

File src/stores/toast/store.ts:

Line 44:

Magic number identified: the literal `3000` represents the default toast auto-dismiss duration but reduces readability and makes future tuning error-prone. Extract a named constant such as `DEFAULT_TOAST_DURATION_MS = 3000` and apply it in the `??` fallback across all occurrences in `src/app/(app)/map.tsx:157-158`, `176-177`, and `221-222`.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

@ucswift

ucswift commented Jul 31, 2026

Copy link
Copy Markdown
Member Author

Approve

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

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 9bedcf5 into master Jul 31, 2026
11 of 12 checks passed
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