-
Notifications
You must be signed in to change notification settings - Fork 24
Refactor mobile app with Neobrutalism UI using Expo and NativeWind #209
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
✅ Deploy Preview for split-but-wiser canceled.
|
WalkthroughThe mobile app undergoes a significant architectural refactoring, transitioning from React Navigation to Expo Router-based navigation with NativeWind styling. The refactor removes the previous auth context, API client infrastructure, screen components, and navigation stack hierarchy, while introducing new typed layout and configuration files to support the modern routing and CSS-in-JS approach. Changes
Possibly related PRs
Suggested reviewers
Pre-merge checks❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
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. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #209 +/- ##
=======================================
Coverage 72.79% 72.79%
=======================================
Files 17 17
Lines 1669 1669
Branches 154 154
=======================================
Hits 1215 1215
Misses 400 400
Partials 54 54
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 14
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
⛔ Files ignored due to path filters (11)
mobile/assets/adaptive-icon.pngis excluded by!**/*.pngmobile/assets/adaptive-icon/background.pngis excluded by!**/*.pngmobile/assets/adaptive-icon/foreground.pngis excluded by!**/*.pngmobile/assets/favicon.pngis excluded by!**/*.pngmobile/assets/icon.pngis excluded by!**/*.pngmobile/assets/pwa/chrome-icon/chrome-icon-144.pngis excluded by!**/*.pngmobile/assets/pwa/chrome-icon/chrome-icon-192.pngis excluded by!**/*.pngmobile/assets/pwa/chrome-icon/chrome-icon-512.pngis excluded by!**/*.pngmobile/assets/splash-icon.pngis excluded by!**/*.pngmobile/assets/splash.pngis excluded by!**/*.pngmobile/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (39)
mobile/.gitignore(1 hunks)mobile/App.js(0 hunks)mobile/api/auth.js(0 hunks)mobile/api/client.js(0 hunks)mobile/api/groups.js(0 hunks)mobile/app.json(1 hunks)mobile/app/_layout.tsx(1 hunks)mobile/app/index.tsx(1 hunks)mobile/babel.config.js(1 hunks)mobile/components/NeoCard.tsx(1 hunks)mobile/components/Themed.tsx(1 hunks)mobile/constants/colors.ts(1 hunks)mobile/context/AuthContext.js(0 hunks)mobile/eas.json(0 hunks)mobile/global.css(1 hunks)mobile/index.js(0 hunks)mobile/lib/utils.ts(1 hunks)mobile/metro.config.js(1 hunks)mobile/nativewind-env.d.ts(1 hunks)mobile/navigation/AccountStackNavigator.js(0 hunks)mobile/navigation/AppNavigator.js(0 hunks)mobile/navigation/AuthNavigator.js(0 hunks)mobile/navigation/GroupsStackNavigator.js(0 hunks)mobile/navigation/MainNavigator.js(0 hunks)mobile/package.json(1 hunks)mobile/screens/AccountScreen.js(0 hunks)mobile/screens/AddExpenseScreen.js(0 hunks)mobile/screens/EditProfileScreen.js(0 hunks)mobile/screens/FriendsScreen.js(0 hunks)mobile/screens/GroupDetailsScreen.js(0 hunks)mobile/screens/GroupSettingsScreen.js(0 hunks)mobile/screens/HomeScreen.js(0 hunks)mobile/screens/JoinGroupScreen.js(0 hunks)mobile/screens/LoginScreen.js(0 hunks)mobile/screens/SignupScreen.js(0 hunks)mobile/tailwind.config.js(1 hunks)mobile/tsconfig.json(1 hunks)mobile/utils/balanceCalculator.js(0 hunks)mobile/utils/currency.js(0 hunks)
💤 Files with no reviewable changes (24)
- mobile/navigation/MainNavigator.js
- mobile/api/auth.js
- mobile/App.js
- mobile/screens/AddExpenseScreen.js
- mobile/navigation/AuthNavigator.js
- mobile/screens/EditProfileScreen.js
- mobile/screens/FriendsScreen.js
- mobile/navigation/AppNavigator.js
- mobile/screens/JoinGroupScreen.js
- mobile/utils/currency.js
- mobile/screens/GroupDetailsScreen.js
- mobile/navigation/AccountStackNavigator.js
- mobile/navigation/GroupsStackNavigator.js
- mobile/screens/HomeScreen.js
- mobile/context/AuthContext.js
- mobile/screens/GroupSettingsScreen.js
- mobile/screens/SignupScreen.js
- mobile/utils/balanceCalculator.js
- mobile/api/groups.js
- mobile/screens/AccountScreen.js
- mobile/screens/LoginScreen.js
- mobile/api/client.js
- mobile/eas.json
- mobile/index.js
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: Devasy23/splitwiser PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T18:33:22.581Z
Learning: Applies to {mobile/context/**/*.js,web/contexts/**/*.{ts,tsx}} : React Context should be used for state management of authentication and app-wide state in both mobile and web
Learnt from: CR
Repo: Devasy23/splitwiser PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T18:33:22.581Z
Learning: Applies to mobile/screens/**/*.js : Mobile screens should be created as components in `mobile/screens/` and registered in the navigation structure
📚 Learning: 2025-11-27T18:33:22.581Z
Learnt from: CR
Repo: Devasy23/splitwiser PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T18:33:22.581Z
Learning: Applies to mobile/screens/**/*.js : Mobile screens should be created as components in `mobile/screens/` and registered in the navigation structure
Applied to files:
mobile/app/index.tsx
🧬 Code graph analysis (5)
mobile/app/index.tsx (2)
mobile/components/Themed.tsx (3)
ThemedView(4-6)ThemedText(8-20)ThemedButton(22-45)mobile/components/NeoCard.tsx (1)
NeoCard(9-15)
mobile/tailwind.config.js (1)
mobile/metro.config.js (2)
require(1-1)require(2-2)
mobile/components/NeoCard.tsx (1)
mobile/lib/utils.ts (1)
cn(4-6)
mobile/components/Themed.tsx (1)
mobile/lib/utils.ts (1)
cn(4-6)
mobile/app/_layout.tsx (1)
web/contexts/ThemeContext.tsx (1)
ThemeProvider(16-41)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: rn-bundle-analysis
🔇 Additional comments (16)
mobile/.gitignore (1)
1-8: Perfect! Now I have all the information needed to verify the review comment. Let me analyze:Key Finding: The root
.gitignorealready contains comprehensive patterns that apply globally to the entire repository, including themobile/directory:
.env,.env.local,.env.development,.env.test,.env.production,.env*.local✓.metro-health-check*(Metro caching) ✓.vscode/,.idea/(IDE configs) ✓- Security files:
.key,.pem,.p12,secrets/,private/, etc. ✓The
mobile/.gitignoreis intentionally simplified because this is a monorepo where the root.gitignoreapplies to all subdirectories. The mobile-specific.gitignoreonly needs patterns unique to that directory.
Rewritten Review Comment:
The
.gitignoresimplification is appropriate for this monorepo structure; root.gitignorealready covers critical patterns.The mobile-specific
.gitignoreis correctly simplified. Environment variables (.env,.env.local, etc.), Metro caches (.metro-health-check*), IDE configs (.vscode/,.idea/), and security files are already ignored via the root.gitignore, which applies globally. There are no missing security-critical patterns—the removal of "local env" entries mentioned in the summary refers to not duplicating patterns already defined at the root level. No changes needed.mobile/global.css (1)
1-3: Tailwind directives are correct; ensure the CSS is importedThe Tailwind directives are standard and correct. Just make sure
global.cssis actually imported from your app entry (e.g.,app/_layout.tsxor equivalent) so Metro bundles it and NativeWind styles take effect.mobile/lib/utils.ts (1)
1-6: cn helper implementation is idiomatic and correctThe
cnhelper usingclsx+twMergeis a standard Tailwind class-merging pattern, and the use of a spreadClassValue[]is type-safe. No changes needed here.mobile/nativewind-env.d.ts (1)
1-1: NativeWind TypeScript reference is correctly set upUsing
nativewind-env.d.tswith/// <reference types="nativewind/types" />and including it intsconfig.jsonmatches NativeWind's official TypeScript setup guidance, ensuring type merging works correctly. The filename is particularly important—NativeWind explicitly warns against using alternative names likenativewind.d.ts, which would prevent the compiler from picking it up.mobile/metro.config.js (1)
1-6: Metro + NativeWind config is correct and follows current documentationThe
metro.config.jsimplementation matches the official NativeWind Expo setup pattern exactly. Withnativewind@^4.1.23andexpo~54.0.25installed, the configuration correctly wraps the default Expo config withwithNativeWind(config, { input: "./global.css" })as recommended in current documentation. The approach is sound as long asglobal.cssremains at the project root.mobile/tsconfig.json (1)
1-19: TypeScript and path alias configuration verified as compatible with Expo SDK 54The strict TypeScript configuration extending
expo/tsconfig.baseis explicitly supported by Expo's official documentation. Path aliases usingbaseUrlandpathsare handled by Metro'stsconfigPathsresolver by default in Expo projects. The@/*mapping to the mobile root is a valid setup pattern. Theincludelist correctly captures.expotypes and NativeWind ambient declarations. No compatibility issues identified with Expo 54.0.25.mobile/babel.config.js (1)
1-12: Config structure is correct for NativeWind v4 + Expo + Reanimated, but be aware of a known Metro/Babel incompatibilityYour setup matches the recommended configuration for these versions (Expo ~54.0.25, NativeWind ^4.1.23, react-native-reanimated ~4.1.1). The presets order and reanimated plugin placement are both correct. However, there's a documented Metro/Babel incompatibility issue where including
"nativewind/babel"can trigger[BABEL] index.js: .plugins is not a valid Plugin propertyerrors. If you encounter this, check your Expo SDK version alignment and clear Metro cache (expo start -c); if the error persists, refer to the NativeWind/Metro GitHub issues for workarounds.mobile/app.json (1)
32-46: Review expo-build-properties plugin configuration.The
expo-build-propertiesplugin with new architecture flags is correctly configured. However, ensure your build environment supports these flags.Note that new architecture requires:
- Xcode 15+ for iOS
- Android Gradle Plugin 8+
- NDK 26+
Verify your CI/CD pipeline and team's local environments meet these requirements.
mobile/package.json (3)
4-4: Entry point change verified and complete.The old
mobile/index.jshas been successfully removed and no references to it remain in the codebase. The entry point has been correctly updated toexpo-router/entry, which is the appropriate entry point for Expo Router projects. No further action needed.
11-36: Removed dependencies verified as unused in codebase.The verification confirms that none of the removed packages—
@react-native-async-storage/async-storage,axios, or React Navigation packages (@react-navigation/native,@react-navigation/stack)—are referenced anywhere in the mobile directory code. The dependency removal is safe.
25-27: The package.json React and React Native versions are correct and compatible; the original concerns are based on outdated information.The review raised concerns based on outdated knowledge:
React Native 0.81.5 is valid: It was released on October 21, 2025 (well after the knowledge cutoff used in the review). The latest stable is now 0.82 (October 6, 2025), not 0.76.x.
React 19.1.0 is compatible with React Native 0.81.5: React 19 was introduced into the React Native release train starting with 0.78/0.80 and continues through 0.81 and 0.82. This combination is fully supported and expected.
expo-router supports React 19: The project's expo-router has experimental support for React 19 features (React Server Components) in SDK 52+, so the dependency choice is sound.
The forced overrides are appropriate: Forcing React 19 across dependencies is the correct approach for React Native 0.81.5, as the entire ecosystem expects React 19.x in these versions.
No action is needed on the package.json React/React Native versions or overrides.
mobile/components/Themed.tsx (2)
1-45: I'm unable to verify the Tailwind configuration directly because the repository clone failed. Since I cannot access the repository files to inspect thetailwind.config.js,global.css, and color constant definitions, I cannot definitively confirm whether all the custom Tailwind classes (neo-bg,neo-white,neo-dark,neo-main,neo-second,neo-accent,shadow-neo,shadow-neo-sm,font-mono-bold,font-sans-bold,font-sans) are properly configured.The original review comment's request for verification is valid and important for ensuring the components will render correctly. However, without access to the repository, I cannot complete the verification task.
Verify all custom Tailwind classes are properly configured.
The Themed components use multiple custom classes that require configuration:
- Colors:
neo-bg,neo-white,neo-dark,neo-main,neo-second,neo-accent- Shadows:
shadow-neo,shadow-neo-sm- Fonts:
font-mono-bold,font-sans-bold,font-sansEnsure all these classes are defined in your Tailwind configuration (
tailwind.config.js) or global CSS file (global.css). Missing definitions will cause these styles to not be applied at runtime.
11-14: Custom font classes are properly configured and fonts are loaded.Verification confirms:
tailwind.config.jscorrectly defines all custom font families:
font-sans: Inter_400Regularfont-sans-bold: Inter_700Boldfont-mono: SpaceGrotesk_400Regularfont-mono-bold: SpaceGrotesk_700Bold_layout.tsxloads all four fonts via theuseFontshook- All font classes used in
Themed.tsx(lines 11-15, 42) map to properly configured fontsNo action required.
mobile/components/NeoCard.tsx (1)
11-11: Custom Tailwind classes are properly configured.All three custom classes used in the NeoCard component are correctly defined in
mobile/tailwind.config.js:
bg-neo-white→colors.neo.white: '#ffffff'(line 21)border-neo-dark→colors.neo.dark: '#1a1a1a'(line 20)shadow-neo→boxShadow.neo: '4px 4px 0px 0px #1a1a1a'(line 25)No configuration issues found.
mobile/app/_layout.tsx (2)
1-1: Consider aligning mobile theme management with web's custom theming approach if dynamic theme switching is needed.The mobile app currently uses
@react-navigation/native'sThemeProviderwithDefaultTheme(line 33), which handles only navigation theme. In contrast, the web version implements a customThemeContext(web/contexts/ThemeContext.tsx) that manages both theme style (neobrutalism/glassmorphism) and mode (light/dark) as app-wide state.While the mobile Tailwind config includes complete neobrutalism design tokens (colors, shadows), there is currently no custom theme context to manage potential style or mode switching. If the mobile app requires dynamic theme switching similar to the web implementation, create a React Context following the same pattern as web's
ThemeContext. If the mobile design is static neobrutalism only, the current navigation-focusedThemeProvideris sufficient—clarify this requirement to avoid future refactoring.Also applies to: 33-33
1-9: React 19 is officially supported by Expo SDK 54 and expo-router 6.0.15 — no compatibility issue here.The original concern is incorrect. Expo SDK 54 explicitly targets React 19.1, and expo-router v6.0.15 is published for use with these versions. The code shown uses standard, supported imports and patterns.
While some community reports mention web-specific build/runtime issues when upgrading to SDK 54 + React 19 (e.g., React.createContext errors), these are not caused by the imports in lines 1-9 or the component structure in lines 14-40. If web builds fail, follow Expo's upgrade guide: use
npx expo install, ensure matching package versions, clear caches, and rebuild native binaries.
mobile/app.json
Outdated
| "newArchEnabled": true, | ||
| "splash": { | ||
| "image": "./assets/splash-icon.png", | ||
| "image": "./assets/splash.png", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Verify splash screen and other asset files exist
# Check if splash.png exists
if [ -f "mobile/assets/splash.png" ]; then
echo "✓ splash.png exists"
ls -lh mobile/assets/splash.png
else
echo "✗ splash.png NOT FOUND"
fi
# Check if old splash-icon.png exists
if [ -f "mobile/assets/splash-icon.png" ]; then
echo "✓ splash-icon.png exists (old filename)"
ls -lh mobile/assets/splash-icon.png
else
echo "✗ splash-icon.png NOT FOUND"
fi
# List all asset files in the directory
echo -e "\nAll files in mobile/assets/:"
if [ -d "mobile/assets" ]; then
ls -lh mobile/assets/
else
echo "mobile/assets/ directory not found"
fiRepository: Devasy23/splitwiser
Length of output: 178
🏁 Script executed:
#!/bin/bash
# Description: Search for splash-related files and explore mobile directory structure
echo "=== Searching for splash-related files ==="
find . -type f -name "*splash*" 2>/dev/null || echo "No splash files found"
echo -e "\n=== Mobile directory structure ==="
ls -la mobile/ 2>/dev/null || echo "mobile/ directory not found"
echo -e "\n=== Check for common asset directories ==="
find mobile -type d -name "assets" -o -name "asset" 2>/dev/null
echo -e "\n=== View app.json content ==="
if [ -f "mobile/app.json" ]; then
cat mobile/app.json
else
echo "mobile/app.json not found"
fiRepository: Devasy23/splitwiser
Length of output: 2311
Create missing mobile/assets/ directory with required asset files.
The mobile/assets/ directory and all referenced asset files are missing from the repository. The app.json file references multiple required assets that do not exist:
./assets/splash.png(splash screen)./assets/icon.png(app icon)./assets/adaptive-icon.png(Android adaptive icon)./assets/favicon.png(web favicon)
This will cause build failures. Create the directory and add all required asset files before building.
🤖 Prompt for AI Agents
In mobile/app.json around line 10, the referenced assets directory and files are
missing; create a new mobile/assets/ directory and add the required image files:
splash.png, icon.png, adaptive-icon.png, and favicon.png (ensure correct
sizes/formats expected by the app - e.g., splash and icon PNGs and adaptive icon
layers), commit them to the repo, and update any paths in app.json if filenames
or locations differ.
| "android": { | ||
| "newArchEnabled": true | ||
| }, | ||
| "ios": { | ||
| "newArchEnabled": true | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# First, locate and examine the package.json file
find . -name "package.json" -type f | head -20Repository: Devasy23/splitwiser
Length of output: 103
🏁 Script executed:
#!/bin/bash
# Check the structure to understand where mobile/package.json is
ls -la mobile/ 2>/dev/null | head -20Repository: Devasy23/splitwiser
Length of output: 1069
🌐 Web query:
react-native-reanimated 4.1.1 new architecture support compatibility
💡 Result:
Short answer: Reanimated 4.1.1 requires the React Native New Architecture (Fabric) — it does not support the legacy (old/Paper) architecture. It is compatible with recent React Native releases (supported 0.78–0.82 in the official compatibility matrix) and expects react-native-worklets 0.5.x or 0.6.x as a peer dependency. If you need to stay on the old architecture, use Reanimated 3.x. [1][2]
Sources:
- Reanimated compatibility matrix (4.1.x entries: New Architecture only; RN versions and worklets table). [1]
- Reanimated migration notes (4.x = New Architecture only; added react-native-worklets dependency). [2]
🌐 Web query:
nativewind 4.1.23 React Native new architecture support
💡 Result:
Short answer: NativeWind 4.1.23 (released Nov 4, 2024) is a v4 release and does not ship full, explicit support for React Native's New Architecture (Fabric/TurboModules). NativeWind v5 is the release that documents explicit New Architecture / RN 0.81+ requirements and new styling features that rely on the New Architecture — upgrade to v5 if you need full New-Architecture support. [1][2][3][4]
Sources
- nativewind 4.1.23 release metadata / changelog. [1]
- package release list / date for 4.1.23. [2]
- NativeWind v5 migration/requirements note (calls out RN 0.81+ and New Architecture support). [3]
- Recent GitHub issues where v4.x (4.1.23) users report runtime/build issues (examples showing v4 can have problems in some RN/Expo setups). [4]
🌐 Web query:
react-native-svg 15.12.1 new architecture support
💡 Result:
Short answer: Yes. react-native-svg added Fabric / New Architecture support back in the v13 major release, and v15.12.1 (released Jul 31, 2025) is compatible with newer React Native releases (adds RN 0.81.0 support and related fixes). [1][2]
Sources:
- react-native-svg GitHub release v15.12.1 (notes: adds RN 0.81.0 support). [1]
- react-native-svg v13 release notes (introduced Fabric / New Architecture support). [2]
🌐 Web query:
lucide-react-native 0.475.0 React Native new architecture
💡 Result:
Short answer
- lucide-react-native v0.475.0 is a React Native package (published on npm). [1][2]
- It renders icons using react-native-svg, so compatibility with React Native’s New Architecture (Fabric/JSI/TurboModules) depends on the react-native-svg native bindings and your RN version — not on lucide’s JS icon components themselves. [2][3]
What to check / do
- Ensure your app’s react-native-svg is a version that supports Fabric (react-native-svg added Fabric support in the v13+ line and continued updates). Use a react-native-svg release compatible with your React Native version. [3]
- Use an RN version that supports the New Architecture (RN >= 0.75 / 0.76+ guidance) or follow RN docs to opt-in/out. If you enable the New Architecture, test native builds on iOS and Android — some native modules require updates or special config. [4]
- If you hit build/runtime errors, check react-native-svg GitHub issues (there are historical/new-arch build reports) and consider disabling the New Architecture temporarily while you resolve native-dep compatibility. [5][6]
References
- lucide-react-native @0.475.0 (npm). [1]
- lucide-react-native docs (uses react-native-svg). [2]
- react-native-svg release notes (Fabric/new-arch support added in v13+). [3]
- React Native “New Architecture is here” / docs (Fabric, JSI, TurboModules). [4]
- react-native-svg GitHub issues discussing New Architecture build problems. [5]
- Example community report about New Architecture incompatibilities and opt-out steps. [6]
If you want, I can: (a) check which react-native-svg version you have and recommend an exact upgrade, or (b) list commands to enable/disable the New Architecture for iOS/Android and run a quick compatibility checklist.
🏁 Script executed:
#!/bin/bash
# Read the mobile/package.json to see actual dependency versions
cat -n ./mobile/package.jsonRepository: Devasy23/splitwiser
Length of output: 1716
Upgrade nativewind to v5 for full React Native new architecture support.
The concern about newArchEnabled: true is partially valid but incorrectly framed. Here's what the verification found:
- nativewind 4.1.23: Does not have explicit new architecture support; v5 is required for proper Fabric/TurboModules compatibility. This is a real incompatibility that may cause build or runtime failures.
- react-native-reanimated 4.1.1: Actually requires the new architecture (v4.x does not support the old Paper architecture), so this is not a concern but a requirement—and your setup correctly includes it.
- react-native-svg 15.12.1: Supports new architecture (Fabric support added in v13+).
- lucide-react-native 0.475.0: Compatible via react-native-svg.
Action: Upgrade nativewind from 4.1.23 to 5.x to ensure full new architecture compatibility with your React Native 0.81.5 setup.
🤖 Prompt for AI Agents
In mobile/app.json around lines 38 to 43, the project enables the RN new
architecture but uses nativewind 4.1.23 which lacks full Fabric/TurboModules
support; update the nativewind dependency to a v5.x release (e.g., "^5.0.0") in
mobile/package.json, run your package manager install, run pod install for iOS
and rebuild both Android and iOS, and apply any required nativewind v5 migration
steps from the v5 changelog (config changes) to ensure compatibility with React
Native 0.81.5 and the new architecture.
| useEffect(() => { | ||
| if (loaded) { | ||
| SplashScreen.hideAsync(); | ||
| } | ||
| }, [loaded]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Add error handling for font loading failures.
The useEffect hides the splash screen when fonts are loaded, but doesn't handle loading errors. If font loading fails, users may be stuck on the splash screen indefinitely.
Consider handling the error state from useFonts:
- const [loaded] = useFonts({
+ const [loaded, error] = useFonts({
Inter_400Regular,
Inter_700Bold,
SpaceGrotesk_400Regular,
SpaceGrotesk_700Bold,
});
useEffect(() => {
- if (loaded) {
+ if (loaded || error) {
SplashScreen.hideAsync();
}
}, [loaded]);
+
+ if (error) {
+ console.error('Font loading error:', error);
+ }Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In mobile/app/_layout.tsx around lines 22 to 26, the effect currently hides the
splash screen only when fonts load successfully; it lacks handling for font
loading errors which can leave users stuck on the splash screen. Update the
component to read the error returned by useFonts, and in the effect hide the
splash screen both when loaded is true or when an error occurs; also surface a
fallback UI or return early to avoid rendering the app with missing fonts, and
log or report the error for diagnostics. Ensure the effect cleans up correctly
and you don’t call hideAsync more than necessary.
| <Stack> | ||
| <Stack.Screen name="index" options={{ headerShown: false }} /> | ||
| </Stack> | ||
| <StatusBar style="dark" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
StatusBar positioning may be incorrect.
The StatusBar is rendered as a sibling to the Stack component inside ThemeProvider. Typically, StatusBar should be:
- Outside all navigation containers (as a top-level component), or
- Inside the first screen component
The current positioning might cause the StatusBar to remount unnecessarily during navigation transitions.
Consider moving it outside the ThemeProvider:
return (
<ThemeProvider value={DefaultTheme}>
+ <StatusBar style="dark" />
<Stack>
<Stack.Screen name="index" options={{ headerShown: false }} />
</Stack>
- <StatusBar style="dark" />
</ThemeProvider>
);Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In mobile/app/_layout.tsx around line 37, the StatusBar is rendered as a sibling
to the Stack inside ThemeProvider which can cause unnecessary remounts during
navigation; move the <StatusBar> out of the ThemeProvider to a top-level
position (above ThemeProvider and navigation containers) so it remains stable
across screen transitions, or alternatively place it inside the app's first
screen component if you require screen-specific bar settings; update imports if
needed and ensure only one global StatusBar instance exists.
| export default function HomeScreen() { | ||
| return ( | ||
| <ThemedView className="flex-1"> | ||
| <SafeAreaView className="flex-1"> | ||
| <ScrollView contentContainerStyle={{ padding: 20 }}> | ||
|
|
||
| {/* Header */} | ||
| <View className="mb-8"> | ||
| <ThemedText type="title" className="mb-2">Hello, Jules!</ThemedText> | ||
| <ThemedText className="text-gray-600">Welcome back to Splitwiser.</ThemedText> | ||
| </View> | ||
|
|
||
| {/* Balance Card */} | ||
| <NeoCard className="bg-neo-main mb-8"> | ||
| <View className="flex-row justify-between items-center mb-4"> | ||
| <ThemedText className="text-neo-white font-mono-bold">TOTAL BALANCE</ThemedText> | ||
| <Wallet color="white" size={24} /> | ||
| </View> | ||
| <ThemedText type="title" className="text-neo-white text-5xl mb-4">$1,234.56</ThemedText> | ||
| <View className="flex-row gap-4"> | ||
| <View className="bg-white/20 px-3 py-1 rounded-full border border-white"> | ||
| <ThemedText className="text-white text-xs font-mono-bold">+2.4% this month</ThemedText> | ||
| </View> | ||
| </View> | ||
| </NeoCard> | ||
|
|
||
| {/* Quick Actions */} | ||
| <View className="flex-row gap-4 mb-8"> | ||
| <ThemedButton | ||
| title="Add Expense" | ||
| className="flex-1" | ||
| variant="secondary" | ||
| onPress={() => console.log('Add Expense')} | ||
| /> | ||
| <ThemedButton | ||
| title="Settle Up" | ||
| className="flex-1" | ||
| variant="accent" | ||
| onPress={() => console.log('Settle Up')} | ||
| /> | ||
| </View> | ||
|
|
||
| {/* Recent Activity */} | ||
| <View className="mb-4 flex-row justify-between items-end"> | ||
| <ThemedText type="subtitle">Recent Activity</ThemedText> | ||
| <ThemedText type="link">View All</ThemedText> | ||
| </View> | ||
|
|
||
| <View className="gap-4"> | ||
| <NeoCard className="flex-row items-center justify-between p-4"> | ||
| <View className="flex-row items-center gap-4"> | ||
| <View className="w-10 h-10 bg-neo-second border-2 border-neo-dark items-center justify-center"> | ||
| <TrendingUp color="black" size={20} /> | ||
| </View> | ||
| <View> | ||
| <ThemedText type="defaultSemiBold">Grocery Run</ThemedText> | ||
| <ThemedText className="text-xs text-gray-500">Yesterday</ThemedText> | ||
| </View> | ||
| </View> | ||
| <ThemedText className="font-mono-bold text-red-500">-$45.00</ThemedText> | ||
| </NeoCard> | ||
|
|
||
| <NeoCard className="flex-row items-center justify-between p-4"> | ||
| <View className="flex-row items-center gap-4"> | ||
| <View className="w-10 h-10 bg-neo-accent border-2 border-neo-dark items-center justify-center"> | ||
| <PieChart color="black" size={20} /> | ||
| </View> | ||
| <View> | ||
| <ThemedText type="defaultSemiBold">Movie Night</ThemedText> | ||
| <ThemedText className="text-xs text-gray-500">2 days ago</ThemedText> | ||
| </View> | ||
| </View> | ||
| <ThemedText className="font-mono-bold text-green-600">+$12.50</ThemedText> | ||
| </NeoCard> | ||
|
|
||
| <NeoCard className="flex-row items-center justify-between p-4"> | ||
| <View className="flex-row items-center gap-4"> | ||
| <View className="w-10 h-10 bg-neo-main border-2 border-neo-dark items-center justify-center"> | ||
| <Wallet color="white" size={20} /> | ||
| </View> | ||
| <View> | ||
| <ThemedText type="defaultSemiBold">Rent Payment</ThemedText> | ||
| <ThemedText className="text-xs text-gray-500">Oct 1</ThemedText> | ||
| </View> | ||
| </View> | ||
| <ThemedText className="font-mono-bold text-red-500">-$800.00</ThemedText> | ||
| </NeoCard> | ||
| </View> | ||
|
|
||
| </ScrollView> | ||
| </SafeAreaView> | ||
| </ThemedView> | ||
| ); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add accessibility labels for icons and interactive elements.
Icons (Wallet, PieChart, TrendingUp) and buttons lack accessibility labels, which impacts screen reader users.
Add accessibilityLabel props:
<Wallet
color="white"
size={24}
+ accessibilityLabel="Wallet icon"
/>And for buttons:
<ThemedButton
title="Add Expense"
className="flex-1"
variant="secondary"
onPress={() => console.log('Add Expense')}
+ accessibilityLabel="Add a new expense"
+ accessibilityHint="Opens the add expense screen"
/>Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In mobile/app/index.tsx around lines 7-100, the icons (Wallet, PieChart,
TrendingUp) and ThemedButton components are missing accessibility labels; add
accessibilityLabel props to each icon with short descriptive text (e.g., "wallet
icon", "pie chart icon", "trending up icon") and mark purely decorative icons as
accessibilityRole="image" or accessible={false} if appropriate, and add
accessibilityLabel and accessibilityRole="button" to both ThemedButton instances
(plus optional accessibilityHint describing the action) so screen readers can
identify and describe these interactive elements.
| export function NeoCard({ className, children }: CardProps) { | ||
| return ( | ||
| <View className={cn("bg-neo-white border-2 border-neo-dark shadow-neo p-4", className)}> | ||
| {children} | ||
| </View> | ||
| ); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
LGTM with optional enhancement suggestion.
The NeoCard component is clean and well-structured. The use of the cn utility for merging class names follows best practices.
Consider adding optional onPress support for interactive cards:
interface CardProps {
className?: string;
children: React.ReactNode;
+ onPress?: () => void;
}
- export function NeoCard({ className, children }: CardProps) {
+ export function NeoCard({ className, children, onPress }: CardProps) {
+ const Component = onPress ? TouchableOpacity : View;
return (
- <View className={cn("bg-neo-white border-2 border-neo-dark shadow-neo p-4", className)}>
+ <Component
+ onPress={onPress}
+ activeOpacity={onPress ? 0.8 : 1}
+ className={cn("bg-neo-white border-2 border-neo-dark shadow-neo p-4", className)}
+ >
{children}
- </View>
+ </Component>
);
}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In mobile/components/NeoCard.tsx around lines 9 to 15, add optional onPress
support so the card can be interactive: update the CardProps to accept an
optional onPress (and optional accessibility props like accessibilityLabel/role
if desired), and conditionally render a Pressable (or TouchableOpacity) when
onPress is provided instead of a plain View; ensure className merging with cn
remains, forward children and other props, and keep non-interactive behavior
unchanged when onPress is undefined.
| className={cn( | ||
| getBgColor(), | ||
| "px-6 py-4 border-2 border-neo-dark shadow-neo active:shadow-neo-sm active:translate-x-[2px] active:translate-y-[2px] transition-all", | ||
| className | ||
| )} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Active pseudo-class with transforms won't work as expected in React Native.
The active:shadow-neo-sm active:translate-x-[2px] active:translate-y-[2px] classes use CSS pseudo-classes and transforms, which don't work the same way in React Native. NativeWind may not fully support these active state transforms.
The current implementation already uses TouchableOpacity with activeOpacity={0.8} for visual feedback. The shadow and translate changes on active won't apply. Consider using the onPressIn/onPressOut events with state if you need custom press effects:
export function ThemedButton({ title, onPress, variant = 'primary', className, ...otherProps }: { title: string, onPress?: () => void, variant?: 'primary' | 'secondary' | 'accent', className?: string }) {
+ const [pressed, setPressed] = React.useState(false);
const getBgColor = () => {
switch(variant) {
case 'secondary': return "bg-neo-second";
case 'accent': return "bg-neo-accent";
default: return "bg-neo-main";
}
}
return (
<TouchableOpacity
activeOpacity={0.8}
onPress={onPress}
+ onPressIn={() => setPressed(true)}
+ onPressOut={() => setPressed(false)}
className={cn(
getBgColor(),
- "px-6 py-4 border-2 border-neo-dark shadow-neo active:shadow-neo-sm active:translate-x-[2px] active:translate-y-[2px] transition-all",
+ "px-6 py-4 border-2 border-neo-dark",
+ pressed ? "shadow-neo-sm" : "shadow-neo",
className
)}
+ style={pressed ? { transform: [{ translateX: 2 }, { translateY: 2 }] } : undefined}
{...otherProps}
>
<Text className="text-neo-white font-mono-bold text-lg text-center uppercase tracking-wider">{title}</Text>
</TouchableOpacity>
);
}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In mobile/components/Themed.tsx around lines 35 to 39, the tailwind classes
using the active: pseudo-class (active:shadow-neo-sm active:translate-x-[2px]
active:translate-y-[2px]) won't work reliably in React Native/NativeWind; remove
those active: classes and implement press-state styling instead: add a local
pressed boolean (useState) and toggle it in onPressIn/onPressOut handlers on the
TouchableOpacity, then conditionally include the shadow and transform styles (or
a reduced style alternative) when pressed; if you prefer simple feedback, keep
activeOpacity={0.8} and omit the transform/shadow changes entirely.
| )} | ||
| {...otherProps} | ||
| > | ||
| <Text className="text-neo-white font-mono-bold text-lg text-center uppercase tracking-wider">{title}</Text> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Consider accessibility implications of uppercase text.
The button uses uppercase class which forces all text to uppercase. While this matches the Neobrutalism design style, it may:
- Impact screen reader pronunciation
- Reduce readability for some users
- Lose semantic meaning of capitalization
Consider adding an accessibilityLabel prop to provide the original text to screen readers:
- export function ThemedButton({ title, onPress, variant = 'primary', className, ...otherProps }: { title: string, onPress?: () => void, variant?: 'primary' | 'secondary' | 'accent', className?: string }) {
+ export function ThemedButton({ title, onPress, variant = 'primary', className, accessibilityLabel, ...otherProps }: { title: string, onPress?: () => void, variant?: 'primary' | 'secondary' | 'accent', className?: string, accessibilityLabel?: string }) {
// ... getBgColor function
return (
<TouchableOpacity
activeOpacity={0.8}
onPress={onPress}
+ accessibilityLabel={accessibilityLabel || title}
className={cn(
getBgColor(),
"px-6 py-4 border-2 border-neo-dark shadow-neo active:shadow-neo-sm active:translate-x-[2px] active:translate-y-[2px] transition-all",
className
)}
{...otherProps}
>
<Text className="text-neo-white font-mono-bold text-lg text-center uppercase tracking-wider">{title}</Text>
</TouchableOpacity>
);
}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In mobile/components/Themed.tsx around line 42, the Text element is forced
uppercase which can harm screen-reader output and readability; add an
accessibilityLabel prop set to the original title (not the
transformed/uppercased string) so assistive tech reads the intended text, e.g.
accessibilityLabel={title}, and keep the visual uppercase styling only for
presentation; ensure the accessibilityLabel uses the original uncased value and
that the prop is passed directly on the Text element.
| export const COLORS = { | ||
| neo: { | ||
| main: '#8855ff', | ||
| second: '#ff9900', | ||
| accent: '#00cc88', | ||
| bg: '#f0f0f0', | ||
| dark: '#1a1a1a', | ||
| white: '#ffffff', | ||
| } | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
COLORS palette looks good; just watch duplication with Tailwind theme
The COLORS.neo values mirror the neo colors in mobile/tailwind.config.js, which is great for consistency. The only risk is future drift between the two definitions; consider a shared source of truth (or at least a brief comment in both places) so they’re intentionally kept in sync over time.
🤖 Prompt for AI Agents
In mobile/constants/colors.ts around lines 1 to 10 the COLORS.neo values
duplicate the neo palette defined in mobile/tailwind.config.js which risks
drift; either extract the palette into a single shared module (e.g., a
colors/theme file) that both tailwind.config.js and this constants file import,
or if extraction is not feasible add a clear sync comment in both files pointing
to the canonical source and instructions for updating both entries when colors
change.
| /** @type {import('tailwindcss').Config} */ | ||
| module.exports = { | ||
| // NOTE: Update this to include the paths to all of your component files. | ||
| content: ["./app/**/*.{js,jsx,ts,tsx}", "./components/**/*.{js,jsx,ts,tsx}"], | ||
| presets: [require("nativewind/preset")], | ||
| theme: { | ||
| extend: { | ||
| fontFamily: { | ||
| sans: ['Inter_400Regular', 'sans-serif'], | ||
| mono: ['SpaceGrotesk_400Regular', 'monospace'], | ||
| 'sans-bold': ['Inter_700Bold', 'sans-serif'], | ||
| 'mono-bold': ['SpaceGrotesk_700Bold', 'monospace'], | ||
| }, | ||
| colors: { | ||
| neo: { | ||
| main: '#8855ff', | ||
| second: '#ff9900', | ||
| accent: '#00cc88', | ||
| bg: '#f0f0f0', | ||
| dark: '#1a1a1a', | ||
| white: '#ffffff', | ||
| } | ||
| }, | ||
| boxShadow: { | ||
| 'neo': '4px 4px 0px 0px #1a1a1a', | ||
| 'neo-sm': '2px 2px 0px 0px #1a1a1a', | ||
| 'neo-lg': '6px 6px 0px 0px #1a1a1a', | ||
| } | ||
| }, | ||
| }, | ||
| plugins: [], | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if mobile/constants/colors.ts exists and inspect its contents
fd "colors\.ts" mobile/Repository: Devasy23/splitwiser
Length of output: 89
🏁 Script executed:
#!/bin/bash
# Get the size of the colors file to decide how to read it
if [ -f "mobile/constants/colors.ts" ]; then
wc -l mobile/constants/colors.ts
fiRepository: Devasy23/splitwiser
Length of output: 92
🌐 Web query:
NativeWind v4 Tailwind CSS setup configuration presets content
💡 Result:
Short answer + example:
- NativeWind v4 recommends adding the NativeWind preset and listing your RN/Expo/Next files in tailwind.config.js content. Content (and other keys like darkMode/prefix) in your project config replace the same key in a preset (Tailwind merges other fields per its rules). [1][2]
Example tailwind.config.js:
module.exports = {
/** @type {import('tailwindcss').Config} /
content: [
"./App.{js,jsx,ts,tsx}",
"./app/**/.{js,jsx,ts,tsx}",
"./components/**/*.{js,jsx,ts,tsx}"
],
presets: [require("nativewind/preset")],
theme: {
extend: {}
},
plugins: []
};
Notes:
- Ensure content includes every file that uses NativeWind classes so Tailwind can purge/generate utilities. [1]
- The presets option loads nativewind/preset as a base; Tailwind’s docs explain which config keys are replaced vs merged when using presets. [2]
Sources:
[1] NativeWind docs — Getting started / Installation.
[2] Tailwind CSS docs — Presets (merging behavior).
🏁 Script executed:
#!/bin/bash
# Read the colors.ts file to compare with tailwind.config.js
cat -n mobile/constants/colors.tsRepository: Devasy23/splitwiser
Length of output: 301
Tailwind + NativeWind configuration is correctly set up; color duplication is real but optional to centralize
The Tailwind config correctly uses nativewind/preset and the content globs follow NativeWind v4 recommendations. The extended fonts, Neo-brutalism colors, and box shadows are all properly configured. However, the neo color palette is duplicated verbatim in mobile/constants/colors.ts; to avoid drift, consider importing colors from the constants file or adding comments to both files noting they must stay synchronized.
🤖 Prompt for AI Agents
In mobile/tailwind.config.js around lines 1 to 32, the neo color palette is
duplicated from mobile/constants/colors.ts which can lead to drift; update the
Tailwind config to either import the color definitions from
mobile/constants/colors.ts (or export the Tailwind colors from that file and
require/import them here) so there is a single source of truth, or add a clear
comment in both files noting they must be kept in sync and include a
reference/path to the canonical file; ensure imports use CommonJS/ESM consistent
with the repo build config and that the shape matches Tailwind's expected color
object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (2)
mobile/app.json (2)
8-8:⚠️ Asset files referenced in app.json are still missing.Lines 8, 12, 28, and 36 reference asset files that do not exist in the
mobile/assets/directory:
./assets/icon.png(line 8)./assets/splash-icon.png(line 12)./assets/adaptive-icon.png(line 28)./assets/favicon.png(line 36)This will cause build and runtime failures. Create the
mobile/assets/directory and add all required image files before building.Also applies to: 12-12, 28-28, 36-36
39-53: Upgrade nativewind to v5.x for new architecture compatibility.The PR enables
newArchEnabled: true(line 10 and plugin lines 46, 49), but nativewind 4.1.23 does not have full new architecture (Fabric/TurboModules) support. This may cause build or runtime failures. Upgrade nativewind from 4.1.23 to 5.x inmobile/package.json, run installation, and apply any required v5 migration steps.Please verify the nativewind version in mobile/package.json and confirm it has been upgraded to v5.x.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
mobile/app.json(2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: Devasy23/splitwiser PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T18:33:22.581Z
Learning: Applies to {mobile/context/**/*.js,web/contexts/**/*.{ts,tsx}} : React Context should be used for state management of authentication and app-wide state in both mobile and web
Learnt from: CR
Repo: Devasy23/splitwiser PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T18:33:22.581Z
Learning: Applies to mobile/screens/**/*.js : Mobile screens should be created as components in `mobile/screens/` and registered in the navigation structure
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: rn-bundle-analysis
- GitHub Check: preview
🔇 Additional comments (2)
mobile/app.json (2)
32-32: VerifyREAD_MEDIA_IMAGESpermission is justified.Line 32 adds the
READ_MEDIA_IMAGESpermission for Android. Ensure this permission is required by the app's functionality (e.g., accessing the photo library for icon/image selection). If not used, remove it to follow the principle of least privilege.
10-10: I'll help you verify the review comment about redundantnewArchEnabledsettings. Let me start by examining the actual file and understanding the Expo configuration.
<function_calls>
shell
#!/bin/bashFind and read the app.json file
find . -name "app.json" -type f | head -20
</function_calls>Now let me search for Expo documentation on
newArchEnabledto verify the claims made in the review:
<function_calls>
Expo newArchEnabled documentation precedence plugin authority
</function_calls>
| "assetBundlePatterns": [ | ||
| "**/*" | ||
| ], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Overly broad asset bundle pattern may include unnecessary files.
The pattern "**/*" includes all files under the app directory in the asset bundle. This may inadvertently include build artifacts, cache files, or other non-asset files. Consider restricting the pattern to only necessary asset directories or file types, e.g., ["assets/**/*", "**/*.ttf"] (if using fonts and other specific assets).
🤖 Prompt for AI Agents
In mobile/app.json around lines 16 to 18, the assetBundlePatterns value is too
broad ("**/*") and may include build artifacts or caches; replace it with a
restricted pattern that only bundles real assets (for example restrict to asset
directories and specific extensions like "assets/**/*" and vectors/fonts such as
"**/*.ttf" or "**/*.png"), updating the array to list only the necessary folders
and file types so non-asset files are excluded from the bundle.
Summary by CodeRabbit
Release Notes
✏️ Tip: You can customize this high-level summary in your review settings.