Skip to content

RC-T41 Updated to Expo 56, Updated Gluestack v5 and Livekit 2.10 - #27

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

RC-T41 Updated to Expo 56, Updated Gluestack v5 and Livekit 2.10#27
ucswift merged 2 commits into
masterfrom
develop

Conversation

@ucswift

@ucswift ucswift commented Jul 31, 2026

Copy link
Copy Markdown
Member

Pull Request Description

This PR upgrades the Resgrid IC application to Expo SDK 56, migrates the styling system to NativeWind v5 (with Tailwind CSS v4), updates Gluestack UI to v5, and bumps LiveKit to 2.10.

Key Changes

Expo SDK 56 Upgrade

  • Android target SDK bumped from 35 to 36; new architecture flag removed from config
  • Added expo-image, expo-sharing, and expo-status-bar plugins
  • Edge-to-edge is now always-on; FocusAwareStatusBar simplified accordingly
  • Web map camera initialization hardened against projection crashes during startup

NativeWind v5 + Tailwind CSS v4 Migration

  • Babel config simplified (no more nativewind preset or jsxImportSource)
  • global.css rewritten with Tailwind v4 @theme/@layer syntax and comprehensive light/dark design tokens driven by CSS custom properties and class-based dark mode (@custom-variant dark)
  • cssInterop replaced by styled() across all components; withNativeWindwithNativewind in Metro config
  • PostCSS config added with @tailwindcss/postcss
  • Theme selection now uses React Native's Appearance.setColorScheme (native) and <html> class toggling (web) instead of NativeWind's colorScheme.set()

Gluestack UI v5 Migration

  • All component imports migrated from @gluestack-ui/* to @gluestack-ui/core/*/creator
  • Utility imports moved from @gluestack-ui/nativewind-utils to @gluestack-ui/utils/nativewind-utils
  • Removed inline PrimitiveIcon definitions; now uses UIIcon from gluestack core wrapped with styled()
  • Removed platform-specific withStyleContextAndStates and withStates wrappers
  • GluestackUIProvider simplified to use class-based theming instead of inline style config objects
  • Grid layout component rewritten with simpler, CSS-grid-equivalent gap calculation
  • Divider simplified to a plain View without the gluestack creator wrapper

Animation Library Migration

  • @legendapp/motion (Motion.View, AnimatePresence) replaced with react-native-reanimated (entering/exiting props) across Modal, Drawer, AlertDialog, Popover, Menu, and Toast components

Navigation Consolidation

  • useFocusEffect, useIsFocused, and navigation re-exports consolidated from @react-navigation/native to expo-router
  • Removed NavigationContainer wrapper and createNavigationContainerRef usage

Additional Fixes & Cleanup

  • Patched gluestack ActionsheetContent to use reactive window dimensions (fixes landscape rendering)
  • Tailwind v4 class renames: shadow-smshadow-xs, rounded-smrounded-xs
  • Web component testID support added (Box, Card, Heading, Text, HStack, VStack, Grid, Skeleton)
  • numberOfLines/line-clamp support added to web Text component
  • Removed MessageCommanderSheet from the command board
  • Map marker pulse animation migrated from CSS @keyframes to react-native-web animationKeyframes
  • Jest configuration updated for Reanimated v4 worklets resolver and NativeWind/react-native-css transform inclusion

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 150 files, which is 50 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: 796cd3dd-1409-4c9b-aed6-b29eb583e639

📥 Commits

