Skip to content

chore: update package-lock.json with peer dependency flags#1

Merged
oryno-dev merged 5 commits into
mainfrom
update/package-lock-changes
Apr 25, 2026
Merged

chore: update package-lock.json with peer dependency flags#1
oryno-dev merged 5 commits into
mainfrom
update/package-lock-changes

Conversation

@oryno-dev
Copy link
Copy Markdown
Owner

Summary

  • Reorganize peer dependency flags in package-lock.json
  • Update after npm install to properly reflect peer dependencies

Changes

  • Modified package-lock.json with updated peer dependency flags for various @babel packages

Reorganize peer dependency flags in package-lock.json after npm install.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 25, 2026

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Local authentication, secure storage, and edge-to-edge display support added.
  • Updates

    • Core dependencies upgraded (React, React Native, TypeScript) and related libraries.
    • Android preview build configuration simplified.
    • Backup file-system operations improved for reliability.
    • App navigation now shows a loading screen until ready.
    • Icon rendering switched to native SVG primitives.
  • Chores

    • Keystore files ignored by version control.
    • Project Babel configuration added.

Walkthrough

Project-wide updates: dependency and tooling bumps, new Babel config, expanded Expo plugins and build config, added .gitignore pattern, awaited async file-system operations, SVG icons switched to react-native-svg, and navigation now shows a loading indicator until app state is ready.

Changes

