Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"expo": {
"extra": {
"eas": {
"projectId": "4418b05e-cf5e-4ccc-a472-1bc936253a63"
}
},
"android": {
"package": "com.karolix.agronavis"
}
}
}
16 changes: 11 additions & 5 deletions apps/mobile/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
"scheme": "agronavis",
"userInterfaceStyle": "automatic",
"newArchEnabled": true,
"assetBundlePatterns": [
"**/*"
],
"splash": {
"image": "./assets/images/splash-icon.png",
"resizeMode": "contain",
Expand All @@ -27,12 +30,13 @@
"foregroundImage": "./assets/images/adaptive-icon.png",
"backgroundColor": "#0E3D1F"
},
"package": "com.agronavis.app",
"package": "com.karolix.agronavis",
"permissions": [
"android.permission.CAMERA",
"android.permission.ACCESS_FINE_LOCATION",
"android.permission.ACCESS_COARSE_LOCATION",
"android.permission.READ_EXTERNAL_STORAGE"
"android.permission.READ_EXTERNAL_STORAGE",
"android.permission.RECORD_AUDIO"

@cubic-dev-ai cubic-dev-ai Bot May 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: RECORD_AUDIO permission is added but no code in the app uses audio recording. Unnecessary permissions increase the app's attack surface and may cause user trust issues or app store review friction. Remove it unless an upcoming feature requires it.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mobile/app.json, line 39:

<comment>`RECORD_AUDIO` permission is added but no code in the app uses audio recording. Unnecessary permissions increase the app's attack surface and may cause user trust issues or app store review friction. Remove it unless an upcoming feature requires it.</comment>

<file context>
@@ -27,12 +30,13 @@
         "android.permission.ACCESS_COARSE_LOCATION",
-        "android.permission.READ_EXTERNAL_STORAGE"
+        "android.permission.READ_EXTERNAL_STORAGE",
+        "android.permission.RECORD_AUDIO"
       ]
     },
</file context>
Fix with Cubic

]
},
"web": {
Expand All @@ -58,15 +62,17 @@
}
],
"expo-secure-store",
"@react-native-community/datetimepicker"
"@react-native-community/datetimepicker",
"./plugins/withMonorepoFix"
],
"experiments": {
"typedRoutes": true
},
"extra": {
"eas": {
"projectId": "YOUR_EAS_PROJECT_ID"
}
"projectId": "4418b05e-cf5e-4ccc-a472-1bc936253a63"
},
"router": {}
}
}
}
12 changes: 12 additions & 0 deletions apps/mobile/app/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Redirect } from 'expo-router';