Reviewing files that changed from the base of the PR and between c103fe4 and 28d45f8.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (150)
  • app.config.ts
  • babel.config.js
  • global.css
  • jest-setup.ts
  • jest.config.js
  • metro.config.js
  • package.json
  • patches/@gluestack-ui+actionsheet+0.2.53.patch
  • patches/@gluestack-ui+core+5.0.15.patch
  • postcss.config.mjs
  • react-native-css-env.d.ts
  • src/app/(app)/__tests__/calls.test.tsx
  • src/app/(app)/__tests__/incidents.test.tsx
  • src/app/(app)/__tests__/index.test.tsx
  • src/app/(app)/_layout.tsx
  • src/app/(app)/calls.tsx
  • src/app/(app)/command.tsx
  • src/app/(app)/incidents.tsx
  • src/app/(app)/index.tsx
  • src/app/(app)/maps/search.tsx
  • src/app/(app)/poi/[id].tsx
  • src/app/(app)/weather-alerts.tsx
  • src/app/_layout.tsx
  • src/app/call/[id].tsx
  • src/app/call/__tests__/[id].security.test.tsx
  • src/app/call/__tests__/[id].test.tsx
  • src/app/call/new/__tests__/what3words.test.tsx
  • src/app/login/__tests__/login-form.test.tsx
  • src/components/call-video-feeds/__tests__/video-feed-card.test.tsx
  • src/components/call-video-feeds/__tests__/video-feed-tab-content.test.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.tsx
  • src/components/calls/call-detail-menu.tsx
  • src/components/calls/call-images-modal.tsx
  • src/components/calls/call-notes-modal.tsx
  • src/components/calls/close-call-bottom-sheet.tsx
  • src/components/check-in-timers/__tests__/check-in-bottom-sheet.test.tsx
  • src/components/check-in-timers/__tests__/check-in-timer-card.test.tsx
  • src/components/command/accountability-section.tsx
  • src/components/command/command-details-sheet.tsx
  • src/components/command/command-section.tsx
  • src/components/command/incident-files-section.tsx
  • src/components/command/incident-map-card.tsx
  • src/components/command/incident-maps-section.tsx
  • src/components/command/incident-weather-section.tsx
  • src/components/command/landscape-structure-board.tsx
  • src/components/command/maps-tabbed-card.tsx
  • src/components/command/needs-section.tsx
  • src/components/command/notes-section.tsx
  • src/components/command/objectives-section.tsx
  • src/components/command/structure-section.tsx
  • src/components/command/timeline-section.tsx
  • src/components/command/timers-section.tsx
  • src/components/command/voice-section.tsx
  • src/components/common/loading.tsx
  • src/components/contacts/contact-card.tsx
  • src/components/contacts/contact-details-sheet.tsx
  • src/components/livekit/__tests__/livekit-bottom-sheet.test.tsx
  • src/components/maps/__tests__/pin-actions.test.tsx
  • src/components/maps/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/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/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/html-renderer/__tests__/html-renderer.test.tsx
  • src/components/ui/html-renderer/index.web.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/safe-area-view/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/components/weather-alerts/weather-alert-card.tsx
  • src/lib/hooks/__tests__/use-selected-theme.test.ts
  • src/lib/hooks/use-selected-theme.tsx
  • src/lib/test-utils.tsx
  • tailwind.config.js
  • tsconfig.json

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 src/app/(app)/maps/search.tsx Outdated
const isActive = segment.value === activeFilter;
return (
<Pressable key={segment.value} onPress={() => handleFilterChange(segment.value)} className={`flex-1 items-center rounded-md px-3 py-2 ${isActive ? 'bg-white shadow-sm dark:bg-gray-600' : ''}`}>
<Pressable key={segment.value} onPress={() => handleFilterChange(segment.value)} className={`flex-1 items-center rounded-md px-3 py-2 ${isActive ? 'bg-white shadow-xs dark:bg-gray-600' : ''}`}>

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

Render performance issue: Inline arrow functions in JSX props, such as onPress={() => handleFilterChange(segment.value)}, create new function instances on every render. Move these function definitions outside the render method to prevent unnecessary re-renders.

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

Prompt for LLM

File src/app/(app)/maps/search.tsx:

Line 176:

Render performance issue: Inline arrow functions in JSX props, such as `onPress={() => handleFilterChange(segment.value)}`, create new function instances on every render. Move these function definitions outside the render method to prevent unnecessary re-renders.

Talk to Kody by mentioning @kody

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

Comment on lines +560 to +562
} catch {
return 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 Kody Rules high

Silent exception swallowing: The catch block in map-view.web.tsx returns false without logging the initialization or projection failure. Log the caught error with structured context before returning false to allow debugging of the retry logic.

Kody rule violation: Avoid empty catch blocks

Prompt for LLM

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

Line 560 to 562:

Silent exception swallowing: The `catch` block in `map-view.web.tsx` returns `false` without logging the initialization or projection failure. Log the caught error with structured context before returning `false` to allow debugging of the retry logic.

Talk to Kody by mentioning @kody

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

Comment on lines +206 to +207
backgroundColor: Appearance.getColorScheme() === 'dark' ? '#171717' : '#fff',
shadowColor: Appearance.getColorScheme() === 'dark' ? '#262626' : '#e5e5e5',

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

Incorrect theme detection: NotificationDetail.tsx and NotificationInbox.tsx use Appearance.getColorScheme() inside StyleSheet.create, which ignores manual in-app theme overrides and evaluates only once at module load. Read the effective scheme via useColorScheme() or nativewind at render time to correctly apply user-selected themes.

// Compute styles at render time from the active scheme so manual
// theme overrides are reflected:
// const isDark = useColorScheme() === 'dark';
// sidebarContainer: { backgroundColor: isDark ? '#171717' : '#fff', ... }
Prompt for LLM

File src/components/notifications/NotificationDetail.tsx:

Line 206 to 207:

Incorrect theme detection: NotificationDetail.tsx and NotificationInbox.tsx use `Appearance.getColorScheme()` inside `StyleSheet.create`, which ignores manual in-app theme overrides and evaluates only once at module load. Read the effective scheme via `useColorScheme()` or `nativewind` at render time to correctly apply user-selected themes.

Suggested Code:

// Compute styles at render time from the active scheme so manual
// theme overrides are reflected:
// const isDark = useColorScheme() === 'dark';
// sidebarContainer: { backgroundColor: isDark ? '#171717' : '#fff', ... }

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

Code duplication: The boolean expression Appearance.getColorScheme() === 'dark' is redundantly evaluated across multiple style properties in NotificationDetail.tsx. Hoist this logic into a single isDark variable before StyleSheet.create to avoid drift and simplify maintenance.

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

Prompt for LLM

File src/components/notifications/NotificationDetail.tsx:

Line 206:

Code duplication: The boolean expression `Appearance.getColorScheme() === 'dark'` is redundantly evaluated across multiple style properties in `NotificationDetail.tsx`. Hoist this logic into a single `isDark` variable before `StyleSheet.create` to avoid drift and simplify maintenance.

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

Code duplication: The expression Appearance.getColorScheme() === 'dark' is duplicated across numerous style properties in NotificationInbox.tsx and NotificationDetail.tsx. Extract this into a single isDark constant or helper function before referencing it in the ternary operations.

Kody rule violation: Extract duplicated logic into functions

Prompt for LLM

File src/components/notifications/NotificationInbox.tsx:

Line 446:

Code duplication: The expression `Appearance.getColorScheme() === 'dark'` is duplicated across numerous style properties in `NotificationInbox.tsx` and `NotificationDetail.tsx`. Extract this into a single `isDark` constant or helper function before referencing it in the ternary operations.

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

Unsafe type cast: createMotionAnimatedComponent(Pressable) asserts a type without proper validation. Ensure safe type casting by utilizing pattern matching or verifying the result before usage.

Kody rule violation: Use safe type casting with as operator

Prompt for LLM

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

Line 23:

Unsafe type cast: `createMotionAnimatedComponent(Pressable)` asserts a type without proper validation. Ensure safe type casting by utilizing pattern matching or verifying the result before usage.

Talk to Kody by mentioning @kody

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

}),
[]
);
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