Cohort / File(s) Summary
Repository config
/.gitignore, app.json, eas.json, babel.config.js
Added *.keystore to .gitignore. Expanded app.json expo.plugins (added expo-local-authentication, react-native-edge-to-edge, expo-secure-store and Android writeExternalStorage config). Added babel.config.js exporting babel-preset-expo. Simplified Android preview in eas.json to buildType: "apk" (removed explicit gradle command).
Dependencies
package.json
Updated core and dev deps: React 19.1.0, react-native 0.81.5, TypeScript ~5.9.2, added/updated Expo and RN libs (including expo-local-authentication, expo-secure-store, react-native-edge-to-edge, babel-preset-expo). Review for compatibility and lockfile updates.
Async file ops
src/utils/autoBackup.ts
File-system flows updated to await directory creation, file write, and file deletion to ensure sequencing in backup creation and cleanup. Error handling preserved.
Icon rendering (modals)
src/components/CheckInModal.tsx, src/components/CheckOutModal.tsx
Replaced inline/web-style SVG/View icon markup with react-native-svg primitives (Svg, Path, Circle) for modal icons; imports adjusted.
Navigation load gating
src/navigation/Navigation.tsx
Navigation now reads loading from useApp() and renders a full-screen centered ActivityIndicator while loading is true, deferring NavigationContainer and onboarding/setup logic; updated React Native imports.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title claims to update package-lock.json with peer dependency flags, but the changeset includes extensive code changes across 9 files (babel.config.js, app.json, eas.json, .gitignore, package.json, autoBackup.ts, CheckInModal.tsx, CheckOutModal.tsx, Navigation.tsx) implementing critical bug fixes and configuration updates unrelated to package-lock.json peer dependencies. Revise the title to accurately reflect the main changes, such as 'fix: address build, configuration, and runtime issues for Android EAS builds' or 'chore: implement babel config, update dependencies, fix SVG rendering, and add loading state'.
Description check ⚠️ Warning The PR description only mentions reorganizing peer dependency flags in package-lock.json, but the changeset includes significant functional changes (babel.config.js creation, plugin configurations, SVG component replacements, navigation loading state, async/await fixes) that are completely unaddressed in the description. Update the description to comprehensively document all changes: babel configuration, dependency updates, SVG rendering fixes, loading state implementation, async operations, and .gitignore additions.
✅ Passed checks (3 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@oryno-dev
Copy link
Copy Markdown
Owner Author

@coderabbitai analysis the entire project and check if there are a missing expo react native files , that might work properly on web but not that on Andriod apk files , and check if there any problems might block the build status on eas build and the overall experience on Android as apk , also dig deeper on some problem i am facing when I built the app as .apk file and faced some problems like "[ 11:32

Tue, 14 Apr

Unable to load script.

Make sure you're running Metro or that your bundle 'index.android.bundle' is packaged correctly for release.

The device must either be USB connected (with bundler set to "localhost:8081") or be on the same Wi-Fi network as your computer (with bundler set to your computer IP) to connect to Metro.

If you're using USB on a physical device, make sure you also run this command:

adb reverse tcp:8081 tcp:8081

jniLoadScript FromAssets

CatalystInstanceImpl.java

loadScriptFromAssets

CatalystInstanceImpl.java:245

loadScript

JSBundleLoader.kt:33

runJSBundle

CatalystInstanceImpl.java:274

createReactContext

ReactInstanceManager.java:1525

lambda$runCreateReactContextOnNewThread$2

ReactInstanceManager.java:1222

lambda$PrBhihCbbAFk4ZReAALGanVLCyc

null

run

D8$$SyntheticClass

run

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 25, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

I'll do a deep analysis of your project structure to identify all potential issues.


🏁 Script executed:

#!/bin/bash
echo "=== PROJECT ROOT STRUCTURE ==="
tree -L 2 --dirsfirst 2>/dev/null || ls -la

echo ""
echo "=== package.json ==="
cat package.json 2>/dev/null || echo "NOT FOUND"

echo ""
echo "=== app.json ==="
cat app.json 2>/dev/null || echo "NOT FOUND"

echo ""
echo "=== app.config.js ==="
cat app.config.js 2>/dev/null || echo "NOT FOUND"

echo ""
echo "=== app.config.ts ==="
cat app.config.ts 2>/dev/null || echo "NOT FOUND"

echo ""
echo "=== eas.json ==="
cat eas.json 2>/dev/null || echo "NOT FOUND"

echo ""
echo "=== metro.config.js ==="
cat metro.config.js 2>/dev/null || echo "NOT FOUND"

echo ""
echo "=== babel.config.js ==="
cat babel.config.js 2>/dev/null || echo "NOT FOUND"

echo ""
echo "=== index.js ==="
cat index.js 2>/dev/null || echo "NOT FOUND"

echo ""
echo "=== index.ts ==="
cat index.ts 2>/dev/null || echo "NOT FOUND"

echo ""
echo "=== tsconfig.json ==="
cat tsconfig.json 2>/dev/null || echo "NOT FOUND"

echo ""
echo "=== Check for expo-router entry ==="
fd -e js -e ts -e tsx "index" --max-depth 2 2>/dev/null

echo ""
echo "=== Android folder structure ==="
tree android/ -L 4 --dirsfirst 2>/dev/null || ls -la android/ 2>/dev/null || echo "NO ANDROID FOLDER"

echo ""
echo "=== android/app/build.gradle ==="
cat android/app/build.gradle 2>/dev/null || echo "NOT FOUND"

echo ""
echo "=== android/build.gradle ==="
cat android/build.gradle 2>/dev/null || echo "NOT FOUND"

echo ""
echo "=== Check src/app directory (expo-router) ==="
find . -path "*/app/_layout*" 2>/dev/null | head -20
find . -path "*/app/index*" 2>/dev/null | head -20

echo ""
echo "=== Check for web-specific imports ==="
rg "Platform.OS" --type-add 'rn:*.{js,jsx,ts,tsx}' --type rn -n 2>/dev/null | head -30

echo ""
echo "=== Check for window/document usage (web-only) ==="
rg "\bwindow\b|\bdocument\b" --type-add 'rn:*.{js,jsx,ts,tsx}' --type rn -n 2>/dev/null | grep -v "\.web\." | head -30

echo ""
echo "=== expo-router check ==="
cat node_modules/expo-router/package.json 2>/dev/null | python3 -c "import json,sys; d=json.load(sys.stdin); print('expo-router version:', d.get('version','?'))" 2>/dev/null || echo "expo-router not found"

echo ""
echo "=== Check for missing native modules ==="
cat package.json | python3 -c "
import json, sys
d = json.load(sys.stdin)
deps = list(d.get('dependencies', {}).keys()) + list(d.get('devDependencies', {}).keys())
native_risky = [p for p in deps if any(k in p for k in ['native', 'camera', 'maps', 'gesture', 'reanimated', 'skia', 'sqlite', 'filesystem', 'notifications', 'blur', 'linear-gradient'])]
print('Potentially native-only packages:', native_risky)
print('All deps:', json.dumps(list(d.get('dependencies', {}).keys()), indent=2))
" 2>/dev/null

echo ""
echo "=== Check expo.main in package.json ==="
cat package.json | python3 -c "import json,sys; d=json.load(sys.stdin); print('main:', d.get('main','NOT SET')); print('expo.main:', d.get('expo', {}).get('main','NOT SET'))" 2>/dev/null

Length of output: 11348


🏁 Script executed:

#!/bin/bash
echo "=== App.js (full content) ==="
cat App.js 2>/dev/null || echo "NOT FOUND"

echo ""
echo "=== src/context/LanguageContext.tsx - window.location.reload lines ==="
grep -n "window.location" src/context/LanguageContext.tsx 2>/dev/null

echo ""
echo "=== src/screens/OnboardingScreen.tsx - window.location lines ==="
grep -n "window.location" src/screens/OnboardingScreen.tsx 2>/dev/null

echo ""
echo "=== src/context/AppContext.tsx (relevant native/web sections) ==="
grep -n "localStorage\|sessionStorage\|window\.\|document\." src/context/AppContext.tsx 2>/dev/null

echo ""
echo "=== Check if babel.config.js or .babelrc exists ==="
fd "babel.config" --max-depth 3 2>/dev/null
fd ".babelrc" --max-depth 3 2>/dev/null

echo ""
echo "=== Check devDependencies in package.json ==="
cat package.json | python3 -c "import json,sys; d=json.load(sys.stdin); print(json.dumps(d.get('devDependencies', {}), indent=2))" 2>/dev/null

echo ""
echo "=== Check if expo-local-authentication is properly declared in app.json plugins ==="
cat app.json | python3 -c "import json,sys; d=json.load(sys.stdin); print(json.dumps(d.get('expo',{}).get('plugins',[]), indent=2))" 2>/dev/null

echo ""
echo "=== Check update.json ==="
cat update.json 2>/dev/null || echo "NOT FOUND"

echo ""
echo "=== Check if there's a .expo folder ==="
ls -la .expo/ 2>/dev/null || echo "NO .expo FOLDER"

echo ""
echo "=== Check src/navigation/Navigation.tsx ==="
cat src/navigation/Navigation.tsx 2>/dev/null || echo "NOT FOUND"

echo ""
echo "=== eas.json full analysis ==="
cat eas.json

echo ""
echo "=== Check AppContext.tsx localStorage usage ==="
grep -n "localStorage\|AsyncStorage\|SecureStore" src/context/AppContext.tsx 2>/dev/null | head -40

echo ""
echo "=== Check for any hardcoded localhost URLs ==="
rg "localhost|127\.0\.0\.1" --type-add 'src:*.{js,jsx,ts,tsx}' --type src -n 2>/dev/null | grep -v node_modules | grep -v plans/ | head -30

echo ""
echo "=== Check react-native-chart-kit SVG dependency issue ==="
cat node_modules/react-native-chart-kit/package.json 2>/dev/null | python3 -c "import json,sys; d=json.load(sys.stdin); print('peerDeps:', json.dumps(d.get('peerDependencies',{}), indent=2))" 2>/dev/null

echo ""
echo "=== Check expo-local-authentication native plugin ==="
cat node_modules/expo-local-authentication/package.json 2>/dev/null | python3 -c "import json,sys; d=json.load(sys.stdin); print('version:', d.get('version','?')); print('plugin:', d.get('main','?'))" 2>/dev/null || echo "NOT FOUND"

echo ""
echo "=== Check src/utils/autoBackup.ts (full) ==="
cat src/utils/autoBackup.ts 2>/dev/null | head -100

Length of output: 18194


@oryno-dev Here is a comprehensive analysis of your project. I found the exact root cause of your "Unable to load script" error, plus several other Android-blocking issues.


🔴 #1 — ROOT CAUSE: eas.json Preview Profile Uses assembleDebug

This is 100% the cause of your error.

// eas.json (CURRENT — BROKEN)
"preview": {
  "android": {
    "buildType": "apk",
    "gradleCommand": ":app:assembleDebug"  // ← THE CULPRIT
  }
}

A debug APK (assembleDebug) does not bundle the JavaScript into the APK file. Instead, it tries to connect to a live Metro bundler over the network/USB. When you run it on a physical device without Metro running, React Native panics with exactly the error you saw:

"Unable to load script. Make sure you're running Metro or that your bundle index.android.bundle is packaged correctly for release."

Fix — change eas.json to:

{
  "cli": {
    "version": ">= 7.0.0"
  },
  "build": {
    "preview": {
      "android": {
        "buildType": "apk"
      }
    },
    "production": {
      "android": {
        "buildType": "apk"
      }
    }
  }
}

Removing gradleCommand makes EAS use assembleRelease, which embeds the JS bundle into the APK so it works standalone on any device.


🔴 #2 — Missing babel.config.js (Build Blocker)

There is no babel.config.js in your project. This is required by Expo to transpile your TypeScript/JSX code before bundling. Without it, EAS may fail to build or produce a broken bundle.

Fix — create babel.config.js at the project root:

module.exports = function (api) {
  api.cache(true);
  return {
    presets: ['babel-preset-expo'],
  };
};

🔴 #3 — Empty devDependencies (Missing babel-preset-expo)

Your package.json has:

"devDependencies": {}

babel-preset-expo is completely missing from your declared dependencies. EAS will try to install it implicitly, but it should be explicitly declared.

Fix — run:

npm install --save-dev babel-preset-expo

🟠 #4expo-local-authentication Not Registered in Plugins

You use biometric/fingerprint authentication via expo-local-authentication, but it is not declared in app.json plugins. This means Android will not receive the required USE_BIOMETRIC and USE_FINGERPRINT manifest permissions, and the feature will silently fail on APK.

Fix — update app.json plugins:

"plugins": [
  [
    "expo-file-system",
    { "android": { "writeExternalStorage": true } }
  ],
  "expo-local-authentication"
]

🟠 #5edgeToEdgeEnabled: true Without the Required Package

Your app.json has:

"android": {
  "edgeToEdgeEnabled": true,
  ...
}

This feature, introduced in Expo SDK 52+, requires react-native-edge-to-edge to manage system bar insets properly. Without it, your UI may be covered by the status bar or navigation bar on Android 15 devices.

Fix — install the package:

npx expo install react-native-edge-to-edge

And add to app.json plugins:

"plugins": [
  "react-native-edge-to-edge",
  ...
]

🟠 #6expo-file-system New API Usage in autoBackup.ts

autoBackup.ts uses the new Next API:

import { Paths, File, Directory } from 'expo-file-system';

This API (Paths, File, Directory) was introduced in expo-file-system v16+ (Next API). Your installed version is ~19.0.21, which supports it. However, you must ensure the plugin is properly configured in app.json (which it is, with writeExternalStorage). This should work on Android, but test it with a release APK since the Next API behavior can differ from the legacy API.


🟡 #7 — Keystore File Committed to Repository (Security Risk)

attery.keystore     ← committed to git repo

Your signing keystore (attery.keystore) is committed directly to the repository. If this repo is ever made public, anyone could re-sign and distribute your app as you.

Fix: Add it to .gitignore and store it securely (EAS can manage keystores for you via eas credentials):

# .gitignore
*.keystore
*.aab
*.apk
*.apks

🟡 #8 — Web-Only Code Properly Guarded ✅

The following looked suspicious but are correctly guarded by Platform.OS === 'web' checks — these will NOT crash Android:

File Line Code Guard
AppContext.tsx 95, 113 localStorage.getItem/setItem ✅ Line 93: if (Platform.OS === 'web')
AppContext.tsx 650–651 localStorage.removeItem ✅ Line 649: if (Platform.OS === 'web')
LanguageContext.tsx 779 window.location.reload() ✅ Line 776: if (Platform.OS === 'web' && !skipReload)
OnboardingScreen.tsx 268 window.location.reload() ✅ Line 266: if (Platform.OS === 'web' && ...)
autoBackup.ts ~79 localStorage.setItem ✅ Inside Platform.OS === 'web' block

Summary Checklist

# Issue Severity Fix
1 assembleDebug in eas.json preview 🔴 Critical Remove gradleCommand, use buildType: "apk" only
2 Missing babel.config.js 🔴 Critical Create with babel-preset-expo preset
3 Missing babel-preset-expo in devDeps 🔴 Critical npm install --save-dev babel-preset-expo
4 expo-local-authentication not in plugins 🟠 High Add to app.json plugins
5 edgeToEdgeEnabled: true without package 🟠 High Install react-native-edge-to-edge
6 expo-file-system Next API 🟠 Medium Test on release APK, monitor for issues
7 Keystore committed to repo 🟡 Security Add to .gitignore, use eas credentials

Start with fix #1 — it will immediately solve the "Unable to load script" error. Then apply fixes #2 and #3 to ensure a clean EAS build.

Ensure backup files are deleted with await to prevent race conditions and ensure
deletions complete before proceeding. Also add babel.config.js to include Babel
configuration for the project build/transpilation.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.gitignore:
- Line 20: The repo added *.keystore to .gitignore but the committed keystore
attery.keystore remains tracked and must be removed from tracking and treated as
compromised; run git rm --cached attery.keystore and commit (e.g., "chore: stop
tracking committed keystore") to untrack it, then rotate the signing key and
migrate to EAS-managed credentials to mitigate exposure.

In `@app.json`:
- Line 43: The plugin entry for "expo-local-authentication" should be changed
from a bare string to an object that supplies a custom iOS permission message
via the faceIDPermission key; update the app.json plugins array so the
"expo-local-authentication" entry is an object with {"faceIDPermission":"Allow
$(PRODUCT_NAME) to use Face ID to authenticate."} (or an app-specific message)
to set NSFaceIDUsageDescription explicitly and provide better context for users.

In `@eas.json`:
- Around line 7-9: The preview EAS profile is using a debug Gradle command that
produced an APK expecting Metro (causing "Unable to load script:
index.android.bundle"); remove any custom gradleCommand like
":app:assembleDebug" from the preview profile so EAS falls back to the default
release-style assemble (keep "android": { "buildType": "apk" }) and the JS will
be bundled into the APK at build time; additionally, explicitly add
"distribution": "internal" to the preview profile to document intent, and if you
need a debug/dev APK in future create a separate "development" profile with
"developmentClient": true instead of using assembleDebug in preview.

In `@package.json`:
- Around line 31-36: Two dependencies use caret ranges but should use Expo's
tilde pinning: change "react-native-edge-to-edge" from ^1.8.1 to ~1.8.1 and
"react-native-web" from ^0.21.0 to ~0.21.0 in package.json; update the version
strings for those package keys so they match the tilde convention used by other
Expo-validated packages and re-run npx expo install --check to verify
compatibility.

In `@src/utils/autoBackup.ts`:
- Around line 26-28: Remove the misleading awaits on synchronous object-methods:
drop the await when calling backupDir.create() (Directory.create), when calling
file.write(...) (File.write) and when calling file.delete() (File.delete) since
these are synchronous in expo-file-system 19.0.21; alternatively, if you truly
need async behavior, replace those calls with the legacy async APIs
(FileSystem.makeDirectoryAsync, FileSystem.writeAsStringAsync,
FileSystem.deleteAsync) and await those instead.
🪄 Autofix (Beta)

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: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 46ddf338-d679-4668-a297-743112cfc993

📥 Commits

Reviewing files that changed from the base of the PR and between 4173fc8 and 46fb5d5.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (6)
  • .gitignore
  • app.json
  • babel.config.js
  • eas.json
  • package.json
  • src/utils/autoBackup.ts

Comment thread .gitignore
*.p12
*.key
*.mobileprovision
*.keystore
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Adding *.keystore is good, but the already-tracked attery.keystore needs to be untracked.

Per the prior diagnostic comment in this PR, a keystore file (attery.keystore) was committed to the repository. Adding the pattern to .gitignore does not remove the file from Git history or stop tracking the existing copy — the committed keystore remains a credential exposure. After merging, run:

git rm --cached attery.keystore
git commit -m "chore: stop tracking committed keystore"

Strongly recommended to also rotate the signing key and migrate to EAS-managed credentials, since the previously committed keystore must be considered compromised.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.gitignore at line 20, The repo added *.keystore to .gitignore but the
committed keystore attery.keystore remains tracked and must be removed from
tracking and treated as compromised; run git rm --cached attery.keystore and
commit (e.g., "chore: stop tracking committed keystore") to untrack it, then
rotate the signing key and migrate to EAS-managed credentials to mitigate
exposure.

Comment thread app.json
}
}
],
"expo-local-authentication",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

🧩 Analysis chain

🌐 Web query:

In Expo SDK 54, what config options does the expo-local-authentication config plugin accept, and is faceIDPermission required to set NSFaceIDUsageDescription?

💡 Result:

In Expo SDK 54, the expo-local-authentication config plugin accepts one option: faceIDPermission, which is a string for iOS that sets the NSFaceIDUsageDescription permission message. Its default value is "Allow $(PRODUCT_NAME) to use Face ID.". The faceIDPermission option is not strictly required because it has a default value. However, Apple requires the NSFaceIDUsageDescription key in Info.plist for apps using Face ID; without it (or if customized to an empty string), the app may crash or fall back to passcode authentication on iOS devices with Face ID.

Citations:


🏁 Script executed:

# Find and examine AppContext.tsx to verify LocalAuthentication.authenticateAsync usage
find . -name "AppContext.tsx" -type f

Repository: oryno-dev/Attenary

Length of output: 91


🏁 Script executed:

# Look at app.json to see the actual plugin configuration
find . -name "app.json" -type f

Repository: oryno-dev/Attenary

Length of output: 73


🏁 Script executed:

# Check AppContext.tsx around lines 340-350 for LocalAuthentication usage
cat -n ./src/context/AppContext.tsx | sed -n '330,360p'

Repository: oryno-dev/Attenary

Length of output: 1069


🏁 Script executed:

# Check app.json for the plugin configuration around line 43
cat -n ./app.json | sed -n '35,55p'

Repository: oryno-dev/Attenary

Length of output: 592


Consider passing custom faceIDPermission config to expo-local-authentication for a more contextual message.

The plugin is currently configured as a bare string. While this will automatically set NSFaceIDUsageDescription to its default value ("Allow $(PRODUCT_NAME) to use Face ID."), providing an explicit config with a message specific to your app's authentication context (e.g., "Allow $(PRODUCT_NAME) to use Face ID to authenticate.") is a recommended best practice and improves the user experience on iOS.

♻️ Suggested expansion
-      "expo-local-authentication",
+      [
+        "expo-local-authentication",
+        {
+          "faceIDPermission": "Allow $(PRODUCT_NAME) to use Face ID to authenticate."
+        }
+      ],
       "react-native-edge-to-edge",
       "expo-secure-store"
📝 Committable suggestion

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

Suggested change
"expo-local-authentication",
[
"expo-local-authentication",
{
"faceIDPermission": "Allow $(PRODUCT_NAME) to use Face ID to authenticate."
}
],
"react-native-edge-to-edge",
"expo-secure-store"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app.json` at line 43, The plugin entry for "expo-local-authentication" should
be changed from a bare string to an object that supplies a custom iOS permission
message via the faceIDPermission key; update the app.json plugins array so the
"expo-local-authentication" entry is an object with {"faceIDPermission":"Allow
$(PRODUCT_NAME) to use Face ID to authenticate."} (or an app-specific message)
to set NSFaceIDUsageDescription explicitly and provide better context for users.

Comment thread eas.json
Comment on lines 7 to 9
"android": {
"buildType": "apk",
"gradleCommand": ":app:assembleDebug"
"buildType": "apk"
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Correct fix — this resolves the "Unable to load script: index.android.bundle" error.

Removing gradleCommand: ":app:assembleDebug" lets EAS fall back to its default release-style assemble for the preview profile, which bundles the JS into the APK at build time. The previous debug command produced an APK that expected Metro at runtime, which is exactly what the stacktrace in CatalystInstanceImpl was reporting.

A couple of small follow-ups worth considering, not blocking:

  • Add "distribution": "internal" explicitly so the intent of preview is documented and eas build won't surprise contributors who default to store distribution.
  • If you ever do want a debug APK in the future for native debugging, create a separate "development" profile with "developmentClient": true rather than reaching for assembleDebug again.
♻️ Optional clarification
     "preview": {
+      "distribution": "internal",
       "android": {
         "buildType": "apk"
       }
     },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@eas.json` around lines 7 - 9, The preview EAS profile is using a debug Gradle
command that produced an APK expecting Metro (causing "Unable to load script:
index.android.bundle"); remove any custom gradleCommand like
":app:assembleDebug" from the preview profile so EAS falls back to the default
release-style assemble (keep "android": { "buildType": "apk" }) and the JS will
be bundled into the APK at build time; additionally, explicitly add
"distribution": "internal" to the preview profile to document intent, and if you
need a debug/dev APK in future create a separate "development" profile with
"developmentClient": true instead of using assembleDebug in preview.

Comment thread package.json
Comment on lines +31 to +36
"react-native-edge-to-edge": "^1.8.1",
"react-native-gesture-handler": "~2.28.0",
"react-native-safe-area-context": "~5.6.0",
"react-native-screens": "~4.16.0",
"react-native-svg": "15.12.1",
"react-native-web": "^0.21.0",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Use Expo's tilde pinning for react-native-edge-to-edge and react-native-web.

react-native-edge-to-edge (^1.8.1) and react-native-web (^0.21.0) currently use caret ranges, while every other Expo-validated package in this manifest uses tilde (~). Expo SDK 54 only validates a specific set of versions for native-bridged packages; allowing minor-version drift on these can cause incompatibilities surfacing only on the Android APK build (e.g., new-arch interop in react-native-edge-to-edge). Aligning with Expo's pinning convention also keeps npx expo install --check quiet.

♻️ Proposed change
-    "react-native-edge-to-edge": "^1.8.1",
+    "react-native-edge-to-edge": "~1.8.1",
@@
-    "react-native-web": "^0.21.0",
+    "react-native-web": "~0.21.0",
📝 Committable suggestion

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

Suggested change
"react-native-edge-to-edge": "^1.8.1",
"react-native-gesture-handler": "~2.28.0",
"react-native-safe-area-context": "~5.6.0",
"react-native-screens": "~4.16.0",
"react-native-svg": "15.12.1",
"react-native-web": "^0.21.0",
"react-native-edge-to-edge": "~1.8.1",
"react-native-gesture-handler": "~2.28.0",
"react-native-safe-area-context": "~5.6.0",
"react-native-screens": "~4.16.0",
"react-native-svg": "15.12.1",
"react-native-web": "~0.21.0",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package.json` around lines 31 - 36, Two dependencies use caret ranges but
should use Expo's tilde pinning: change "react-native-edge-to-edge" from ^1.8.1
to ~1.8.1 and "react-native-web" from ^0.21.0 to ~0.21.0 in package.json; update
the version strings for those package keys so they match the tilde convention
used by other Expo-validated packages and re-run npx expo install --check to
verify compatibility.

Comment thread src/utils/autoBackup.ts
Comment on lines +26 to +28
if (!backupDir.exists) {
await backupDir.create();
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect the installed expo-file-system 19.0.21 type definitions to determine
# whether create / write / delete on Directory and File return Promises.
fd -t f -e d.ts . node_modules/expo-file-system 2>/dev/null | head -50
fd -t f 'next' node_modules/expo-file-system 2>/dev/null
rg -nP -C2 '\b(create|write|delete)\s*\(' node_modules/expo-file-system 2>/dev/null --type=ts | head -80

Repository: oryno-dev/Attenary

Length of output: 44


🌐 Web query:

In expo-file-system 19.0.21 (Expo SDK 54 stable next API), do Directory.create(), File.write(), and File.delete() return Promises, or are they synchronous? What are the recommended async variants?

💡 Result:

In expo-file-system 19.0.21 (the stable next API in Expo SDK 54), Directory.create(options?), File.write(content), and File.delete are all synchronous methods that return void. They perform operations immediately and throw errors synchronously if they fail (e.g., if the path doesn't exist, no permissions, or already exists without idempotent/overwrite options). Examples from docs and types: - Directory.create(options?: DirectoryCreateOptions): void - File.write(content: string | Uint8Array): void - Directory/File.delete: void They have no Promise return; they are sync by design for the new object-oriented API. Recommended async variants (where available): - For copy/move: Use the async copy and move methods (return Promise), or legacy async functions like FileSystem.makeDirectoryAsync, FileSystem.writeAsStringAsync, FileSystem.deleteAsync. - For read operations: Use async methods like file.text, file.bytes (return Promise), with Sync counterparts like textSync, bytesSync. - For creation/writing/deletion specifically, no direct async versions in the new API; use try-catch for error handling, or fall back to legacy async API from expo-file-system/legacy (e.g., makeDirectoryAsync, writeAsStringAsync, deleteAsync) if async behavior is needed. The new API prioritizes sync for simplicity/performance, with some methods recently made async (e.g., copy/move in recent updates).

Citations:


Remove unnecessary await statements for synchronous methods.

Directory.create() (line 27), File.write() (line 104), and File.delete() (line 146) are synchronous methods that return void in expo-file-system 19.0.21. The added await statements are harmless—they resolve immediately without waiting—but they misrepresent the API design and create confusion.

Either remove the await keywords or, if actual async behavior is needed, switch to the legacy async API (FileSystem.makeDirectoryAsync, FileSystem.writeAsStringAsync, FileSystem.deleteAsync). The new object-oriented API does not provide async variants for create/write/delete operations.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/autoBackup.ts` around lines 26 - 28, Remove the misleading awaits
on synchronous object-methods: drop the await when calling backupDir.create()
(Directory.create), when calling file.write(...) (File.write) and when calling
file.delete() (File.delete) since these are synchronous in expo-file-system
19.0.21; alternatively, if you truly need async behavior, replace those calls
with the legacy async APIs (FileSystem.makeDirectoryAsync,
FileSystem.writeAsStringAsync, FileSystem.deleteAsync) and await those instead.

@oryno-dev
Copy link
Copy Markdown
Owner Author

@coderabbitai , App Crashed

View config getter callback for component 'path' must be a function (received "undefined"). Make sure to start component names with a capital letter.

Envar Laint Violation View.config getter callback for component Must be function Treceived undetinet Make sure to start component nases with a capital letter

at Invariant laddress at indes android bundle:1:154374)

at get faddress at index androad bundle 1:2329117

of completelork (address at index android bundle 5123001

at completelmatofwork (addruss at andex android bundle 1:530524)

at performünitofwork (address at Index android bundle: 529800) at workloopSync (address at toder android bundle: 1 528787)

At render Root Sync (address at indes, android bundle 1.5280141

at por formerünkoot (address at index anarojo bundle: 1 526040)

at purforaSyn KOH (address at iles.androll bundle 1:478386)

anushSyntMor katrossRoots imp (ddress at indiex android.bohole:|:477293)

at batchediupdatestani (södress at index android.bundle: 1-543000)

at hatchedupdates81 (address at indita android bundle:114689101 at receiveRoptNodeIDEvent (address at Index android tongle: 1 469198)

at recaleetouches (address at index android bundle 539667

at apply (native)

at callFunction address at andex, android, bundle 1:1638851

at anonymous (address-at index andraxd bundle t:162328)

guard (address at index android bundle 1 1632091

Error

An error occurred. The app will attempt to restart.

OK

Attenary issues2
First issues that the on-boarding screens is being initialized each time I open the app

second issues when I click check in the app fully crash , reboot on onboarding

found where why that happening, and investigate on any another issues in the entire project

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 25, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "=== Check react-native-svg installation and version ==="
cat node_modules/react-native-svg/package.json 2>/dev/null | python3 -c "import json,sys; d=json.load(sys.stdin); print('react-native-svg version:', d.get('version','?'))" 2>/dev/null || echo "react-native-svg NOT FOUND in node_modules"

echo ""
echo "=== Check if react-native-svg is in package.json ==="
cat package.json | python3 -c "import json,sys; d=json.load(sys.stdin); deps={**d.get('dependencies',{}),**d.get('devDependencies',{})}; print('react-native-svg:', deps.get('react-native-svg','NOT DECLARED'))" 2>/dev/null

echo ""
echo "=== Search for lowercase <path component usage (SVG crash cause) ==="
rg '<[Pp]ath\b' --type-add 'rn:*.{js,jsx,ts,tsx}' --type rn -n 2>/dev/null | grep -v node_modules | head -40

echo ""
echo "=== Search for SVG imports ==="
rg "from 'react-native-svg'|from \"react-native-svg\"|require.*react-native-svg" --type-add 'rn:*.{js,jsx,ts,tsx}' --type rn -n 2>/dev/null | grep -v node_modules | head -30

echo ""
echo "=== Search for react-native-chart-kit usage ==="
rg "react-native-chart-kit|LineChart|BarChart|PieChart|ProgressChart" --type-add 'rn:*.{js,jsx,ts,tsx}' --type rn -n 2>/dev/null | grep -v node_modules | head -30

echo ""
echo "=== Check app.json plugins for react-native-svg ==="
cat app.json | python3 -c "import json,sys; d=json.load(sys.stdin); print(json.dumps(d.get('expo',{}).get('plugins',[]), indent=2))" 2>/dev/null

echo ""
echo "=== OnboardingScreen - look for completion flag / AsyncStorage / SecureStore ==="
grep -n "AsyncStorage\|SecureStore\|setItem\|onboard\|hasOnboarded\|isFirstLaunch\|firstLaunch\|completed\|hasCompleted" src/screens/OnboardingScreen.tsx 2>/dev/null | head -50

echo ""
echo "=== AppContext - onboarding flag logic ==="
grep -n "onboard\|hasOnboarded\|isFirstLaunch\|firstLaunch\|hasCompleted\|AsyncStorage\|SecureStore" src/context/AppContext.tsx 2>/dev/null | head -60

echo ""
echo "=== Check button handler - search for 'check' related handlers ==="
rg "onCheck\|handleCheck\|checkIn\|check[_\s]?[Ii]n\|'check'\|\"check\"\|onPress.*check\|check.*onPress" --type-add 'rn:*.{js,jsx,ts,tsx}' --type rn -n 2>/dev/null | grep -v node_modules | head -40

echo ""
echo "=== Full list of screens ==="
fd -e tsx -e ts -e jsx -e js . src/screens/ 2>/dev/null

echo ""
echo "=== Full list of src files ==="
tree src/ -L 3 2>/dev/null || find src/ -type f -name "*.tsx" -o -name "*.ts" | head -50

echo ""
echo "=== Check if react-native-svg is configured in metro.config.js ==="
cat metro.config.js 2>/dev/null || echo "NOT FOUND"

echo ""
echo "=== Check for transformer/svg config ==="
rg "svgr\|svg\|transformer" metro.config.js 2>/dev/null || echo "No SVG transformer config found"

echo ""
echo "=== CheckIn or Check screen file ==="
fd -e tsx -e ts "check" src/ 2>/dev/null
cat src/screens/CheckInScreen.tsx 2>/dev/null | head -80 || echo "CheckInScreen NOT FOUND"
cat src/screens/CheckIn.tsx 2>/dev/null | head -80 || echo "CheckIn.tsx NOT FOUND"

Length of output: 14786


🏁 Script executed:

#!/bin/bash
echo "=== CheckInModal.tsx (full) ==="
cat src/components/CheckInModal.tsx 2>/dev/null

echo ""
echo "=== CheckOutModal.tsx (full) ==="
cat src/components/CheckOutModal.tsx 2>/dev/null

echo ""
echo "=== Navigation.tsx (full) ==="
cat src/navigation/Navigation.tsx 2>/dev/null

echo ""
echo "=== AppContext - loadData function (lines 90-220) ==="
sed -n '90,220p' src/context/AppContext.tsx 2>/dev/null

echo ""
echo "=== AppContext - saveData / completeOnboarding (lines 540-640) ==="
sed -n '540,640p' src/context/AppContext.tsx 2>/dev/null

echo ""
echo "=== Check if react-native-svg is properly installed ==="
ls node_modules/ | grep svg 2>/dev/null || echo "No svg packages in node_modules"

echo ""
echo "=== Check package-lock.json for react-native-svg ==="
python3 -c "
import json
with open('package-lock.json') as f:
    d = json.load(f)
pkgs = d.get('packages', {})
svg_keys = [k for k in pkgs if 'react-native-svg' in k]
for k in svg_keys:
    print(k, '->', pkgs[k].get('version','?'), '| resolved:', pkgs[k].get('resolved','?')[:60])
" 2>/dev/null || echo "Could not parse package-lock.json"

echo ""
echo "=== TimeClockScreen - check button / check-in action ==="
grep -n "check\|Check\|handlePress\|onPress\|clock\|Clock\|start\|Start" src/screens/TimeClockScreen.tsx 2>/dev/null | head -40

echo ""
echo "=== Check ProgressTracker.tsx SVG/chart-kit section ==="
head -20 src/components/ProgressTracker.tsx 2>/dev/null

echo ""
echo "=== Check if react-native-svg is a peer dep of react-native-chart-kit ==="
cat node_modules/react-native-chart-kit/package.json 2>/dev/null | python3 -c "import json,sys; d=json.load(sys.stdin); print(json.dumps(d.get('peerDependencies',{}), indent=2))" 2>/dev/null || echo "react-native-chart-kit not in node_modules either"

echo ""
echo "=== Verify expo install would find react-native-svg ==="
cat package.json | python3 -c "
import json,sys
d = json.load(sys.stdin)
deps = d.get('dependencies',{})
print('react-native-svg in deps:', deps.get('react-native-svg','NOT FOUND'))
print('react-native-chart-kit in deps:', deps.get('react-native-chart-kit','NOT FOUND'))
" 2>/dev/null

Length of output: 33354


@oryno-dev I've found both root causes — they are completely different bugs. Here's the full breakdown:


🔴 Issue #1 — App Crashes on "Check In" Button

Root Cause: Raw HTML SVG elements in CheckInModal.tsx and CheckOutModal.tsx

Both modal files define icon components using raw HTML <svg> and <path> tags — those only exist in browsers, not in React Native. The moment the modal opens, React Native tries to render a <path> element, can't find its native config, and crashes with exactly your error:

"View config getter callback for component 'path' must be a function (received 'undefined')"

In CheckInModal.tsx:

// ❌ BROKEN — raw HTML SVG
const UserIcon = ({ color = '#94a3b8', size = 24 }) => (
  <View style={{ width: size, height: size }}>
    <svg viewBox="0 0 24 24" fill="none">   {/* ← HTML element, not RN */}
      <path d="M20 21v-2a8 8 0 1 0-16 0v2" stroke={color} ... />
      <circle cx="12" cy="7" r="4" stroke={color} ... />
    </svg>
  </View>
);

In CheckOutModal.tsx:

// ❌ BROKEN — same problem
const WarningIcon = ({ color = '#94a3b8', size = 24 }) => (
  <View style={{ width: size, height: size }}>
    <svg viewBox="0 0 24 24" fill="none">
      <path d="M12 9v4M12 17h.01M10.29..." stroke={color} ... />
    </svg>
  </View>
);

Fix — replace raw HTML with react-native-svg components (same pattern used correctly in all your other screens):

src/components/CheckInModal.tsx — replace the icon definitions at the top:

import Svg, { Path, Circle } from 'react-native-svg';

// ✅ FIXED
const UserIcon = ({ color = '#94a3b8', size = 24 }) => (
  <Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
    <Path
      d="M20 21v-2a8 8 0 1 0-16 0v2"
      stroke={color}
      strokeWidth="2"
      strokeLinecap="round"
      strokeLinejoin="round"
    />
    <Circle cx="12" cy="7" r="4" stroke={color} strokeWidth="2" />
  </Svg>
);

const CheckIcon = ({ color = '#22c55e', size = 24 }) => (
  <Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
    <Path
      d="M20 6L9 17l-5-5"
      stroke={color}
      strokeWidth="2"
      strokeLinecap="round"
      strokeLinejoin="round"
    />
  </Svg>
);

src/components/CheckOutModal.tsx — replace the icon definitions at the top:

import Svg, { Path } from 'react-native-svg';

// ✅ FIXED
const WarningIcon = ({ color = '#94a3b8', size = 24 }) => (
  <Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
    <Path
      d="M12 9v4M12 17h.01M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"
      stroke={color}
      strokeWidth="2"
      strokeLinecap="round"
      strokeLinejoin="round"
    />
  </Svg>
);

const DocumentIcon = ({ color = '#94a3b8', size = 24 }) => (
  <Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
    <Path
      d="M4 4h7l4 4v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z"
      stroke={color}
      strokeWidth="2"
      strokeLinecap="round"
      strokeLinejoin="round"
    />
    <Path d="M10 9h4M10 13h4M10 17h2" stroke={color} strokeWidth="2" strokeLinecap="round" />
  </Svg>
);

🔴 Issue #2 — Onboarding Shows Every App Launch

Root Cause: Navigation renders before AsyncStorage finishes loading

Here is the exact sequence that causes the bug:

1. App starts → appData.onboardingCompleted = false (default)
2. Navigation.tsx renders:
   → useState(!appData.onboardingCompleted) = useState(true)  ← "show onboarding"
3. Stack Navigator MOUNTS with initialRouteName = 'Onboarding'  ← locked in
4. loadData() finishes → appData.onboardingCompleted = true
5. useEffect fires → setShowOnboarding(false)
6. BUT the navigator is already on the 'Onboarding' screen
   initialRouteName doesn't change after mount → user sees onboarding again

Fix — gate Navigation rendering on the loading flag from AppContext:

In src/navigation/Navigation.tsx, add this guard at the top of the Navigation component:

import { View, ActivityIndicator } from 'react-native'; // already imported
import { useApp } from '../context/AppContext';

const Navigation = () => {
  const { appData, loading } = useApp(); // add `loading`
  // ...existing state...

  // ✅ ADD THIS — wait for AsyncStorage to finish before rendering
  if (loading) {
    return (
      <View style={{ flex: 1, backgroundColor: '#0f172a', justifyContent: 'center', alignItems: 'center' }}>
        <ActivityIndicator size="large" color="#6366f1" />
      </View>
    );
  }

  // rest of component remains unchanged...
  return (
    <NavigationContainer>
      ...
    </NavigationContainer>
  );
};

This ensures the useState(!appData.onboardingCompleted) only initializes after real data is loaded from AsyncStorage, so the correct initialRouteName is picked from the very first render.


Summary

Issue Root Cause Fix
App crash on Check In/Out <svg>/<path> HTML tags in CheckInModal.tsx + CheckOutModal.tsx Replace with <Svg>/<Path> from react-native-svg
Onboarding every launch Navigation mounts before AsyncStorage load completes Add if (loading) return <splash> in Navigation.tsx

Both fixes are small and surgical — no architectural changes needed. Start with the loading guard fix first (it's one line of logic), then fix the two modal SVG icon components.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/navigation/Navigation.tsx (1)

76-92: ⚠️ Potential issue | 🔴 Critical

Critical: Rules of Hooks violation — early return before useState/useEffect will crash the app.

The early return on lines 78–84 executes before the useState calls on lines 85–87 and the useEffects. On the first render loading is true, so React only calls useApp(). Once loadData() finishes and loading flips to false, the next render calls useApp() + 3× useState + 2× useEffect, violating the Rules of Hooks. React will throw "Rendered more hooks than during the previous render" and crash on virtually every cold start.

Move the loading check after all hook declarations:

🐛 Proposed fix
 const Navigation = () => {
   const { appData, loading } = useApp();
-  // Wait for data to load before rendering navigation
-  if (loading) {
-    return (
-      <View style={{ flex: 1, backgroundColor: '#0f172a', justifyContent: 'center', alignItems: 'center' }}>
-        <ActivityIndicator size="large" color="#6366f1" />
-      </View>
-    );
-  }
   const [showOnboarding, setShowOnboarding] = useState(!appData.onboardingCompleted);
   const [updateInfo, setUpdateInfo] = useState<UpdateInfo | null>(null);
   const [showUpdateModal, setShowUpdateModal] = useState(false);

   useEffect(() => {
     setShowOnboarding(!appData.onboardingCompleted);
   }, [appData.onboardingCompleted]);

   useEffect(() => {
     if (!showOnboarding && Platform.OS !== 'web') {
       checkForUpdate()
         .then(update => {
           if (update) {
             setUpdateInfo(update);
             setShowUpdateModal(true);
           }
         })
         .catch(error => {
           console.log('Update check failed (non-critical):', error?.message || error);
         });
     }
   }, [showOnboarding]);

   const handleDismissUpdate = () => {
     setShowUpdateModal(false);
   };

+  // Wait for data to load before rendering navigation
+  if (loading) {
+    return (
+      <View style={{ flex: 1, backgroundColor: '#0f172a', justifyContent: 'center', alignItems: 'center' }}>
+        <ActivityIndicator size="large" color="#6366f1" />
+      </View>
+    );
+  }
+
   return (
     <NavigationContainer>

Alternatively, gate the mount in App.js before <Navigation /> renders (cleaner): conditionally render <Navigation /> only after loading === false in the Provider component.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/navigation/Navigation.tsx` around lines 76 - 92, The loading early-return
is placed before hook declarations, violating the Rules of Hooks; move the
loading check so that all hooks (the useState calls for showOnboarding,
updateInfo, showUpdateModal and the useEffect that calls setShowOnboarding based
on appData.onboardingCompleted) are declared first, then return the
ActivityIndicator when loading is true, or alternatively stop rendering
<Navigation /> higher up and only mount Navigation when useApp().loading is
false; ensure useApp() remains the first hook call and the useEffect for
setShowOnboarding keeps its dependency on appData.onboardingCompleted.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/navigation/Navigation.tsx`:
- Around line 76-92: The loading early-return is placed before hook
declarations, violating the Rules of Hooks; move the loading check so that all
hooks (the useState calls for showOnboarding, updateInfo, showUpdateModal and
the useEffect that calls setShowOnboarding based on appData.onboardingCompleted)
are declared first, then return the ActivityIndicator when loading is true, or
alternatively stop rendering <Navigation /> higher up and only mount Navigation
when useApp().loading is false; ensure useApp() remains the first hook call and
the useEffect for setShowOnboarding keeps its dependency on
appData.onboardingCompleted.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e7dc8f19-0c66-4619-8649-41a812070d37

📥 Commits

Reviewing files that changed from the base of the PR and between 46fb5d5 and 9de407b.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (3)
  • src/components/CheckInModal.tsx
  • src/components/CheckOutModal.tsx
  • src/navigation/Navigation.tsx

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/navigation/Navigation.tsx (1)

76-83: ⚠️ Potential issue | 🔴 Critical

Loading gate doesn't fully fix the onboarding-on-every-launch bug — showOnboarding initial state is still stale.

showOnboarding is initialized by useState(!appData.onboardingCompleted) using the default appData (before storage load). When loadData() finishes in AppContext it batches setAppData(...) and setLoading(false), so on the first render where loading === false:

  • appData.onboardingCompleted already reflects the loaded value (e.g. true),
  • but showOnboarding is still the stale initial true — the sync useEffect at lines 81–83 only schedules setShowOnboarding(false) after this render,
  • so NavigationContainer mounts with initialRouteName='Onboarding'. initialRouteName is only honored on the navigator's first mount, so the subsequent state update doesn't redirect the user.

The navigation.replace('Main') in OnboardingScreen (context snippet 3) only masks this with a brief flash of the onboarding screen — the underlying issue (and the symptom you reported) isn't actually resolved by the loading gate alone.

Simplest fix: derive showOnboarding directly from context instead of mirroring it into local state. By the time we reach the navigator (post loading-gate), appData is the loaded value.

🛠️ Proposed fix
 const Navigation = () => {
   const { appData, loading } = useApp();
-  const [showOnboarding, setShowOnboarding] = useState(!appData.onboardingCompleted);
   const [updateInfo, setUpdateInfo] = useState<UpdateInfo | null>(null);
   const [showUpdateModal, setShowUpdateModal] = useState(false);
 
-  useEffect(() => {
-    setShowOnboarding(!appData.onboardingCompleted);
-  }, [appData.onboardingCompleted]);
-
   useEffect(() => {
-    if (!showOnboarding && Platform.OS !== 'web') {
+    if (!loading && appData.onboardingCompleted && Platform.OS !== 'web') {
       checkForUpdate()
         .then(update => {
           if (update) {
             setUpdateInfo(update);
             setShowUpdateModal(true);
           }
         })
         .catch(error => {
           console.log('Update check failed (non-critical):', error?.message || error);
         });
     }
-  }, [showOnboarding]);
+  }, [loading, appData.onboardingCompleted]);
 
   // Wait for data to load before rendering navigation
   if (loading) {
     return (
       <View style={{ flex: 1, backgroundColor: '#0f172a', justifyContent: 'center', alignItems: 'center' }}>
         <ActivityIndicator size="large" color="#6366f1" />
       </View>
     );
   }
 
+  const showOnboarding = !appData.onboardingCompleted;
+
   const handleDismissUpdate = () => {

This guarantees initialRouteName is computed from the freshly-loaded appData on the navigator's first mount, removing both the stale-state race and the dependency on OnboardingScreen's fallback redirect. As a bonus it also avoids firing the update check before storage load completes.

Also applies to: 100-107

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/navigation/Navigation.tsx` around lines 76 - 83, The local state
showOnboarding (initialized via useState(!appData.onboardingCompleted) and
synced in the useEffect) can be stale and causes the navigator to mount with the
wrong initialRouteName; instead remove the local showOnboarding state and derive
the value directly from context (use appData.onboardingCompleted and loading to
compute whether to show onboarding) so initialRouteName for NavigationContainer
is computed from the loaded appData on first mount; update any references
(including the update-check gating) to use the derived value and remove the
now-unnecessary setShowOnboarding and its useEffect to avoid the race that
causes the onboarding flash and reliance on OnboardingScreen navigation.replace.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/navigation/Navigation.tsx`:
- Around line 76-83: The local state showOnboarding (initialized via
useState(!appData.onboardingCompleted) and synced in the useEffect) can be stale
and causes the navigator to mount with the wrong initialRouteName; instead
remove the local showOnboarding state and derive the value directly from context
(use appData.onboardingCompleted and loading to compute whether to show
onboarding) so initialRouteName for NavigationContainer is computed from the
loaded appData on first mount; update any references (including the update-check
gating) to use the derived value and remove the now-unnecessary
setShowOnboarding and its useEffect to avoid the race that causes the onboarding
flash and reliance on OnboardingScreen navigation.replace.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 025983c3-8216-42c7-9ca7-5194a64f4f6a

📥 Commits

Reviewing files that changed from the base of the PR and between 9de407b and fb833fc.

📒 Files selected for processing (1)
  • src/navigation/Navigation.tsx

@oryno-dev oryno-dev merged commit 53c0edb into main Apr 25, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant