add authentication#3
Conversation
📝 WalkthroughWalkthroughThis PR integrates Clerk authentication into a React Native Expo app. It adds ClerkProvider and PostHogProvider to the root layout, supplies environment keys, implements sign-in (with optional MFA), sign-up (email verification), and forgot-password flows, gates routes based on auth state, adds validation helpers, and surfaces user info in protected screens. ChangesClerk Authentication Integration
Sequence Diagram(s)sequenceDiagram
participant User
participant App as RootLayout
participant PostHog as PostHogProvider
participant Clerk as ClerkProvider
participant SignIn as SignInScreen
participant AuthAPI as Clerk API
participant Tabs as Tabs
User->>App: Launch app
App->>PostHog: init(EXPO_PUBLIC_POSTHOG_API_KEY, host)
PostHog->>Clerk: initialize(publishableKey, tokenCache)
Clerk->>Tabs: render routes
User->>SignIn: submit(email,password)
SignIn->>AuthAPI: signIn.create()
alt complete
AuthAPI-->>SignIn: status=complete
SignIn->>Tabs: navigate home
else needs_client_trust
AuthAPI-->>SignIn: status=needs_client_trust
User->>SignIn: submit verification code
SignIn->>AuthAPI: verify code
AuthAPI-->>SignIn: status=complete
SignIn->>Tabs: navigate home
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
.idea/workspace.xml (1)
106-135:⚠️ Potential issue | 🟠 Major | ⚡ Quick winRemove
.idea/workspace.xmlfrom source control and ignore it.This file contains machine-specific IDE state and local absolute paths (for example on Line 126 and Line 132), which creates noisy churn and can leak developer environment details. Please drop it from the PR and add
.idea/workspace.xml(or.idea/) to.gitignore.🤖 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 @.idea/workspace.xml around lines 106 - 135, The .idea/workspace.xml file contains machine-specific IDE state and absolute local paths; remove it from the PR and stop committing it by deleting it from the index and adding it to .gitignore: remove .idea/workspace.xml from git tracking (e.g., git rm --cached .idea/workspace.xml or remove the whole .idea/ directory from tracking), add an entry for .idea/ or .idea/workspace.xml to .gitignore, commit the .gitignore change, and amend the PR so the workspace.xml content no longer appears in the diff.
🧹 Nitpick comments (2)
app/_layout.tsx (1)
1-31: ⚡ Quick winCall
SplashScreen.preventAutoHideAsync()before waiting on fonts.Expo’s root-layout examples pair a font gate with
SplashScreen.preventAutoHideAsync()at module scope. Without that call, the native splash can auto-hide beforehideAsync()runs, which defeats this loading guard and can cause a brief blank/unstyled first paint. (docs.expo.dev)♻️ Proposed fix
import { SplashScreen, Stack } from "expo-router"; import "`@/global.css`"; import { useFonts } from "expo-font"; import { useEffect } from "react"; import { ClerkProvider } from "`@clerk/expo`"; import { tokenCache } from "`@clerk/expo/token-cache`"; +SplashScreen.preventAutoHideAsync(); + const publishableKey = process.env.EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY!;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/_layout.tsx` around lines 1 - 31, Add a module‑scope call to SplashScreen.preventAutoHideAsync() so the native splash won't auto-hide before fonts finish loading: at the top of the file (before RootLayout and before awaiting fonts) call SplashScreen.preventAutoHideAsync(), ideally wrapped in a try/catch to surface any errors; keep the existing useEffect that calls SplashScreen.hideAsync() when fontsloaded is true (in RootLayout) so the splash is hidden only after fonts are ready.app/(auth)/forgot-password.tsx (1)
151-163: ⚡ Quick winUse one autofill API for the new-password field.
React Native documents
autoCompleteandtextContentTypeas overlapping hints and explicitly says not to set both; when both are present,textContentTypetakes precedence, so autofill behavior can diverge across platforms. (reactnative.dev)♻️ Proposed fix
<TextInput className={clsx( "auth-input", pwError && "auth-input-error" )} value={newPassword} onChangeText={setNewPassword} placeholder="Create a new password" placeholderTextColor={colors.mutedForeground} secureTextEntry - autoComplete="password-new" - textContentType="newPassword" + autoComplete={Platform.select({ + android: "password-new", + default: "new-password", + })} />🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/`(auth)/forgot-password.tsx around lines 151 - 163, The TextInput currently sets both autoComplete and textContentType which conflicts; remove textContentType="newPassword" and rely on autoComplete="password-new" (or vice-versa if you prefer platform-specific behavior) so only one autofill hint is used; update the TextInput props in the component where newPassword and setNewPassword are used (the TextInput instance shown) to keep a single autofill API and ensure pwError class handling remains unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.env:
- Line 1: Remove the hardcoded Clerk test publishable key from the tracked .env
and replace it with a neutral placeholder (e.g.,
EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY=your_clerk_publishable_key_here); ensure the
real key is injected per environment (CI/deploy secrets or environment
variables) and add or update a .env.example describing the variable so
developers know to populate it locally; also ensure any commit that contains the
real key is reverted/rotated and that EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY is
listed in .gitignore or excluded from tracked secret files moving forward.
In `@app/`(auth)/forgot-password.tsx:
- Around line 247-250: The subtitle Text node with className "auth-subtitle"
contains an unescaped apostrophe in "we'll" causing react/no-unescaped-entities;
update that Text child to escape the apostrophe (e.g., replace "we'll" with
"we'll" or wrap the string in a JS expression like {"we'll"}) so the linter
stops flagging it while leaving the visible text unchanged.
- Around line 48-56: In goHome (the goHome function that calls signIn.finalize
and uses decorateUrl("/")), detect Clerk-decorated absolute URLs by checking if
the decorated url startsWith("http") and if so perform external navigation
(window.location.replace or location.assign) instead of router.replace;
otherwise continue using router.replace(url as Href). Also remove overlapping
autofill hints on the "New password" TextInput by using only one of
autoComplete="password-new" or textContentType="newPassword" (or choose via
Platform.select) to avoid conflicting hints. Finally fix the JSX unescaped
apostrophe in the copy that uses "we'll" (replace with We'll or use a
string expression) to satisfy react/no-unescaped-entities.
In `@app/`(auth)/sign-in.tsx:
- Line 18: The sign-in form is incorrectly using getPasswordError (and enforcing
min-length) from getPasswordError/isValidEmail which blocks valid existing
passwords; change the sign-in validation to only require a non-empty password:
remove or bypass calls to getPasswordError in the sign-in form handlers and
replace that check with a simple non-empty check (e.g., password.trim().length >
0) where getPasswordError is currently used (see references to
getPasswordError/isValidEmail in the sign-in component and the submit/validation
logic around the password input and form submit handlers).
---
Outside diff comments:
In @.idea/workspace.xml:
- Around line 106-135: The .idea/workspace.xml file contains machine-specific
IDE state and absolute local paths; remove it from the PR and stop committing it
by deleting it from the index and adding it to .gitignore: remove
.idea/workspace.xml from git tracking (e.g., git rm --cached .idea/workspace.xml
or remove the whole .idea/ directory from tracking), add an entry for .idea/ or
.idea/workspace.xml to .gitignore, commit the .gitignore change, and amend the
PR so the workspace.xml content no longer appears in the diff.
---
Nitpick comments:
In `@app/_layout.tsx`:
- Around line 1-31: Add a module‑scope call to
SplashScreen.preventAutoHideAsync() so the native splash won't auto-hide before
fonts finish loading: at the top of the file (before RootLayout and before
awaiting fonts) call SplashScreen.preventAutoHideAsync(), ideally wrapped in a
try/catch to surface any errors; keep the existing useEffect that calls
SplashScreen.hideAsync() when fontsloaded is true (in RootLayout) so the splash
is hidden only after fonts are ready.
In `@app/`(auth)/forgot-password.tsx:
- Around line 151-163: The TextInput currently sets both autoComplete and
textContentType which conflicts; remove textContentType="newPassword" and rely
on autoComplete="password-new" (or vice-versa if you prefer platform-specific
behavior) so only one autofill hint is used; update the TextInput props in the
component where newPassword and setNewPassword are used (the TextInput instance
shown) to keep a single autofill API and ensure pwError class handling remains
unchanged.
🪄 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: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 03947a78-e31f-4086-bcf8-4e1e1ef9867e
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (13)
.env.idea/workspace.xmlapp.jsonapp/(auth)/_layout.tsxapp/(auth)/forgot-password.tsxapp/(auth)/sign-in.tsxapp/(auth)/sign-up.tsxapp/(tabs)/_layout.tsxapp/(tabs)/index.tsxapp/(tabs)/settings.tsxapp/_layout.tsxlib/validation.tspackage.json
| @@ -0,0 +1 @@ | |||
| EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_YW11c2luZy1tb2xsdXNrLTE2LmNsZXJrLmFjY291bnRzLmRldiQ No newline at end of file | |||
There was a problem hiding this comment.
Don't hardwire the Clerk test tenant into the default app config.
pk_test_... points the app at Clerk's test instance. If this tracked .env is the default on main, release builds will authenticate against the wrong tenant. Keep only a placeholder/example in source control and inject the real publishable key per environment.
🔧 Suggested change
-EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_YW11c2luZy1tb2xsdXNrLTE2LmNsZXJrLmFjY291bnRzLmRldiQ
+EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY=<set-per-environment>📝 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.
| EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_YW11c2luZy1tb2xsdXNrLTE2LmNsZXJrLmFjY291bnRzLmRldiQ | |
| EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY=<set-per-environment> |
🧰 Tools
🪛 Betterleaks (1.3.1)
[high] 1-1: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
🪛 dotenv-linter (4.0.0)
[warning] 1-1: [EndingBlankLine] No blank line at the end of the file
(EndingBlankLine)
🤖 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 @.env at line 1, Remove the hardcoded Clerk test publishable key from the
tracked .env and replace it with a neutral placeholder (e.g.,
EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY=your_clerk_publishable_key_here); ensure the
real key is injected per environment (CI/deploy secrets or environment
variables) and add or update a .env.example describing the variable so
developers know to populate it locally; also ensure any commit that contains the
real key is reverted/rotated and that EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY is
listed in .gitignore or excluded from tracked secret files moving forward.
| const goHome = async () => { | ||
| await signIn.finalize({ | ||
| navigate: ({ session, decorateUrl }) => { | ||
| if (session?.currentTask) return; | ||
| const url = decorateUrl("/"); | ||
| router.replace(url as Href); | ||
| }, | ||
| }); | ||
| }; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate and inspect the target file
FILE="app/(auth)/forgot-password.tsx"
if [ ! -f "$FILE" ]; then
echo "Missing file: $FILE" >&2
exit 1
fi
echo "=== File: $FILE (lines 1-120) ==="
nl -ba "$FILE" | sed -n '1,140p'
echo "=== File: $FILE (lines 220-320) ==="
nl -ba "$FILE" | sed -n '220,320p'
echo "=== Search for decorateUrl / router.replace / Platform usage ==="
rg -n "decorateUrl\\(|router\\.replace\\(|Platform\\.OS" "$FILE" || true
echo "=== Search for TextInput autofill props ==="
rg -n "autoComplete=|textContentType=|newPassword" "$FILE" || trueRepository: Hill-source/react-native-subscription-app
Length of output: 206
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="app/(auth)/forgot-password.tsx"
if [ ! -f "$FILE" ]; then
echo "Missing file: $FILE" >&2
exit 1
fi
echo "=== File: $FILE (lines 1-140) ==="
cat -n "$FILE" | sed -n '1,140p'
echo "=== File: $FILE (lines 200-320) ==="
cat -n "$FILE" | sed -n '200,320p'
echo "=== Search for decorateUrl / router.replace / Href / Platform ==="
rg -n "decorateUrl\\(|router\\.replace\\(|Href\\b|Platform\\.OS" "$FILE" || true
echo "=== Search for TextInput autofill props ==="
rg -n "autoComplete=|textContentType=|newPassword|password-new" "$FILE" || true
echo "=== Search for react/no-unescaped-entities ==="
rg -n "react/no-unescaped-entities" "$FILE" || trueRepository: Hill-source/react-native-subscription-app
Length of output: 13383
🌐 Web query:
Clerk Expo custom flow forgot password decorateUrl url.startsWith("http") router.replace href
💡 Result:
When implementing custom authentication flows in Clerk with Expo, the pattern involving decorateUrl, router.replace (or router.push), and url.startsWith("http") is used to handle post-authentication navigation securely and flexibly [1][2][3]. This pattern ensures that your application correctly handles redirects, whether the target is an internal route within your Expo Router application or an external URL [1][3]. The standard implementation for finalizing an authentication flow (such as sign-in, sign-up, or password reset) is as follows: 1. Use the finalize method from your authentication resource (e.g., signIn.finalize or signUp.finalize) [1][2][4]. 2. Provide a navigate callback function that receives the session and decorateUrl arguments [1][2]. 3. Inside the callback: - Check for any pending session tasks (e.g., if a user needs to complete a password reset or MFA) using session?.currentTask [1][3]. - If no tasks are pending, call decorateUrl(path) to obtain the final, Clerk-signed destination URL [1][2]. - Verify if the URL is external by checking if it starts with "http" [1][3]. - If it is an external URL, use window.location.href = url [1][2]. - If it is an internal path, use your router's navigation method, typically router.push(url) or router.replace(url) [1][2][3]. Example Implementation: finalize({ navigate: async ({ session, decorateUrl }) => { // 1. Handle pending session tasks if they exist if (session?.currentTask) { console.log('Pending task:', session.currentTask); return; } // 2. Decorate the target URL const url = decorateUrl('/'); // 3. Perform the appropriate navigation if (url.startsWith('http')) { window.location.href = url; } else { router.push(url); // Or router.replace(url) } }, }); This approach is recommended because decorateUrl automatically handles necessary Clerk parameters, and the check for "http" prevents errors when attempting to use an internal router for external web addresses [1][3]. For managing session tasks like forgot password flows, it is also highly recommended to configure the taskUrls prop on your ClerkProvider to centralize your redirect logic [3][5].
Citations:
- 1: https://clerk.com/docs/guides/development/custom-flows/authentication/forgot-password
- 2: https://clerk.com/docs/guides/development/custom-flows/authentication/sign-in-or-up
- 3: https://clerk.com/docs/guides/development/custom-flows/authentication/session-tasks
- 4: https://clerk.com/docs/react/guides/development/custom-flows/authentication/email-links
- 5: https://clerk.com/docs/guides/development/custom-flows/authentication/legacy/forgot-password
🌐 Web query:
react-native TextInput autoComplete password-new and textContentType newPassword overlap guidance
💡 Result:
In React Native, the autoComplete and textContentType props serve similar purposes, but they have different platform implementations and historical precedence rules. Guidance and Best Practices 1. Prefer autoComplete: The autoComplete prop is the recommended approach for modern React Native development because it is supported across both Android and iOS [1]. It provides a unified API that maps to platform-specific autofill hints [2]. 2. Avoid using both if possible: To maintain clean code and avoid unintended behavior, you should avoid setting both autoComplete and textContentType on the same TextInput [1]. 3. Precedence rules: For backwards compatibility, if you must set both props, textContentType takes precedence over autoComplete on iOS [1]. 4. Platform-specific usage: - On iOS, textContentType provides the system with semantic meaning for autofill and keychain interaction [3][4]. Use newPassword for password creation/reset fields to trigger appropriate system keychain saving behavior [3]. - On Android, autoComplete maps to internal autofill hints [2]. - If platform-specific nuances require distinct values, use Platform.select to conditionally set the props [1]. Implementation Example For a new password field, the following pattern is recommended: <TextInput autoComplete="new-password" secureTextEntry={true} // Other props... /> If you are maintaining older codebases where textContentType is already established, note that textContentType="newPassword" is the equivalent semantic mapping for a new password field on iOS [3][2]. If you encounter issues with iOS suggesting passwords on incorrect fields, ensure that preceding fields (like email or username) are also correctly configured with their respective autoComplete values (e.g., "username" or "email") [5][6]. Summary of Key Values - For standard password fields: autoComplete="password" or textContentType="password" [3][2]. - For new password/registration fields: autoComplete="new-password" or textContentType="newPassword" [3][2].
Citations:
- 1: react/react-native@c0abff1
- 2: react/react-native@73abcba
- 3: https://github.com/facebook/react-native-website/blob/main/docs/textinput.md
- 4: [iOS][TextInput] Add iOS 10 textContentType for TextInput react/react-native#18526
- 5: https://stackoverflow.com/questions/69461700/auto-fill-password-using-text-input-react-native
- 6: https://stackoverflow.com/questions/73121106/react-native-auto-fill-and-suggestions-for-email-and-password-inputs
🌐 Web query:
react/no-unescaped-entities JSX "we'll" lint rule
💡 Result:
The react/no-unescaped-entities ESLint rule flags characters in your JSX that may conflict with JSX syntax or be misinterpreted by the parser [1][2]. When you use a contraction like "we'll" in JSX, the single quote (apostrophe) can trigger this rule because it may be mistaken for the closing delimiter of a string or JSX attribute [2]. To resolve this while keeping the text as "we'll," you have several options: 1. Use an HTML entity: Replace the straight single quote with its HTML entity equivalent, such as ' or ' [1][2]. Example:
Citations:
- 1: https://github.com/jsx-eslint/eslint-plugin-react/blob/HEAD/docs/rules/no-unescaped-entities.md
- 2: https://www.wemmyo.com/blog/react-no-unescaped-entities
- 3: https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-unescaped-entities.md
- 4: https://stackoverflow.com/questions/43177074/how-can-i-fix-this-violation-of-this-react-no-unescaped-entitie-of-an-eslint-r
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="app/(auth)/forgot-password.tsx"
echo "=== Password TextInput area (lines 130-190) ==="
cat -n "$FILE" | sed -n '130,190p'Repository: Hill-source/react-native-subscription-app
Length of output: 3970
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="app/(auth)/forgot-password.tsx"
rg -n "\\bLinking\\b" "$FILE" || trueRepository: Hill-source/react-native-subscription-app
Length of output: 67
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n "decorateUrl\\(.*\\).*router\\.replace|decorateUrl\\(.*\\).*router\\.(push|replace)" app || true
rg -n "decorateUrl\\(" app || trueRepository: Hill-source/react-native-subscription-app
Length of output: 309
🏁 Script executed:
#!/bin/bash
set -euo pipefail
for FILE in app/'(auth)'/sign-in.tsx app/'(auth)'/sign-up.tsx; do
echo "=== $FILE goHome/finalize navigation section (lines 1-120) ==="
cat -n "$FILE" | sed -n '1,120p'
echo
doneRepository: Hill-source/react-native-subscription-app
Length of output: 10184
Handle Clerk-decorated external URLs in goHome (don’t always router.replace absolute http(s) URLs).
- In
app/(auth)/forgot-password.tsx,goHomedoesconst url = decorateUrl("/")and always callsrouter.replace(url as Href); follow Clerk’s pattern to detecturl.startsWith("http")and use external navigation instead (the same pattern also exists inapp/(auth)/sign-in.tsxandapp/(auth)/sign-up.tsx). New passwordTextInputsets bothautoComplete="password-new"andtextContentType="newPassword"; avoid overlapping autofill hints (or set one viaPlatform.select).- JSX text uses
we'llat ~line 249, which tripsreact/no-unescaped-entities—replace the apostrophe (e.g.,We'llor{"We'll ..."}).
🐛 Proposed fix (for `goHome`)
const goHome = async () => {
await signIn.finalize({
navigate: ({ session, decorateUrl }) => {
if (session?.currentTask) return;
const url = decorateUrl("/");
+ if (Platform.OS === "web" && url.startsWith("http")) {
+ window.location.href = url;
+ return;
+ }
router.replace(url as Href);
},
});
};🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/`(auth)/forgot-password.tsx around lines 48 - 56, In goHome (the goHome
function that calls signIn.finalize and uses decorateUrl("/")), detect
Clerk-decorated absolute URLs by checking if the decorated url
startsWith("http") and if so perform external navigation
(window.location.replace or location.assign) instead of router.replace;
otherwise continue using router.replace(url as Href). Also remove overlapping
autofill hints on the "New password" TextInput by using only one of
autoComplete="password-new" or textContentType="newPassword" (or choose via
Platform.select) to avoid conflicting hints. Finally fix the JSX unescaped
apostrophe in the copy that uses "we'll" (replace with We'll or use a
string expression) to satisfy react/no-unescaped-entities.
| <Text className="auth-title text-center">Reset your password</Text> | ||
| <Text className="auth-subtitle"> | ||
| Enter your email and we'll send you a code to reset your password | ||
| </Text> |
There was a problem hiding this comment.
Escape the apostrophe in the subtitle.
Line 249 is already flagged by react/no-unescaped-entities, so this will keep lint red until the apostrophe is escaped.
🛠️ Proposed fix
<Text className="auth-subtitle">
- Enter your email and we'll send you a code to reset your password
+ Enter your email and we'll send you a code to reset your password
</Text>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <Text className="auth-title text-center">Reset your password</Text> | |
| <Text className="auth-subtitle"> | |
| Enter your email and we'll send you a code to reset your password | |
| </Text> | |
| <Text className="auth-title text-center">Reset your password</Text> | |
| <Text className="auth-subtitle"> | |
| Enter your email and we'll send you a code to reset your password | |
| </Text> |
🧰 Tools
🪛 ESLint
[error] 249-249: ' can be escaped with ', ‘, ', ’.
(react/no-unescaped-entities)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/`(auth)/forgot-password.tsx around lines 247 - 250, The subtitle Text
node with className "auth-subtitle" contains an unescaped apostrophe in "we'll"
causing react/no-unescaped-entities; update that Text child to escape the
apostrophe (e.g., replace "we'll" with "we'll" or wrap the string in a JS
expression like {"we'll"}) so the linter stops flagging it while leaving the
visible text unchanged.
| } from "react-native"; | ||
| import { SafeAreaView as RNSafeAreaView } from "react-native-safe-area-context"; | ||
| import { colors } from "@/constants/theme"; | ||
| import { getPasswordError, isValidEmail } from "@/lib/validation"; |
There was a problem hiding this comment.
Don't enforce sign-up password rules on sign-in.
This screen prevents submission for any password shorter than 8 characters before Clerk can authenticate it. That can lock out existing or migrated users whose valid password doesn't match the current sign-up policy. On sign-in, only require a non-empty password.
🔧 Suggested change
-import { getPasswordError, isValidEmail } from "`@/lib/validation`";
+import { isValidEmail } from "`@/lib/validation`";
@@
- const passwordError = submitted ? getPasswordError(password) : null;
+ const passwordError =
+ submitted && password.length === 0 ? "Password is required" : null;
@@
- if (!isValidEmail(email) || getPasswordError(password)) return;
+ if (!isValidEmail(email) || password.length === 0) return;Also applies to: 34-35, 49-52
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/`(auth)/sign-in.tsx at line 18, The sign-in form is incorrectly using
getPasswordError (and enforcing min-length) from getPasswordError/isValidEmail
which blocks valid existing passwords; change the sign-in validation to only
require a non-empty password: remove or bypass calls to getPasswordError in the
sign-in form handlers and replace that check with a simple non-empty check
(e.g., password.trim().length > 0) where getPasswordError is currently used (see
references to getPasswordError/isValidEmail in the sign-in component and the
submit/validation logic around the password input and form submit handlers).
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/_layout.tsx`:
- Around line 36-43: PostHogProvider is being initialized directly with
process.env.EXPO_PUBLIC_POSTHOG_API_KEY and process.env.EXPO_PUBLIC_POSTHOG_HOST
which can be undefined; change this by validating those env vars before passing
them (remove the non-null assert) and render PostHogProvider only when a
validated apiKey (and optional host) exists—otherwise skip the provider (or
render a no-op wrapper) so analytics is not booted with invalid config; look for
PostHogProvider usage in _layout.tsx and gate its rendering around a simple
check of EXPO_PUBLIC_POSTHOG_API_KEY (and EXPO_PUBLIC_POSTHOG_HOST if required),
using publishableKey/tokenCache usage inside the guarded block unchanged.
🪄 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: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: cbe61d10-7c18-4385-bd53-a7f670d45ced
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (6)
.env.gitignore.idea/workspace.xmlapp.jsonapp/_layout.tsxpackage.json
✅ Files skipped from review due to trivial changes (3)
- .gitignore
- .idea/workspace.xml
- app.json
| <PostHogProvider | ||
| apiKey={process.env.EXPO_PUBLIC_POSTHOG_API_KEY!} | ||
| options={{ host: process.env.EXPO_PUBLIC_POSTHOG_HOST }} | ||
| > | ||
| <ClerkProvider publishableKey={publishableKey} tokenCache={tokenCache}> | ||
| <Stack screenOptions={{ headerShown: false }} /> | ||
| </ClerkProvider> | ||
| </PostHogProvider> |
There was a problem hiding this comment.
Guard the PostHog provider behind validated env config.
Unlike publishableKey, the PostHog env vars go straight into provider init. A missing or misspelled EXPO_PUBLIC_POSTHOG_API_KEY / EXPO_PUBLIC_POSTHOG_HOST will boot with invalid analytics config instead of failing clearly or skipping analytics.
🔧 Suggested change
+const posthogApiKey = process.env.EXPO_PUBLIC_POSTHOG_API_KEY;
+const posthogHost = process.env.EXPO_PUBLIC_POSTHOG_HOST;
+
export default function RootLayout() {
const [fontsloaded] = useFonts({
"sans-regular": require("../assets/fonts/PlusJakartaSans-Regular.ttf"),
"sans-medium": require("../assets/fonts/PlusJakartaSans-Medium.ttf"),
"sans-semibold": require("../assets/fonts/PlusJakartaSans-SemiBold.ttf"),
"sans-bold": require("../assets/fonts/PlusJakartaSans-Bold.ttf"),
"sans-extrabold": require("../assets/fonts/PlusJakartaSans-ExtraBold.ttf"),
"sans-light": require("../assets/fonts/PlusJakartaSans-Light.ttf"),
});
@@
if (!fontsloaded) {
return null;
}
+ const app = (
+ <ClerkProvider publishableKey={publishableKey} tokenCache={tokenCache}>
+ <Stack screenOptions={{ headerShown: false }} />
+ </ClerkProvider>
+ );
+
+ if (!posthogApiKey || !posthogHost) {
+ return app;
+ }
+
return (
- <PostHogProvider
- apiKey={process.env.EXPO_PUBLIC_POSTHOG_API_KEY!}
- options={{ host: process.env.EXPO_PUBLIC_POSTHOG_HOST }}
- >
- <ClerkProvider publishableKey={publishableKey} tokenCache={tokenCache}>
- <Stack screenOptions={{ headerShown: false }} />
- </ClerkProvider>
- </PostHogProvider>
+ <PostHogProvider apiKey={posthogApiKey} options={{ host: posthogHost }}>
+ {app}
+ </PostHogProvider>
);
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/_layout.tsx` around lines 36 - 43, PostHogProvider is being initialized
directly with process.env.EXPO_PUBLIC_POSTHOG_API_KEY and
process.env.EXPO_PUBLIC_POSTHOG_HOST which can be undefined; change this by
validating those env vars before passing them (remove the non-null assert) and
render PostHogProvider only when a validated apiKey (and optional host)
exists—otherwise skip the provider (or render a no-op wrapper) so analytics is
not booted with invalid config; look for PostHogProvider usage in _layout.tsx
and gate its rendering around a simple check of EXPO_PUBLIC_POSTHOG_API_KEY (and
EXPO_PUBLIC_POSTHOG_HOST if required), using publishableKey/tokenCache usage
inside the guarded block unchanged.
Summary by CodeRabbit
New Features
Feature Updates
Chores