Unguarded string literals: Anchor positions like 'left', 'right', and 'top' are hardcoded in long ternary chains across multiple files. Define an enum or a const tuple for anchor values to prevent typos and centralize maintenance.

Kody rule violation: Use enums instead of magic strings

Prompt for LLM

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

Line 171:

Unguarded string literals: Anchor positions like 'left', 'right', and 'top' are hardcoded in long ternary chains across multiple files. Define an enum or a const tuple for anchor values to prevent typos and centralize maintenance.

Talk to Kody by mentioning @kody

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

Comment on lines +16 to +18
useEffect(() => {
Appearance.setColorScheme(mode === 'system' ? 'unspecified' : mode);
}, [mode]);

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

Theme override bug: GluestackUIProvider's useEffect pins Appearance to the resolved value, preventing the app from following OS dark↔light switches when 'system' is selected. Gate the setColorScheme call so it only forces an override when mode !== 'system'.

// Only force the override when a concrete mode is requested;
// 'system' should leave Appearance unset so the OS value wins.
useEffect(() => {
    if (mode !== 'system') {
      Appearance.setColorScheme(mode);
    } else {
      Appearance.setColorScheme('unspecified');
    }
  }, [mode]);
Prompt for LLM

File src/components/ui/gluestack-ui-provider/index.tsx:

Line 16 to 18:

Theme override bug: GluestackUIProvider's `useEffect` pins Appearance to the resolved value, preventing the app from following OS dark↔light switches when 'system' is selected. Gate the `setColorScheme` call so it only forces an override when `mode !== 'system'`.

Suggested Code:

// Only force the override when a concrete mode is requested;
// 'system' should leave Appearance unset so the OS value wins.
useEffect(() => {
    if (mode !== 'system') {
      Appearance.setColorScheme(mode);
    } else {
      Appearance.setColorScheme('unspecified');
    }
  }, [mode]);

Talk to Kody by mentioning @kody

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

import { Appearance, useColorScheme, View, type ViewProps } from 'react-native';

import { config } from './config';
export type ModeType = 'light' | 'dark' | 'system';

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

Fragile type definition: ModeType is a hand-maintained string-literal union, risking drift with its inline usages. Derive the type from a runtime constant using typeof MODES[number] to establish a single source of truth.

Kody rule violation: Derive TypeScript types from validation schemas

Prompt for LLM

File src/components/ui/gluestack-ui-provider/index.tsx:

Line 7:

Fragile type definition: `ModeType` is a hand-maintained string-literal union, risking drift with its inline usages. Derive the type from a runtime constant using `typeof MODES[number]` to establish a single source of truth.

Talk to Kody by mentioning @kody

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

// drives the class-based `dark:` variant (see @custom-variant in global.css).
const osScheme = useColorScheme();
const colorSchemeName: 'light' | 'dark' = mode === 'system' ? (osScheme === 'dark' ? 'dark' : 'light') : mode === 'dark' ? 'dark' : 'light';
const resolvedScheme: 'light' | 'dark' = mode === 'system' ? (osScheme === 'dark' ? 'dark' : 'light') : mode;

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 literal duplication: Shared literals 'light', 'dark', and 'system' are scattered inline across gluestack-ui-provider/index.tsx. Reference centralized constants or enum members instead of bare strings to prevent errors and maintain a single source of truth.

Kody rule violation: Centralize string constants

Prompt for LLM

File src/components/ui/gluestack-ui-provider/index.tsx:

Line 14:

String literal duplication: Shared literals 'light', 'dark', and 'system' are scattered inline across `gluestack-ui-provider/index.tsx`. Reference centralized constants or enum members instead of bare strings to prevent errors and maintain a single source of truth.

Talk to Kody by mentioning @kody

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

});
const StyledUIIcon = styled(UIIcon, { className: 'style' });

type IIConProps = IPrimitiveIcon & VariantProps<typeof iconStyle> & React.ComponentPropsWithoutRef<typeof UIIcon>;

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

Typographical error: The type name IIConProps incorrectly capitalizes the 'C', violating naming conventions and reducing searchability. Rename it to IIconProps consistently across the codebase, including consumer files like video-feed-card.test.tsx.

Kody rule violation: Use proper naming conventions

Prompt for LLM

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

Line 28:

Typographical error: The type name `IIConProps` incorrectly capitalizes the 'C', violating naming conventions and reducing searchability. Rename it to `IIconProps` consistently across the codebase, including consumer files like `video-feed-card.test.tsx`.

Talk to Kody by mentioning @kody

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

},
}}
entering={FadeIn.duration(200).easing(Easing.linear)}
exiting={FadeOut.duration(200).easing(Easing.linear)}

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

Unnamed magic number: The numeric literal 200 is used inline for animation durations across multiple components. Extract this value to a module-level constant, such as MODAL_ANIMATION_DURATION = 200, to improve maintainability and readability.

Kody rule violation: Replace magic numbers with named constants

Prompt for LLM

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

Line 93:

Unnamed magic number: The numeric literal `200` is used inline for animation durations across multiple components. Extract this value to a module-level constant, such as `MODAL_ANIMATION_DURATION = 200`, to improve maintainability and readability.

Talk to Kody by mentioning @kody

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

applyColorScheme(theme as ColorSchemeType);
}
} catch (error) {
console.error('Failed to load selected theme:', error);

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: The catch block in use-selected-theme.tsx uses console.error with a plain message string and raw error object. Replace this with a structured logger call that includes the operation name and relevant identifiers, such as logger.error('loadSelectedTheme failed', { op: 'loadSelectedTheme', key: SELECTED_THEME, err: error }).

Kody rule violation: Include error context in structured logs

Prompt for LLM

File src/lib/hooks/use-selected-theme.tsx:

Line 58:

Unstructured error logging: The `catch` block in `use-selected-theme.tsx` uses `console.error` with a plain message string and raw error object. Replace this with a structured logger call that includes the operation name and relevant identifiers, such as `logger.error('loadSelectedTheme failed', { op: 'loadSelectedTheme', key: SELECTED_THEME, err: error })`.

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.

// Helper function to get tag style based on notification type
const getTypeTagStyle = (type: string): StyleProp<ViewStyle> => {
// Helper function to get the themed tag style key based on notification type
type TypeTagStyleKey = 'typeTagDefault' | 'typeTagInfo' | 'typeTagSuccess' | 'typeTagWarning' | 'typeTagAlert';

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

The type union duplicates property keys defined in createThemedStyles, risking drift where adding or renaming a key requires manual updates in both places without compiler detection. Declare a const tuple and derive the type (const TYPE_TAG_KEYS = ['typeTagDefault','typeTagInfo','typeTagSuccess','typeTagWarning','typeTagAlert'] as const; type TypeTagStyleKey = typeof TYPE_TAG_KEYS[number];), or introduce a string enum that serves as both the type and the value source.

Kody rule violation: Derive TypeScript types from validation schemas

Prompt for LLM

File src/components/notifications/NotificationDetail.tsx:

Line 179:

The type union duplicates property keys defined in createThemedStyles, risking drift where adding or renaming a key requires manual updates in both places without compiler detection. Declare a const tuple and derive the type (const TYPE_TAG_KEYS = ['typeTagDefault','typeTagInfo','typeTagSuccess','typeTagWarning','typeTagAlert'] as const; type TypeTagStyleKey = typeof TYPE_TAG_KEYS[number];), or introduce a string enum that serves as both the type and the value source.

Talk to Kody by mentioning @kody

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


if (lowerType.includes('alert') || lowerType.includes('emergency')) {
return styles.typeTagAlert;
return 'typeTagAlert';

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 returned from the helper function are typo-prone and not refactor-safe; changing a key name silently desynchronizes call sites and the themed-styles object. Define a string enum (e.g. enum TypeTagStyleKey { Alert='typeTagAlert', Warning='typeTagWarning', Info='typeTagInfo', Success='typeTagSuccess', Default='typeTagDefault' }) and return the enum member instead of the raw string.

Kody rule violation: Use enums instead of magic strings

Prompt for LLM

File src/components/notifications/NotificationDetail.tsx:

Line 185:

Magic strings returned from the helper function are typo-prone and not refactor-safe; changing a key name silently desynchronizes call sites and the themed-styles object. Define a string enum (e.g. enum TypeTagStyleKey { Alert='typeTagAlert', Warning='typeTagWarning', Info='typeTagInfo', Success='typeTagSuccess', Default='typeTagDefault' }) and return the enum member instead of the raw string.

Talk to Kody by mentioning @kody

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


export const NotificationInbox = ({ isOpen, onClose }: NotificationInboxProps) => {
const { t } = useTranslation();
const { colorScheme } = useColorScheme();

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

Identical hook sequences duplicating useColorScheme() and React.useMemo appear in both NotificationRow (lines 87–88) and NotificationInbox (lines 142–143), violating DRY and increasing the maintenance surface. Extract a custom hook, e.g. function useThemedStyles() { const { colorScheme } = useColorScheme(); return React.useMemo(() => createThemedStyles(colorScheme === 'dark'), [colorScheme]); }, and call const themed = useThemedStyles(); in both components.

Kody rule violation: Extract duplicated logic into functions

Prompt for LLM

File src/components/notifications/NotificationInbox.tsx:

Line 142:

Identical hook sequences duplicating useColorScheme() and React.useMemo appear in both NotificationRow (lines 87–88) and NotificationInbox (lines 142–143), violating DRY and increasing the maintenance surface. Extract a custom hook, e.g. function useThemedStyles() { const { colorScheme } = useColorScheme(); return React.useMemo(() => createThemedStyles(colorScheme === 'dark'), [colorScheme]); }, and call const themed = useThemedStyles(); in both components.

Talk to Kody by mentioning @kody

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

@ucswift
ucswift merged commit ca9c7cb into master Jul 31, 2026
9 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