Features: added the tab navigation, styled it, added fonts and manage…#1
Conversation
📝 WalkthroughWalkthroughThis PR transforms a React Native/Expo app from a theme-color-scheme-based architecture to NativeWind/Tailwind CSS styling. The app gains a new auth route group, restructures tab navigation using data-driven configuration, adds onboarding and subscription detail screens, and removes old theme-dependent components while standardizing styling with design tokens. ChangesApp Redesign to NativeWind & Data-Driven Navigation
Sequence Diagram(s)The changes are primarily architectural and configuration-focused rather than involving multi-step interactive flows, so no sequence diagram is generated. Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 9
🧹 Nitpick comments (8)
type.d.ts (1)
4-8: ⚡ Quick winNarrow
AppTab.nameto known tab route names.
name: stringpermits invalid route values and weakens tab-route safety.Proposed change
declare global { + type AppTabName = "index" | "insights" | "subscriptions" | "settings"; interface AppTab { - name: string; + name: AppTabName; title: string; icon: ImageSourcePropType; } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@type.d.ts` around lines 4 - 8, AppTab.name is currently a string which allows invalid route names; change it to a narrow union of the actual tab route names (or a keyed type) so only valid routes are allowed. Define or reuse a route-name type (e.g., export type TabRouteName = keyof RootTabParamList or an explicit union like 'Home' | 'Search' | 'Profile') and change AppTab.name: string to AppTab.name: TabRouteName; update imports/exports so type.d.ts references the existing navigation param list (RootTabParamList) if present, or add the explicit union of known tab route names.assets/constants/data.ts (1)
3-8: ⚡ Quick winAdd a compile-time contract for
tabs.
tabsis currently inferred without enforcing the shared tab shape. Typing it prevents silent route/config drift.Proposed change
export const tabs = [ { name: "index", title: "Home", icon: icons.home }, { name: "insights", title: "Insights", icon: icons.activity }, { name: "subscriptions", title: "Subscriptions", icon: icons.wallet }, { name: "settings", title: "Settings", icon: icons.setting } -] +] as const satisfies readonly AppTab[];🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@assets/constants/data.ts` around lines 3 - 8, Define a TypeScript contract for the tab entries and annotate the exported constant so its shape is enforced: add a Tab type (e.g. interface Tab { name: string; title: string; icon: /* type of icons values, e.g. keyof typeof icons or a shared Icon type */ }) and then declare the exported tabs with that type (for example export const tabs: Tab[] = [...] or use the newer satisfies operator: export const tabs = [...] satisfies Tab[]), referencing the existing tabs and icons identifiers so each element must have name, title and icon matching the contract.image.d.ts (1)
1-20: ⚡ Quick winAvoid
anyfor bitmap asset module declarations.Using
anyhere drops useful type checks. PreferImageSourcePropTypefor png/jpg/jpeg/gif declarations.Proposed change
declare module "*.png" { - const value: any; + const value: import("react-native").ImageSourcePropType; export default value; } declare module "*.jpg" { - const value: any; + const value: import("react-native").ImageSourcePropType; export default value; } declare module "*.jpeg" { - const value: any; + const value: import("react-native").ImageSourcePropType; export default value; } declare module "*.gif" { - const value: any; + const value: import("react-native").ImageSourcePropType; export default value; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@image.d.ts` around lines 1 - 20, The module declarations for bitmap assets (*.png, *.jpg, *.jpeg, *.gif) use any which loses type safety; import the ImageSourcePropType type from 'react-native' at the top of this declaration file and change the bitmap module declarations so their exported const value is typed as ImageSourcePropType (keep export default value). Leave the SVG declaration unchanged or type it as string/SVG module if desired, but ensure the unique module patterns ("*.png", "*.jpg", "*.jpeg", "*.gif") and the exported symbol name value are updated to use ImageSourcePropType instead of any.app/_layout.tsx (1)
15-19: ⚡ Quick winHandle the async Promise from
SplashScreen.hideAsync().
SplashScreen.hideAsync()returns a Promise, but the code does not await it or handle potential errors. While splash screen hiding rarely fails, unhandled promise rejections could surface in production.♻️ Proposed fix to handle the Promise
useEffect(() => { if (fontsLoaded) { - SplashScreen.hideAsync(); + SplashScreen.hideAsync().catch(err => { + console.error("Failed to hide splash screen:", err); + }); } }, [fontsLoaded]);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/_layout.tsx` around lines 15 - 19, The effect currently calls SplashScreen.hideAsync() without awaiting or handling errors; update the useEffect that watches fontsLoaded so when fontsLoaded is true it invokes an async IIFE (or calls a helper async function) to await SplashScreen.hideAsync() and wrap the await in a try/catch, logging or handling any error; keep the dependency on fontsLoaded and ensure you don't make the effect callback itself async.app/(auth)/Sign_in.tsx (1)
8-8: ⚡ Quick winConsider using PascalCase for component names.
The component is named
Sign_in(snake_case). React convention recommends PascalCase (SignIn) for component names, which also aids readability and distinguishes components from hooks or utilities.♻️ Suggested refactor
-const Sign_in = () => { +const SignIn = () => { return ( <SafeAreaView className="flex-1 bg-background p-5 ">Update the export as well:
-export default Sign_in; +export default SignIn;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/`(auth)/Sign_in.tsx at line 8, Rename the React component function Sign_in to PascalCase SignIn and update its export (e.g., export default SignIn) plus any internal references or imports that use Sign_in; ensure the component identifier, default export, and all usages (imports in other modules) are updated to the new SignIn name to keep naming consistent and avoid broken imports.app/(auth)/Sign_up.tsx (1)
9-9: ⚡ Quick winConsider using PascalCase for component names.
The component is named
Sign_up(snake_case). React convention recommends PascalCase (SignUp) for component names.♻️ Suggested refactor
-const Sign_up = () => { +const SignUp = () => { return ( <SafeAreaView className="flex-1 bg-background p-5 ">Update the export as well:
-export default Sign_up; +export default SignUp;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/`(auth)/Sign_up.tsx at line 9, Rename the React component function Sign_up to use PascalCase (SignUp) and update any corresponding export/default export and references (e.g., imports or usages of Sign_up) to the new identifier so the component and its export/imports consistently use SignUp.app/subscriptions/[id].tsx (1)
1-18: ⚡ Quick winUse SafeAreaView for consistency with other screens.
This screen uses a plain
Viewwrapper, while all other screens in the app useSafeAreaViewfromreact-native-safe-area-contextwrapped withnativewind'sstyled(). For consistent safe-area handling and styling, consider adopting the same pattern.♻️ Suggested refactor to match other screens
Update imports:
import { useLocalSearchParams, Link } from 'expo-router'; import { View, Text } from 'react-native' +import "`@/global.css`"; +import { styled } from "nativewind"; +import { SafeAreaView as RNSafeAreaView } from "react-native-safe-area-context"; + +const SafeAreaView = styled(RNSafeAreaView);Update the component:
- return ( - <View> + return ( + <SafeAreaView className="flex-1 bg-background p-5"> <Text>Subscription Details : {id}</Text>Close with SafeAreaView:
- </View> + </SafeAreaView>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/subscriptions/`[id].tsx around lines 1 - 18, Replace the root View in the subscription_details component with a styled SafeAreaView to match other screens: import { SafeAreaView } from 'react-native-safe-area-context' and import { styled } from 'nativewind', create a StyledSafeAreaView via styled(SafeAreaView) and use that as the top-level wrapper in the subscription_details function (keep existing Text and Link usage and the useLocalSearchParams hook), ensuring styling classes previously applied to View are moved to the StyledSafeAreaView.app/onboarding.tsx (1)
8-8: ⚡ Quick winUse PascalCase for component names.
The component is named
onboarding(lowercase). React convention requires PascalCase (Onboarding) for component names to distinguish them from HTML elements and hooks.♻️ Suggested refactor
-const onboarding = () => { +const Onboarding = () => { return ( <SafeAreaView className="flex-1 bg-background p-5 ">Update the export:
-export default onboarding; +export default Onboarding;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/onboarding.tsx` at line 8, The component function is named onboarding (lowercase) which breaks React naming conventions; rename the function to Onboarding and update any references and the default export to export default Onboarding so the component uses PascalCase; ensure any usages/imports of onboarding are updated to Onboarding to keep names consistent and avoid collisions with HTML elements.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/_layout.tsx`:
- Around line 15-19: Call SplashScreen.preventAutoHideAsync() immediately in the
module/root scope (outside the React component) so the splash cannot auto-hide
before assets load, e.g., invoke and catch errors at module initialization; keep
the existing useEffect that calls SplashScreen.hideAsync() when fontsLoaded
becomes true (the useEffect referencing fontsLoaded should remain as-is). Ensure
you reference the same SplashScreen methods (SplashScreen.preventAutoHideAsync
and SplashScreen.hideAsync) and the existing useEffect/fontsLoaded logic so
hideAsync is only called after fontsLoaded is true.
- Around line 7-14: The useFonts call currently only destructures fontsLoaded
and ignores the error tuple; update the destructuring to capture the second
return value (e.g., const [fontsLoaded, fontError] = useFonts(...)) and add
handling in the component to bail out of the perpetual loading state when
fontError is truthy (for example, render a fallback UI or log and continue
rendering). Locate the useFonts usage in this file (_layout.tsx) and reference
fontsLoaded and the new fontError to ensure the app does not remain stuck when
font loading fails.
In `@app/`(auth)/Sign_in.tsx:
- Around line 11-13: The "Sign In" heading Text element in Sign_in.tsx is
missing the styling used in the Sign Up screen; update the Text node that
renders "Sign In" to include the same className ("text-xl font-bold") so the
heading matches Sign_up.tsx's appearance and maintains consistent typography
across auth screens.
- Line 27: Remove the dead commented-out JSX Link in Sign_in.tsx: delete the
line containing {/*<Link href="/(auth)/Sign_up">Sign Up</Link>*/} so the Sign_in
component no longer includes commented-out code and the file stays clean and
maintainable.
In `@app/`(tabs)/index.tsx:
- Line 1: Remove the duplicate import of the CSS module "`@/global.css`" from
app/(tabs)/index.tsx and any nested layout files (e.g., app/(tabs)/_layout.tsx
and app/(auth)/_layout.tsx); keep the single root import in app/_layout.tsx only
so the global stylesheet is applied once. Locate and delete the line importing
"`@/global.css`" in the mentioned files (search for the exact string
"`@/global.css`") and confirm no other non-root files import it.
In `@app/subscriptions/`[id].tsx:
- Around line 4-5: The component subscription_details is named in snake_case
while using the useLocalSearchParams hook, which violates React hook naming
rules; rename the component to PascalCase (e.g., SubscriptionDetails) and update
any corresponding export/default export and references to that component so the
function name is PascalCase and hooks are called from a proper React component
name.
In `@global.css`:
- Around line 47-52: Remove the duplicate CSS custom properties for the font
families (--font-sans, --font-sans-light, --font-sans-medium,
--font-sans-semibold, --font-sans-bold, --font-sans-extrabold) in global.css by
deleting the redundant block so only the original declarations remain; locate
the repeated set (the second occurrence of those --font-sans* properties) and
remove it to eliminate bloat and lint errors while keeping the initial
definitions intact.
In `@package.json`:
- Line 31: Replace the preview NativeWind dependency in package.json: find the
"nativewind": "^5.0.0-preview.4" entry and change it to the stable v4.1 release
(e.g. "nativewind": "4.1.x" or specific "4.1.0") so production uses the
supported stable version; if you intentionally need the v5 preview, add a
justification comment in the PR description instead of shipping the preview in
package.json.
- Around line 32-34: The package.json currently pins "react" and "react-dom" to
the exact version "19.1.0" while "react-native" is "0.81.5"; loosen the pins to
a compatible semver range to avoid strict renderer version-check failures by
changing the "react" and "react-dom" entries from exact versions to a range (for
example ^19.1.0 or >=19.1.0 <20.0.0) so the React runtime can satisfy
react-native@0.81.5’s expected React 19.x installation; after updating the
"react" and "react-dom" entries run the app and tests and verify no React 19 API
breakages occur.
---
Nitpick comments:
In `@app/_layout.tsx`:
- Around line 15-19: The effect currently calls SplashScreen.hideAsync() without
awaiting or handling errors; update the useEffect that watches fontsLoaded so
when fontsLoaded is true it invokes an async IIFE (or calls a helper async
function) to await SplashScreen.hideAsync() and wrap the await in a try/catch,
logging or handling any error; keep the dependency on fontsLoaded and ensure you
don't make the effect callback itself async.
In `@app/`(auth)/Sign_in.tsx:
- Line 8: Rename the React component function Sign_in to PascalCase SignIn and
update its export (e.g., export default SignIn) plus any internal references or
imports that use Sign_in; ensure the component identifier, default export, and
all usages (imports in other modules) are updated to the new SignIn name to keep
naming consistent and avoid broken imports.
In `@app/`(auth)/Sign_up.tsx:
- Line 9: Rename the React component function Sign_up to use PascalCase (SignUp)
and update any corresponding export/default export and references (e.g., imports
or usages of Sign_up) to the new identifier so the component and its
export/imports consistently use SignUp.
In `@app/onboarding.tsx`:
- Line 8: The component function is named onboarding (lowercase) which breaks
React naming conventions; rename the function to Onboarding and update any
references and the default export to export default Onboarding so the component
uses PascalCase; ensure any usages/imports of onboarding are updated to
Onboarding to keep names consistent and avoid collisions with HTML elements.
In `@app/subscriptions/`[id].tsx:
- Around line 1-18: Replace the root View in the subscription_details component
with a styled SafeAreaView to match other screens: import { SafeAreaView } from
'react-native-safe-area-context' and import { styled } from 'nativewind', create
a StyledSafeAreaView via styled(SafeAreaView) and use that as the top-level
wrapper in the subscription_details function (keep existing Text and Link usage
and the useLocalSearchParams hook), ensuring styling classes previously applied
to View are moved to the StyledSafeAreaView.
In `@assets/constants/data.ts`:
- Around line 3-8: Define a TypeScript contract for the tab entries and annotate
the exported constant so its shape is enforced: add a Tab type (e.g. interface
Tab { name: string; title: string; icon: /* type of icons values, e.g. keyof
typeof icons or a shared Icon type */ }) and then declare the exported tabs with
that type (for example export const tabs: Tab[] = [...] or use the newer
satisfies operator: export const tabs = [...] satisfies Tab[]), referencing the
existing tabs and icons identifiers so each element must have name, title and
icon matching the contract.
In `@image.d.ts`:
- Around line 1-20: The module declarations for bitmap assets (*.png, *.jpg,
*.jpeg, *.gif) use any which loses type safety; import the ImageSourcePropType
type from 'react-native' at the top of this declaration file and change the
bitmap module declarations so their exported const value is typed as
ImageSourcePropType (keep export default value). Leave the SVG declaration
unchanged or type it as string/SVG module if desired, but ensure the unique
module patterns ("*.png", "*.jpg", "*.jpeg", "*.gif") and the exported symbol
name value are updated to use ImageSourcePropType instead of any.
In `@type.d.ts`:
- Around line 4-8: AppTab.name is currently a string which allows invalid route
names; change it to a narrow union of the actual tab route names (or a keyed
type) so only valid routes are allowed. Define or reuse a route-name type (e.g.,
export type TabRouteName = keyof RootTabParamList or an explicit union like
'Home' | 'Search' | 'Profile') and change AppTab.name: string to AppTab.name:
TabRouteName; update imports/exports so type.d.ts references the existing
navigation param list (RootTabParamList) if present, or add the explicit union
of known tab route names.
🪄 Autofix (Beta)
❌ Autofix failed (check again to retry)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: d5545ae5-eca8-4493-b7a7-de7df36c3d37
⛔ Files ignored due to path filters (46)
assets/expo.icon/Assets/expo-symbol 2.svgis excluded by!**/*.svgassets/expo.icon/Assets/grid.pngis excluded by!**/*.pngassets/fonts/PlusJakartaSans-Bold.ttfis excluded by!**/*.ttfassets/fonts/PlusJakartaSans-ExtraBold.ttfis excluded by!**/*.ttfassets/fonts/PlusJakartaSans-Light.ttfis excluded by!**/*.ttfassets/fonts/PlusJakartaSans-Medium.ttfis excluded by!**/*.ttfassets/fonts/PlusJakartaSans-Regular.ttfis excluded by!**/*.ttfassets/fonts/PlusJakartaSans-SemiBold.ttfis excluded by!**/*.ttfassets/icons/activity.pngis excluded by!**/*.pngassets/icons/add.pngis excluded by!**/*.pngassets/icons/adobe.pngis excluded by!**/*.pngassets/icons/back.pngis excluded by!**/*.pngassets/icons/canva.pngis excluded by!**/*.pngassets/icons/claude.pngis excluded by!**/*.pngassets/icons/dropbox.pngis excluded by!**/*.pngassets/icons/figma.pngis excluded by!**/*.pngassets/icons/github.pngis excluded by!**/*.pngassets/icons/home.pngis excluded by!**/*.pngassets/icons/logo.pngis excluded by!**/*.pngassets/icons/medium.pngis excluded by!**/*.pngassets/icons/menu.pngis excluded by!**/*.pngassets/icons/netflix.pngis excluded by!**/*.pngassets/icons/notion.pngis excluded by!**/*.pngassets/icons/openai.pngis excluded by!**/*.pngassets/icons/plus.pngis excluded by!**/*.pngassets/icons/setting.pngis excluded by!**/*.pngassets/icons/spotify.pngis excluded by!**/*.pngassets/icons/wallet.pngis excluded by!**/*.pngassets/images/avatar.pngis excluded by!**/*.pngassets/images/expo-badge-white.pngis excluded by!**/*.pngassets/images/expo-badge.pngis excluded by!**/*.pngassets/images/expo-logo.pngis excluded by!**/*.pngassets/images/icon.pngis excluded by!**/*.pngassets/images/logo-glow.pngis excluded by!**/*.pngassets/images/partial-react-logo.pngis excluded by!**/*.pngassets/images/splash-icon.pngis excluded by!**/*.pngassets/images/splash-pattern.pngis excluded by!**/*.pngassets/images/tabIcons/explore.pngis excluded by!**/*.pngassets/images/tabIcons/explore@2x.pngis excluded by!**/*.pngassets/images/tabIcons/explore@3x.pngis excluded by!**/*.pngassets/images/tabIcons/home.pngis excluded by!**/*.pngassets/images/tabIcons/home@2x.pngis excluded by!**/*.pngassets/images/tabIcons/home@3x.pngis excluded by!**/*.pngassets/images/tutorial-web.pngis excluded by!**/*.pngbun.lockis excluded by!**/*.lockpackage-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (40)
app.jsonapp/(auth)/Sign_in.tsxapp/(auth)/Sign_up.tsxapp/(auth)/_layout.tsxapp/(tabs)/_layout.tsxapp/(tabs)/explore.tsxapp/(tabs)/index.tsxapp/(tabs)/insights.tsxapp/(tabs)/settings.tsxapp/(tabs)/subscriptions.tsxapp/_layout.tsxapp/modal.tsxapp/onboarding.tsxapp/subscriptions/[id].tsxassets/constants/data.tsassets/constants/icons.tsassets/constants/theme.tsassets/expo.icon/icon.jsoncomponents/external-link.tsxcomponents/haptic-tab.tsxcomponents/hello-wave.tsxcomponents/parallax-scroll-view.tsxcomponents/themed-text.tsxcomponents/themed-view.tsxcomponents/ui/collapsible.tsxcomponents/ui/icon-symbol.ios.tsxcomponents/ui/icon-symbol.tsxconstants/theme.tsglobal.csshooks/use-color-scheme.tshooks/use-color-scheme.web.tshooks/use-theme-color.tsimage.d.tsmetro.config.jsnativewind-env.d.tspackage.jsonpostcss.config.mjsscripts/reset-project.jstsconfig.jsontype.d.ts
💤 Files with no reviewable changes (16)
- app/modal.tsx
- components/haptic-tab.tsx
- hooks/use-color-scheme.ts
- hooks/use-color-scheme.web.ts
- hooks/use-theme-color.ts
- components/external-link.tsx
- components/ui/icon-symbol.ios.tsx
- components/ui/icon-symbol.tsx
- components/themed-view.tsx
- components/parallax-scroll-view.tsx
- components/hello-wave.tsx
- scripts/reset-project.js
- components/themed-text.tsx
- components/ui/collapsible.tsx
- app/(tabs)/explore.tsx
- constants/theme.ts
| const [fontsLoaded] = useFonts({ | ||
| "sans-regular": require("../assets/fonts/PlusJakartaSans-Regular.ttf"), | ||
| "sans-bold": require("../assets/fonts/PlusJakartaSans-Bold.ttf"), | ||
| "sans-medium": require("../assets/fonts/PlusJakartaSans-Medium.ttf"), | ||
| "sans-semibold": require("../assets/fonts/PlusJakartaSans-SemiBold.ttf"), | ||
| "sans-extrabold": require("../assets/fonts/PlusJakartaSans-ExtraBold.ttf"), | ||
| "sans-light": require("../assets/fonts/PlusJakartaSans-Light.ttf") | ||
| }) |
There was a problem hiding this comment.
Handle font loading errors to prevent infinite loading state.
The useFonts hook returns a tuple [loaded, error], but the code only destructures loaded. If font loading fails (corrupted files, missing assets, permission issues), the app will remain stuck showing nothing indefinitely because fontsLoaded will never become true.
🛡️ Proposed fix to add error handling
- const [fontsLoaded] = useFonts({
+ const [fontsLoaded, fontError] = useFonts({
"sans-regular": require("../assets/fonts/PlusJakartaSans-Regular.ttf"),
"sans-bold": require("../assets/fonts/PlusJakartaSans-Bold.ttf"),
"sans-medium": require("../assets/fonts/PlusJakartaSans-Medium.ttf"),
"sans-semibold": require("../assets/fonts/PlusJakartaSans-SemiBold.ttf"),
"sans-extrabold": require("../assets/fonts/PlusJakartaSans-ExtraBold.ttf"),
"sans-light": require("../assets/fonts/PlusJakartaSans-Light.ttf")
})
useEffect(() => {
- if (fontsLoaded) {
+ if (fontsLoaded || fontError) {
SplashScreen.hideAsync();
}
}, [fontsLoaded]);
- if (!fontsLoaded) return null;
+ if (!fontsLoaded && !fontError) return null;Optionally, you can show an error message when fontError is truthy instead of proceeding.
📝 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.
| const [fontsLoaded] = useFonts({ | |
| "sans-regular": require("../assets/fonts/PlusJakartaSans-Regular.ttf"), | |
| "sans-bold": require("../assets/fonts/PlusJakartaSans-Bold.ttf"), | |
| "sans-medium": require("../assets/fonts/PlusJakartaSans-Medium.ttf"), | |
| "sans-semibold": require("../assets/fonts/PlusJakartaSans-SemiBold.ttf"), | |
| "sans-extrabold": require("../assets/fonts/PlusJakartaSans-ExtraBold.ttf"), | |
| "sans-light": require("../assets/fonts/PlusJakartaSans-Light.ttf") | |
| }) | |
| const [fontsLoaded, fontError] = useFonts({ | |
| "sans-regular": require("../assets/fonts/PlusJakartaSans-Regular.ttf"), | |
| "sans-bold": require("../assets/fonts/PlusJakartaSans-Bold.ttf"), | |
| "sans-medium": require("../assets/fonts/PlusJakartaSans-Medium.ttf"), | |
| "sans-semibold": require("../assets/fonts/PlusJakartaSans-SemiBold.ttf"), | |
| "sans-extrabold": require("../assets/fonts/PlusJakartaSans-ExtraBold.ttf"), | |
| "sans-light": require("../assets/fonts/PlusJakartaSans-Light.ttf") | |
| }) | |
| useEffect(() => { | |
| if (fontsLoaded || fontError) { | |
| SplashScreen.hideAsync(); | |
| } | |
| }, [fontsLoaded]); | |
| if (!fontsLoaded && !fontError) return null; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/_layout.tsx` around lines 7 - 14, The useFonts call currently only
destructures fontsLoaded and ignores the error tuple; update the destructuring
to capture the second return value (e.g., const [fontsLoaded, fontError] =
useFonts(...)) and add handling in the component to bail out of the perpetual
loading state when fontError is truthy (for example, render a fallback UI or log
and continue rendering). Locate the useFonts usage in this file (_layout.tsx)
and reference fontsLoaded and the new fontError to ensure the app does not
remain stuck when font loading fails.
| useEffect(() => { | ||
| if (fontsLoaded) { | ||
| SplashScreen.hideAsync(); | ||
| } | ||
| }, [fontsLoaded]); |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Does expo-router version 6 automatically call SplashScreen.preventAutoHideAsync(), or do developers need to call it manually in the root layout?
💡 Result:
Expo Router does not automatically call SplashScreen.preventAutoHideAsync for you. Developers need to call it themselves in the correct place (typically global/module scope in the root file/layout), and you should use Expo Router’s SplashScreen import. Expo’s docs describe preventAutoHideAsync as a function you call to keep the native splash screen visible until you explicitly hide it, and they recommend calling it in global scope rather than inside React components/hooks [1]. Expo Router has been observed to “take control of the splash screen and hide it automatically,” which can override attempts to manually control it if you’re not using the right SplashScreen (from expo-router) and/or you call it too late [2]. Related Expo issue threads also show that splash screen behavior with expo-router depends on using the correct import and calling preventAutoHideAsync early enough (module scope) [3][4]. So, for Expo Router v6: call SplashScreen.preventAutoHideAsync manually in your root layout/root entry file (module scope), then call hideAsync/hide when ready; don’t rely on expo-router to do the prevent call automatically [1][3][4].
Citations:
- 1: https://docs.expo.dev/versions/latest/sdk/splash-screen/
- 2: [expo-router][expo-splash-screen] Expo Router makes the splash screen uncontrollable expo/expo#40464
- 3: Splash Screen Disappears Unaffected by preventAutoHideAsync() expo/expo#34582
- 4: SplashScreen auto-hides when using Expo Router expo/expo#23958
Fix splash screen race in app/_layout.tsx: add SplashScreen.preventAutoHideAsync() in root/module scope.
Expo Router v6 doesn’t automatically call SplashScreen.preventAutoHideAsync(), so only calling SplashScreen.hideAsync() after fontsLoaded (lines 15–19) can let the splash auto-hide before fonts are ready, causing a potential FOUT/flash. Call SplashScreen.preventAutoHideAsync() early in the root layout (module/global scope) and then call hideAsync() once fonts are loaded.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/_layout.tsx` around lines 15 - 19, Call
SplashScreen.preventAutoHideAsync() immediately in the module/root scope
(outside the React component) so the splash cannot auto-hide before assets load,
e.g., invoke and catch errors at module initialization; keep the existing
useEffect that calls SplashScreen.hideAsync() when fontsLoaded becomes true (the
useEffect referencing fontsLoaded should remain as-is). Ensure you reference the
same SplashScreen methods (SplashScreen.preventAutoHideAsync and
SplashScreen.hideAsync) and the existing useEffect/fontsLoaded logic so
hideAsync is only called after fontsLoaded is true.
| <Text> | ||
| Sign In | ||
| </Text> |
There was a problem hiding this comment.
Add styling to the heading for consistency.
The "Sign In" heading lacks a className, while the Sign Up screen (line 12 in Sign_up.tsx) styles its heading with text-xl font-bold.
🎨 Suggested fix for consistent heading style
- <Text>
+ <Text className="text-xl font-bold">
Sign In
</Text>📝 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.
| <Text> | |
| Sign In | |
| </Text> | |
| <Text className="text-xl font-bold"> | |
| Sign In | |
| </Text> |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/`(auth)/Sign_in.tsx around lines 11 - 13, The "Sign In" heading Text
element in Sign_in.tsx is missing the styling used in the Sign Up screen; update
the Text node that renders "Sign In" to include the same className ("text-xl
font-bold") so the heading matches Sign_up.tsx's appearance and maintains
consistent typography across auth screens.
| > | ||
| Go Home | ||
| </Link> | ||
| {/*<Link href="/(auth)/Sign_up">Sign Up</Link>*/} |
There was a problem hiding this comment.
Remove commented-out code.
The commented Link to Sign_up is dead code and should be removed for cleaner maintainability.
🧹 Proposed cleanup
- {/*<Link href="/(auth)/Sign_up">Sign Up</Link>*/}📝 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.
| {/*<Link href="/(auth)/Sign_up">Sign Up</Link>*/} |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/`(auth)/Sign_in.tsx at line 27, Remove the dead commented-out JSX Link in
Sign_in.tsx: delete the line containing {/*<Link href="/(auth)/Sign_up">Sign
Up</Link>*/} so the Sign_in component no longer includes commented-out code and
the file stays clean and maintainable.
| @@ -1,98 +1,33 @@ | |||
| import { Image } from 'expo-image'; | |||
| import { Platform, StyleSheet } from 'react-native'; | |||
| import "@/global.css"; | |||
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify where global.css is imported
rg -n --type=tsx --type=ts '["'\'']`@/global`\.css["'\'']'
# Locate root layout files to confirm the intended import location
fd -i '_layout.tsx' appRepository: hackden3618/react-native-learn-recurly
Length of output: 190
Deduplicate @/global.css to a single root import
app/_layout.tsx already imports @/global.css, but it’s also imported in app/(tabs)/index.tsx and nested layouts (app/(tabs)/_layout.tsx, app/(auth)/_layout.tsx). Keep only the root-level import.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/`(tabs)/index.tsx at line 1, Remove the duplicate import of the CSS
module "`@/global.css`" from app/(tabs)/index.tsx and any nested layout files
(e.g., app/(tabs)/_layout.tsx and app/(auth)/_layout.tsx); keep the single root
import in app/_layout.tsx only so the global stylesheet is applied once. Locate
and delete the line importing "`@/global.css`" in the mentioned files (search for
the exact string "`@/global.css`") and confirm no other non-root files import it.
| const subscription_details = () => { | ||
| const { id } = useLocalSearchParams<{ id: string }>(); |
There was a problem hiding this comment.
Component name must be PascalCase when using hooks.
The component subscription_details uses the useLocalSearchParams hook on line 5, but the component name is snake_case. React requires component names to be PascalCase when calling hooks, or ESLint will flag react-hooks/rules-of-hooks violations and React may not recognize this as a component.
🐛 Proposed fix for naming
-const subscription_details = () => {
+const SubscriptionDetails = () => {
const { id } = useLocalSearchParams<{ id: string }>();Update the export:
-export default subscription_details;
+export default SubscriptionDetails;📝 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.
| const subscription_details = () => { | |
| const { id } = useLocalSearchParams<{ id: string }>(); | |
| const SubscriptionDetails = () => { | |
| const { id } = useLocalSearchParams<{ id: string }>(); |
🧰 Tools
🪛 ESLint
[error] 5-5: React Hook "useLocalSearchParams" is called in function "subscription_details" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".
(react-hooks/rules-of-hooks)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/subscriptions/`[id].tsx around lines 4 - 5, The component
subscription_details is named in snake_case while using the useLocalSearchParams
hook, which violates React hook naming rules; rename the component to PascalCase
(e.g., SubscriptionDetails) and update any corresponding export/default export
and references to that component so the function name is PascalCase and hooks
are called from a proper React component name.
| --font-sans: sans-regular; | ||
| --font-sans-light: sans-light; | ||
| --font-sans-medium: sans-medium; | ||
| --font-sans-semibold: sans-semibold; | ||
| --font-sans-bold: sans-bold; | ||
| --font-sans-extrabold: sans-extrabold; |
There was a problem hiding this comment.
Remove duplicate font family custom properties.
Lines 47-52 duplicate the font family definitions from lines 20-25. This causes unnecessary CSS bloat and triggers linting errors.
🔧 Proposed fix
--spacing-24: 96px;
--spacing-30: 120px;
-
- --font-sans: sans-regular;
- --font-sans-light: sans-light;
- --font-sans-medium: sans-medium;
- --font-sans-semibold: sans-semibold;
- --font-sans-bold: sans-bold;
- --font-sans-extrabold: sans-extrabold;
}📝 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.
| --font-sans: sans-regular; | |
| --font-sans-light: sans-light; | |
| --font-sans-medium: sans-medium; | |
| --font-sans-semibold: sans-semibold; | |
| --font-sans-bold: sans-bold; | |
| --font-sans-extrabold: sans-extrabold; | |
| --spacing-24: 96px; | |
| --spacing-30: 120px; | |
| } |
🧰 Tools
🪛 Stylelint (17.12.0)
[error] 47-47: Duplicate custom property "--font-sans" (declaration-block-no-duplicate-custom-properties)
(declaration-block-no-duplicate-custom-properties)
[error] 48-48: Duplicate custom property "--font-sans-light" (declaration-block-no-duplicate-custom-properties)
(declaration-block-no-duplicate-custom-properties)
[error] 49-49: Duplicate custom property "--font-sans-medium" (declaration-block-no-duplicate-custom-properties)
(declaration-block-no-duplicate-custom-properties)
[error] 50-50: Duplicate custom property "--font-sans-semibold" (declaration-block-no-duplicate-custom-properties)
(declaration-block-no-duplicate-custom-properties)
[error] 51-51: Duplicate custom property "--font-sans-bold" (declaration-block-no-duplicate-custom-properties)
(declaration-block-no-duplicate-custom-properties)
[error] 52-52: Duplicate custom property "--font-sans-extrabold" (declaration-block-no-duplicate-custom-properties)
(declaration-block-no-duplicate-custom-properties)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@global.css` around lines 47 - 52, Remove the duplicate CSS custom properties
for the font families (--font-sans, --font-sans-light, --font-sans-medium,
--font-sans-semibold, --font-sans-bold, --font-sans-extrabold) in global.css by
deleting the redundant block so only the original declarations remain; locate
the repeated set (the second occurrence of those --font-sans* properties) and
remove it to eliminate bloat and lint errors while keeping the initial
definitions intact.
| "expo-symbols": "~1.0.8", | ||
| "expo-system-ui": "~6.0.9", | ||
| "expo-web-browser": "~15.0.10", | ||
| "nativewind": "^5.0.0-preview.4", |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Is NativeWind 5.0.0-preview.4 stable for production use?
💡 Result:
No. NativeWind 5.0.0-preview.4 is not indicated as stable for production; NativeWind v5 is explicitly a preview, and the project’s own migration docs direct production apps to use NativeWind v4.1 instead. Evidence: - The official v5 migration/upgrade materials state that Nativewind v5 is “currently available in preview” and installed via the preview channel (nativewind@preview). [1] - The migration guide for NativeWind states: “NativeWind v5 is in preview” and: “For production apps, use NativeWind v4.1, which is stable and fully supported.” It also notes for v5: “Only use for experimental projects.” [2] - A community discussion round-tripping v4→v5 echoes the “prefer for this to be stable…before relying on it in production” sentiment, consistent with v5 being a preview rather than a production-guaranteed release. [3] Practical takeaway: if you need production stability, stick to the stable v4.1 line; treat 5.0.0-preview.4 as experimental until the project declares a production-ready v5 release. [2][1]
Citations:
- 1: https://www.nativewind.dev/blog/v5-migration-guide
- 2: https://nativewind-nativewind.mintlify.app/guides/migration
- 3: Trouble migrating from NativeWind v4 to v5 (Expo + Tailwind CSS 4) nativewind/nativewind#1617
Avoid NativeWind v5 preview in production dependency
nativewind@^5.0.0-preview.4 is a v5 preview release; NativeWind’s upgrade/migration docs state v5 is “currently available in preview” / “only use for experimental projects” and direct production apps to use nativewind v4.1 (stable). Replace the dependency with the stable v4.1 line (or justify keeping v5 preview for experimental-only usage).
https://www.nativewind.dev/blog/v5-migration-guide • https://nativewind-nativewind.mintlify.app/guides/migration
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@package.json` at line 31, Replace the preview NativeWind dependency in
package.json: find the "nativewind": "^5.0.0-preview.4" entry and change it to
the stable v4.1 release (e.g. "nativewind": "4.1.x" or specific "4.1.0") so
production uses the supported stable version; if you intentionally need the v5
preview, add a justification comment in the PR description instead of shipping
the preview in package.json.
| "react": "19.1.0", | ||
| "react-dom": "19.1.0", | ||
| "react-native": "0.81.5", |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check React Native version requirements and React compatibility
# Search for React version constraints in react-native package
rg -n "react.*peerDependencies" node_modules/react-native/package.json -A 3 -B 1 || echo "Could not find peerDependencies"
# Check if there are any React 19 compatibility notes in the repository
rg -n -i "react.*(19|compatibility)" --type md -g '!node_modules/**'Repository: hackden3618/react-native-learn-recurly
Length of output: 114
🌐 Web query:
React Native 0.81.5 compatibility with React 19
💡 Result:
React Native 0.81.5 is compatible with React 19 only when the React version is exactly the one RN 0.81.5 expects. What’s expected for RN 0.81.5: - RN 0.81.5’s bundled React Native renderer performs a strict version check and expects React 19.1.0 (i.e., react and react-native-renderer must match exactly) [1][2]. What that means in practice: - If you set your app’s react package to something like React 19.1.2 (or any other React 19.x that isn’t 19.1.0), you can hit an “Incompatible React versions” runtime error due to that strict equality check [1][2]. - RN 0.81 has already had follow-up updates to sync React 19.1.4 into the 0.81 release line (meaning later RN patches bump the expected React version) [3]. Recommended approach for compatibility with React 19: - Prefer upgrading React Native to a patch version that syncs the exact React 19.x you want (since 0.81.5 specifically expects 19.1.0) [1][3]. - Alternatively, pin react to 19.1.0 to satisfy RN 0.81.5’s exact-match requirement [1][2]. Confidence: High (based on RN renderer’s strict expected React version and version-mismatch behavior) [1][2].
Citations:
- 1: [React2Shell] How to mitigate in React Native react/react-native#54772
- 2: Be less strict in the react / react-native-renderer version check react-native-community/discussions-and-proposals#964
- 3: [0.81] Sync React 19.1.4 into React Native react/react-native#54846
Downgrade React Native/React 19 compatibility risk (exact version pin)
- RN
0.81.5’s renderer performs a strict React version check and expects React19.1.0; withpackage.jsonpinning"react": "19.1.0"(and"react-dom": "19.1.0") alongside"react-native": "0.81.5", the common “incompatible React versions” runtime error from a React19.xmismatch should be avoided. - Still run the app/tests and verify any React 19 API breakages used by your code or third-party dependencies.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@package.json` around lines 32 - 34, The package.json currently pins "react"
and "react-dom" to the exact version "19.1.0" while "react-native" is "0.81.5";
loosen the pins to a compatible semver range to avoid strict renderer
version-check failures by changing the "react" and "react-dom" entries from
exact versions to a range (for example ^19.1.0 or >=19.1.0 <20.0.0) so the React
runtime can satisfy react-native@0.81.5’s expected React 19.x installation;
after updating the "react" and "react-dom" entries run the app and tests and
verify no React 19 API breakages occur.
|
Note Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it. An unexpected error occurred while generating fixes: |
…d the routes
Summary by CodeRabbit
Release Notes
New Features
UI & Design