/**
* Root index — Expo Router needs this file to handle the app's entry URL.
* Redirects immediately to the auth welcome screen.
* Once authentication is integrated (Clerk), add your auth check here:
* - isSignedIn → redirect to /(tabs)
* - not signed in → redirect to /(auth)/welcome
*/
export default function Index() {
return <Redirect href="/(auth)/welcome" />;
}
Binary file modified apps/mobile/assets/images/adaptive-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified apps/mobile/assets/images/favicon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified apps/mobile/assets/images/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified apps/mobile/assets/images/notification-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified apps/mobile/assets/images/splash-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions apps/mobile/eas.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"cli": {
"version": ">= 19.0.8",
"appVersionSource": "remote"
},
"build": {
"development": {
"developmentClient": true,
"distribution": "internal"
},
"preview": {
"distribution": "internal"
},
"production": {
"autoIncrement": true
}
Comment on lines +11 to +16

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 | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Read-only check: show effective profile drift between root and app-level EAS configs.
python - <<'PY'
import json, pathlib
root = json.loads(pathlib.Path("eas.json").read_text())
app  = json.loads(pathlib.Path("apps/mobile/eas.json").read_text())

for profile in ["preview", "production"]:
    r = root.get("build", {}).get(profile, {})
    a = app.get("build", {}).get(profile, {})
    print(f"\n[{profile}]")
    for key in ["distribution", "android", "env", "autoIncrement"]:
        print(f"{key}: root={r.get(key)!r} | app={a.get(key)!r}")
PY

Repository: jpdevhub/Agronavis-App

Length of output: 459


Align apps/mobile/eas.json build profiles with root eas.json to prevent build/install drift.

build.preview and build.production in apps/mobile/eas.json omit android.buildType: "apk" and env.npm_config_legacy_peer_deps: "true" that exist in the root config, so running EAS from different locations can yield different behavior.

Proposed alignment diff
   "build": {
     "development": {
       "developmentClient": true,
       "distribution": "internal"
     },
     "preview": {
-      "distribution": "internal"
+      "distribution": "internal",
+      "android": {
+        "buildType": "apk"
+      },
+      "env": {
+        "npm_config_legacy_peer_deps": "true"
+      }
     },
     "production": {
-      "autoIncrement": true
+      "autoIncrement": true,
+      "android": {
+        "buildType": "apk"
+      },
+      "env": {
+        "npm_config_legacy_peer_deps": "true"
+      }
     }
   },
📝 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
"preview": {
"distribution": "internal"
},
"production": {
"autoIncrement": true
}
"preview": {
"distribution": "internal",
"android": {
"buildType": "apk"
},
"env": {
"npm_config_legacy_peer_deps": "true"
}
},
"production": {
"autoIncrement": true,
"android": {
"buildType": "apk"
},
"env": {
"npm_config_legacy_peer_deps": "true"
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/mobile/eas.json` around lines 11 - 16, The build profiles in
apps/mobile/eas.json (the "preview" and "production" objects under "build") are
missing android.buildType: "apk" and env.npm_config_legacy_peer_deps: "true"
that are present in the root eas.json; update the "preview" and "production"
profiles in apps/mobile/eas.json to include android.buildType set to "apk" and
add an env object with npm_config_legacy_peer_deps set to "true" so EAS behavior
is consistent regardless of working directory.

},
"submit": {
"production": {}
}
}
19 changes: 15 additions & 4 deletions apps/mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"scripts": {
"start": "expo start",
"dev": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"android": "expo run:android",
"ios": "expo run:ios",
"web": "expo start --web",
"build:android": "eas build --platform android",
"build:ios": "eas build --platform ios",
Expand Down Expand Up @@ -63,7 +63,7 @@
"react-native-screens": "~4.16.0",
"react-native-svg": "15.12.1",
"react-native-web": "~0.21.0",
"react-native-worklets": "^0.8.3",
"react-native-worklets": "0.5.1",
"react-redux": "^9.2.0",
"redux-persist": "^6.0.0",
"zod": "^3.23.8"
Expand All @@ -72,7 +72,6 @@
"@expo/ngrok": "^4.1.3",
"@types/jest": "^29.5.12",
"@types/react": "~19.1.0",
"@types/react-native": "^0.72.8",
"babel-plugin-module-resolver": "^5.0.2",
"eslint": "^9.25.0",
"eslint-config-expo": "~10.0.0",
Expand All @@ -83,6 +82,18 @@
"typescript": "~5.9.2"
},
"private": true,
"overrides": {
"react-native-svg": "15.12.1",
"react-native-worklets": "0.5.1"
},
"expo": {
"install": {
"exclude": [
"react-native-svg",
"react-native-worklets"
]
}
},
"jest": {
"preset": "jest-expo",
"transformIgnorePatterns": [
Expand Down
118 changes: 118 additions & 0 deletions apps/mobile/plugins/withMonorepoFix.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/**
* Comprehensive monorepo fix for ALL native modules hoisted to root node_modules.
*
* ROOT CAUSE:
* npm workspaces hoists native modules AND react-native to root node_modules.
* Native packages' build.gradle files use relative paths or pass incorrect
* paths to CMake that only work in a flat (non-monorepo) node_modules layout.
*
* STRATEGY: Create symlinks from expected sibling paths → actual react-native paths.
* This is more robust than copying individual files because:
* - Symlinks give access to ALL files in those directories (cmake-utils, etc.)
* - No need to know which specific files each package needs
* - No file duplication
*
* SYMLINKS CREATED (in root node_modules):
* ReactAndroid/ → react-native/ReactAndroid/
* Fixes: react-native-gesture-handler (ReactAndroid/gradle.properties)
* react-native-worklets (ReactAndroid/cmake-utils/folly-flags.cmake)
* react-native-reanimated (ReactAndroid/cmake-utils/*)
*
* ReactCommon/ → react-native/ReactCommon/
* Fixes: react-native-worklets (ReactCommon/cmake-utils/react-native-flags.cmake)
* react-native-reanimated (ReactCommon/cmake-utils/*)
*
* gradle/ → react-native/gradle/
* Fixes: react-native-svg (gradle/libs.versions.toml)
*
* EXT PROPERTIES SET (for @react-native-picker/picker and similar):
* REACT_NATIVE_DIR = react-native directory
* REACT_NATIVE_NODE_MODULES_DIR = node_modules directory
*/
const { withProjectBuildGradle } = require("@expo/config-plugins");

const withMonorepoFix = (config) => {
return withProjectBuildGradle(config, (config) => {
const contents = config.modResults.contents;

if (contents.includes("_MONOREPO_FIX_")) {
return config;
}

const injection = `
// _MONOREPO_FIX_ ──────────────────────────────────────────────────────────────
// Fixes native modules that break when npm workspaces hoists them to root.
// Creates symlinks so all packages find react-native's subdirectories.

def _rnDir = null
def _rnCandidates = [
new File(rootDir, "../node_modules/react-native"), // non-hoisted
new File(rootDir, "../../../node_modules/react-native"), // hoisted to monorepo root
]
for (def _c : _rnCandidates) {
if (_c.exists() && new File(_c, "ReactAndroid/gradle.properties").exists()) {
_rnDir = _c
break
}
}

if (_rnDir != null) {
def _nodeModulesDir = _rnDir.parentFile
logger.lifecycle("[MonorepoFix] react-native at: " + _rnDir.canonicalPath)

// ── Ext properties (for @react-native-picker/picker etc.) ──────────────
ext.REACT_NATIVE_DIR = _rnDir
ext.REACT_NATIVE_NODE_MODULES_DIR = _nodeModulesDir
subprojects {
project.ext.REACT_NATIVE_DIR = _rnDir
project.ext.REACT_NATIVE_NODE_MODULES_DIR = _nodeModulesDir
}

// ── Symlinks ───────────────────────────────────────────────────────────
// Create sibling symlinks so packages can find react-native's directories
// regardless of how they construct paths internally or pass args to CMake.
//
// node_modules/ReactAndroid/ → react-native/ReactAndroid/
// node_modules/ReactCommon/ → react-native/ReactCommon/
// node_modules/gradle/ → react-native/gradle/
//
def _symlinkDirs = ["ReactAndroid", "ReactCommon", "gradle"]
for (def _dirname : _symlinkDirs) {
def _source = new File(_rnDir, _dirname)
def _link = new File(_nodeModulesDir, _dirname)
if (_source.exists()) {
if (!_link.exists()) {
try {
java.nio.file.Files.createSymbolicLink(
_link.toPath(),
_source.toPath()
)
logger.lifecycle("[MonorepoFix] Symlinked: node_modules/" + _dirname + " -> react-native/" + _dirname)
} catch (Exception _e) {
// Symlink failed — fall back to copying key files
logger.warn("[MonorepoFix] Symlink failed for " + _dirname + ": " + _e.message + " — falling back to file copy")
_link.mkdirs()
ant.copy(todir: _link.absolutePath, overwrite: false) {
fileset(dir: _source.absolutePath)
}
}
} else {
logger.lifecycle("[MonorepoFix] Already exists: node_modules/" + _dirname)
}
} else {
logger.warn("[MonorepoFix] Source not found: react-native/" + _dirname)
}
}

} else {
logger.warn("[MonorepoFix] WARNING: react-native not found in any candidate path!")
}
// ─────────────────────────────────────────────────────────────────────────────
`;

config.modResults.contents = contents + injection;
return config;
});
};

module.exports = withMonorepoFix;
73 changes: 73 additions & 0 deletions apps/mobile/plugins/withReactNativePickerFix.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/**

@cubic-dev-ai cubic-dev-ai Bot May 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: This plugin file is dead code — it's never registered in app.json or imported anywhere. The active plugin ./plugins/withMonorepoFix already sets ext.REACT_NATIVE_DIR and ext.REACT_NATIVE_NODE_MODULES_DIR using the same logic. Consider removing this file to avoid confusion.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mobile/plugins/withReactNativePickerFix.js, line 73:

<comment>This plugin file is dead code — it's never registered in `app.json` or imported anywhere. The active plugin `./plugins/withMonorepoFix` already sets `ext.REACT_NATIVE_DIR` and `ext.REACT_NATIVE_NODE_MODULES_DIR` using the same logic. Consider removing this file to avoid confusion.</comment>

<file context>
@@ -0,0 +1,73 @@
+  });
+};
+
+module.exports = withReactNativePickerFix;
</file context>
Fix with Cubic

* Comprehensive monorepo fix for native modules that can't find react-native.
*
* Problems fixed:
* - @react-native-picker/picker → reads REACT_NATIVE_NODE_MODULES_DIR
* - react-native-gesture-handler → reads rootProject.ext.REACT_NATIVE_DIR
*
* Root cause: npm workspaces hoists react-native to the monorepo root
* node_modules. Native modules use relative paths that break when react-native
* isn't at the same node_modules level as themselves.
*
* Strategy:
* 1. Use pure Groovy (no `node` command) to check both hoisted and
* non-hoisted locations for react-native.
* 2. Set REACT_NATIVE_DIR directly on the ROOT PROJECT's ext so RNGH's
* safeExtGet("REACT_NATIVE_DIR") (which calls rootProject.ext.get())
* can find it.
* 3. Also propagate via subprojects{} for other native modules that read
* from their own project.ext.
*
* Paths (rootDir = apps/mobile/android/):
* ../node_modules/react-native → apps/mobile/node_modules/react-native
* ../../../node_modules/react-native → <monorepo-root>/node_modules/react-native (hoisted)
*/
const { withProjectBuildGradle } = require("@expo/config-plugins");

const withReactNativePickerFix = (config) => {
return withProjectBuildGradle(config, (config) => {
const contents = config.modResults.contents;

if (contents.includes("REACT_NATIVE_DIR")) {
// Already patched, skip
return config;
}

const injection = `
// ─── Monorepo Fix ────────────────────────────────────────────────────────────
// Resolves react-native location for native modules in a monorepo where npm
// workspaces may hoist react-native to the root node_modules.
// Uses pure Groovy file checks — no dependency on 'node' being in PATH.
def _reactNativeDir = null
def _rnCandidates = [
new File(rootDir, "../node_modules/react-native"), // non-hoisted: apps/mobile/node_modules
new File(rootDir, "../../../node_modules/react-native"), // hoisted: <monorepo-root>/node_modules
]
for (def candidate : _rnCandidates) {
if (candidate.exists() && new File(candidate, "ReactAndroid/gradle.properties").exists()) {
_reactNativeDir = candidate
break
}
}
if (_reactNativeDir != null) {
// Set on ROOT PROJECT ext directly — read by RNGH via rootProject.ext.get("REACT_NATIVE_DIR")
ext.REACT_NATIVE_DIR = _reactNativeDir
ext.REACT_NATIVE_NODE_MODULES_DIR = _reactNativeDir.parentFile
// Also propagate to all subprojects for other native modules
subprojects {
ext.REACT_NATIVE_DIR = _reactNativeDir
ext.REACT_NATIVE_NODE_MODULES_DIR = _reactNativeDir.parentFile
}
logger.lifecycle("[MonorepoFix] react-native resolved at: " + _reactNativeDir.absolutePath)
} else {
logger.warn("[MonorepoFix] WARNING: Could not resolve react-native location!")
}
// ─────────────────────────────────────────────────────────────────────────────
`;

config.modResults.contents = contents + injection;
return config;
});
};

module.exports = withReactNativePickerFix;
33 changes: 33 additions & 0 deletions eas.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"cli": {
"version": ">= 18.13.0",
"appVersionSource": "remote"
},
"build": {
"development": {
"developmentClient": true,
"distribution": "internal"
},
"preview": {
"distribution": "internal",
"android": {

@cubic-dev-ai cubic-dev-ai Bot May 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1: Production builds should use AAB format (the default), not APK. APK is appropriate for preview/internal distribution, but Google Play Store requires AAB for submission. Since you have a submit.production block, this will prevent successful store submission.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At eas.json, line 13:

<comment>Production builds should use AAB format (the default), not APK. APK is appropriate for `preview`/internal distribution, but Google Play Store requires AAB for submission. Since you have a `submit.production` block, this will prevent successful store submission.</comment>

<file context>
@@ -0,0 +1,33 @@
+    },
+    "preview": {
+      "distribution": "internal",
+      "android": {
+        "buildType": "apk"
+      },
</file context>
Fix with Cubic

"buildType": "apk"
},
"env": {
"npm_config_legacy_peer_deps": "true"
}
},
"production": {
"autoIncrement": true,
"android": {
"buildType": "apk"
},
"env": {
"npm_config_legacy_peer_deps": "true"
}
}
},
"submit": {
"production": {}
Comment on lines +20 to +31

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 | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

For Expo EAS as of 2026, does build.production.android.buildType: "apk"produce an artifact compatible with Google Play submission viaeas submit, or is AAB required?

💡 Result:

Yes—however, for Google Play submissions via Expo EAS Submit, AAB is the expected/accepted format, and Expo explicitly recommends AAB for store submission. 1) What build.production.android.buildType: "apk" produces Expo’s EAS Build configuration supports generating an Android.apk by setting android.buildType to apk in your build profile. Expo notes that the default Android build output is an Android App Bundle (AAB), and that.apk is mainly for install/testing on emulators or devices rather than store distribution. [1][2] 2) What eas submit accepts for Google Play Expo’s EAS Submit documentation states that EAS Submit accepts any valid Android App Bundle (.aab) file for Android submissions to Google Play. [3] 3) Is an APK sufficient for Google Play via EAS Submit? Expo’s store-submission guidance is that production builds are ready to be submitted to app stores, and Expo recommends using AAB when submitting to stores; AAB is the default configuration. [4][2] Given (a) Expo’s explicit “AAB recommended/default for Google Play store submission” guidance [4][2] and (b) EAS Submit’s stated acceptance of valid .aab Android binaries for Google Play submissions [3], the safe conclusion as of 2026-05-23 is: AAB is required/expected for Play submission via eas submit, and using buildType: "apk" may not be compatible with Play submission through EAS Submit. Practical recommendation If your goal is Google Play via eas submit, keep your production Android build as the default AAB output (omit android.buildType: "apk" for the production profile), and use eas submit --platform android after building the production profile. [4][5][3]

Citations:


Switch production Android build from apk to AAB for eas submit
eas.json sets build.production.android.buildType: "apk" (lines 20-31), which makes EAS Build output an APK. For Google Play submissions via Expo EAS Submit, store submission expects an Android App Bundle (.aab), so keep production aligned to the AAB/default output (e.g., remove the android.buildType: "apk" override) and restrict APK to preview/testing if needed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@eas.json` around lines 20 - 31, The production profile in eas.json currently
forces an Android APK via the "production" -> "android" -> "buildType": "apk"
setting which prevents eas submit from producing an AAB; remove that override
(or change it to "aab") so the production profile outputs an Android App Bundle
for Google Play submissions and reserve "apk" only for testing/preview profiles
(e.g., "preview" profile), updating the "production" profile's android.buildType
accordingly.

}
}
Loading
Loading