Release/2.0.23#2271
Conversation
Agent-Logs-Url: https://github.com/PackRat-AI/PackRat/sessions/c06bd591-dda9-4bcd-a776-5e77ee05fddf Co-authored-by: mikib0 <54102880+mikib0@users.noreply.github.com>
Agent-Logs-Url: https://github.com/PackRat-AI/PackRat/sessions/0d5003cc-1c2f-4b67-89e6-70823c2c812f Co-authored-by: mikib0 <54102880+mikib0@users.noreply.github.com>
Agent-Logs-Url: https://github.com/PackRat-AI/PackRat/sessions/0d5003cc-1c2f-4b67-89e6-70823c2c812f Co-authored-by: mikib0 <54102880+mikib0@users.noreply.github.com>
Agent-Logs-Url: https://github.com/PackRat-AI/PackRat/sessions/0d5003cc-1c2f-4b67-89e6-70823c2c812f Co-authored-by: mikib0 <54102880+mikib0@users.noreply.github.com>
Agent-Logs-Url: https://github.com/PackRat-AI/PackRat/sessions/0d5003cc-1c2f-4b67-89e6-70823c2c812f Co-authored-by: mikib0 <54102880+mikib0@users.noreply.github.com>
Agent-Logs-Url: https://github.com/PackRat-AI/PackRat/sessions/5f9c1470-ddc4-4e24-a27d-68261ccb873f Co-authored-by: mikib0 <54102880+mikib0@users.noreply.github.com>
Agent-Logs-Url: https://github.com/PackRat-AI/PackRat/sessions/bb733d52-2184-48a4-a4a5-b6130c47c621 Co-authored-by: mikib0 <54102880+mikib0@users.noreply.github.com>
…terns Unify search UI pattern across Guides, Pack Templates, and Trips screens
- Fix check-android-textinput.sh: - Use awk for multiline-capable TextInput import detection - Replace PCRE lookahead with portable grep -v pipeline for third-party check - Change --diff-filter=A to --diff-filter=AM to catch modifications too - Fix android-keyboard-focus-prevention-strategies.md: - Replace non-existent Keyboard.emit() with proper addListener mock pattern - Fix cleanup test to assert remove() on subscription object - Add event type assertion to both unit and integration test examples - Fix android-textinput-keyboard-focus-loss.md: - Correct broken cross-reference paths (../CLAUDE.md -> ../../../CLAUDE.md) - Fix android-keyboard-prevention-implementation-summary.md: - Remove references to non-existent test files - Remove reference to non-existent eslint-android-textinput-rules.js Agent-Logs-Url: https://github.com/PackRat-AI/PackRat/sessions/804a88c4-bdca-492f-b2e8-a62b2174dfbe Co-authored-by: mikib0 <54102880+mikib0@users.noreply.github.com>
- Delete scripts/check-android-textinput.sh - Remove android-textinput-check entry from lefthook.yml - Update all docs to remove references to the deleted script: - android-keyboard-prevention-implementation-summary.md - android-keyboard-focus-prevention-strategies.md - android-textinput-checklist.md Agent-Logs-Url: https://github.com/PackRat-AI/PackRat/sessions/4d463d8b-d0b7-4c89-943d-fb9c1b4f8177
…ndroid fix(expo): TextInput not losing focus after dismissing keyboard
fix(expo): resolve packId type incompatibility in TripForm
Main -> Development
📝 WalkthroughWalkthroughAdds a keyboard-hide blur hook and two ref-forwarding input wrappers (TextInput, SearchInput), migrates many screens to use them, improves list search UX (headers, results/empty states), adds extensive Android keyboard-focus docs and checklists, removes a debug screen, and bumps multiple package/app versions 2.0.22 → 2.0.23. Changes
Sequence Diagram(s)sequenceDiagram
participant User as "User / UI"
participant Screen as "Screen Component"
participant Wrapper as "expo-app Input Wrapper"
participant Hook as "useKeyboardHideBlur"
participant RNKeyboard as "React Native Keyboard"
User->>Screen: focus input / type
Screen->>Wrapper: forward ref to input
Wrapper->>Hook: register keyboardDidHide listener (with ref)
User->>RNKeyboard: hide keyboard
RNKeyboard-->>Hook: keyboardDidHide event
Hook->>Wrapper: call ref.current?.blur()
Wrapper->>Screen: input blurred (imperative)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly related PRs
Suggested reviewers
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)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Coverage Report for Expo Unit Tests Coverage (./apps/expo)
File CoverageNo changed files found. |
Coverage Report for API Unit Tests Coverage (./packages/api)
File CoverageNo changed files found. |
There was a problem hiding this comment.
Pull request overview
This PR introduces enhanced Expo TextInput and SearchInput wrapper components that apply an Android keyboard-dismiss blur fix via a new useKeyboardHideBlur hook, and refactors multiple screens to use these wrappers consistently. It also bumps the monorepo version to 2.0.23 and adds supporting Android keyboard-focus documentation/checklists.
Changes:
- Added
useKeyboardHideBlurplus ExpoTextInput/SearchInputwrappers and adopted them across affected screens. - Improved search UI content rendering in Trips / Pack Templates / Guides screens (placeholders, result counts, and empty states).
- Bumped package/app versions to
2.0.23, updated lockfile, and added documentation for the Android keyboard focus issue and prevention.
Reviewed changes
Copilot reviewed 47 out of 48 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
packages/web-ui/package.json |
Version bump to 2.0.23. |
packages/ui/package.json |
Version bump to 2.0.23. |
packages/mcp/package.json |
Version bump to 2.0.23. |
packages/guards/package.json |
Version bump to 2.0.23. |
packages/env/package.json |
Version bump to 2.0.23. |
packages/config/package.json |
Version bump to 2.0.23. |
packages/cli/package.json |
Version bump to 2.0.23. |
packages/checks/package.json |
Version bump to 2.0.23. |
packages/api/package.json |
Version bump to 2.0.23. |
packages/api/container_src/package.json |
Version bump to 2.0.23. |
packages/api-client/package.json |
Version bump to 2.0.23. |
packages/analytics/package.json |
Version bump to 2.0.23. |
package.json |
Monorepo version bump to 2.0.23. |
docs/solutions/ui-bugs/android-textinput-keyboard-focus-loss.md |
New long-form writeup of the Android keyboard focus persistence issue and solution. |
docs/android-textinput-checklist.md |
New checklist for preventing Android keyboard focus persistence regressions. |
docs/android-keyboard-prevention-implementation-summary.md |
New summary doc for the prevention strategy rollout. |
docs/android-keyboard-focus-prevention-strategies.md |
New prevention strategy doc (includes reviewer checklist and testing guidance). |
bun.lock |
Lockfile updated for version bumps and @types/bun updates. |
apps/landing/package.json |
App version bump to 2.0.23. |
apps/guides/package.json |
App version bump to 2.0.23. |
apps/expo/package.json |
App version bump to 2.0.23. |
apps/expo/lib/i18n/locales/en.json |
Added new i18n keys for updated search placeholders and result labels. |
apps/expo/lib/hooks/useKeyboardHideBlur.tsx |
New hook subscribing to keyboardDidHide and blurring the referenced input. |
apps/expo/components/TextInput.tsx |
New enhanced RN TextInput wrapper applying the blur-on-keyboard-hide fix and forwarding refs. |
apps/expo/components/SearchInput.tsx |
New enhanced NativeWindUI SearchInput wrapper applying the blur-on-keyboard-hide fix and forwarding refs. |
apps/expo/features/wildlife/screens/IdentificationScreen.tsx |
Switched to enhanced TextInput wrapper import. |
apps/expo/features/weather/screens/LocationsScreen.tsx |
Switched to enhanced SearchInput wrapper import. |
apps/expo/features/weather/screens/LocationSearchScreen.tsx |
Switched to enhanced SearchInput wrapper import. |
apps/expo/features/trips/screens/TripListScreen.tsx |
Added richer search content (placeholder/results/empty state) for Trips search bar. |
apps/expo/features/trips/components/TripForm.tsx |
Adjusted submit payload shaping (notably packId) when creating/updating trips. |
apps/expo/features/trail-conditions/components/SubmitConditionReportForm.tsx |
Switched to enhanced TextInput wrapper import and consolidated RN imports. |
apps/expo/features/pack-templates/screens/PackTemplateListScreen.tsx |
Added richer search content and placeholder; refactored list header logic. |
apps/expo/features/guides/screens/GuidesListScreen.tsx |
Added richer search content and placeholder; refactored list header logic. |
apps/expo/features/feed/screens/PostDetailScreen.tsx |
Switched to enhanced TextInput wrapper; adjusted ref typing to RN TextInput. |
apps/expo/features/feed/screens/CreatePostScreen.tsx |
Switched to enhanced TextInput wrapper import and consolidated RN imports. |
apps/expo/features/catalog/screens/PackSelectionScreen.tsx |
Switched to enhanced SearchInput wrapper import. |
apps/expo/features/catalog/screens/AddCatalogItemDetailsScreen.tsx |
Switched to enhanced TextInput wrapper import. |
apps/expo/features/catalog/components/CatalogBrowserModal.tsx |
Switched to enhanced SearchInput wrapper import. |
apps/expo/features/ai/components/ReportModal.tsx |
Switched to enhanced TextInput wrapper import and consolidated RN imports. |
apps/expo/features/ai-packs/screens/AIPacksScreen.tsx |
Switched to enhanced TextInput wrapper import and minor formatting adjustments. |
apps/expo/app/auth/one-time-password.tsx |
Applied useKeyboardHideBlur to OTP input field refs. |
apps/expo/app/(app)/trip/location-search.tsx |
Switched to enhanced SearchInput wrapper import. |
apps/expo/app/(app)/textinputdebug.tsx |
Removed the debug screen for the old TextInput behavior. |
apps/expo/app/(app)/messages/chat.tsx |
Switched to enhanced TextInput wrapper import. |
apps/expo/app/(app)/messages/chat.android.tsx |
Switched to enhanced TextInput wrapper import. |
apps/expo/app/(app)/ai-chat.tsx |
Switched to enhanced TextInput wrapper import. |
apps/expo/app.config.ts |
Expo app version bump to 2.0.23. |
apps/admin/package.json |
Admin app version bump to 2.0.23. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Actionable comments posted: 9
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/expo/features/guides/screens/GuidesListScreen.tsx`:
- Around line 135-158: The search results currently render inside a plain
ScrollView in GuidesListScreen which prevents pagination from triggering; update
the UI to use a paginated list (e.g., FlatList) or add an onEndReached handler
that calls useSearchGuides.fetchNextPageSearch when
useSearchGuides.hasNextPageSearch is true, and feed the flattened guides array
from useSearchGuides (pages -> items) as the data source; keep the existing
GuideCard rendering and handleGuidePress, and add a simple footer/loading
indicator while fetchNextPageSearch is loading so more pages load as the user
scrolls.
In `@apps/expo/features/pack-templates/screens/PackTemplateListScreen.tsx`:
- Around line 168-185: listHeader() can return null/undefined during search or
non-"All" tabs but stickyHeaderIndices is still set to [0], causing the first
list item to become sticky; update the component where stickyHeaderIndices is
passed (the FlatList/SectionList usage that references stickyHeaderIndices at
the call site and the other occurrence around the second instance at the
referenced lines) to compute stickyHeaderIndices dynamically — e.g., set
stickyHeaderIndices = listHeader() ? [0] : [] (or undefined) so the header is
only marked sticky when listHeader() actually returns a rendered header.
In `@apps/expo/features/trips/components/TripForm.tsx`:
- Around line 115-119: The submitted payload can include packId as an empty
string because the picker uses '' for "No pack selected"; update the submitData
creation in TripForm (the submitData variable that spreads value) to normalize
empty-string packId to undefined (e.g., detect value.packId === '' and set
packId to undefined, otherwise keep value.packId or nullish fallback) so the API
receives undefined instead of ''. Keep the existing location and nullish
handling unchanged.
In `@apps/expo/lib/hooks/useKeyboardHideBlur.tsx`:
- Around line 10-16: The hook signature for useKeyboardHideBlur should accept
nullable refs so callers can pass useRef<TextInput | null>(null) without
casting; change the parameter type from React.RefObject<{ blur?: () => void }>
to React.RefObject<{ blur?: () => void } | null> (or use React.RefObject<{
blur?: () => void } | null | undefined>) and keep the existing optional chaining
inside the keyboardDidHideCallback and cleanup logic; update any related
types/uses to match the widened signature while leaving function name
useKeyboardHideBlur unchanged.
In `@docs/android-keyboard-focus-prevention-strategies.md`:
- Line 61: The Markdown snippet has an unclosed backtick around the path
`expo-app/components/?` which renders incorrectly; update the checklist item so
the path is wrapped in a proper code span and remove the stray question mark if
unintended (e.g., use `expo-app/components/`), ensuring the reference to the
enhanced TextInput/SearchInput components remains inside the closed backticks
and the checklist line reads clearly.
- Around line 32-41: The example uses useRef<any>(null) which violates the
project's no-any rule; update the ref to a concrete type instead (e.g. change
useRef<any> to useRef<TextInput | null> or useRef<React.ComponentRef<typeof
SomeInputComponent> | null>) and adjust the forwardRef generic if needed
(InputRef/InputProps and the CustomInput/SomeInputComponent references) so
useImperativeHandle(ref, () => inputRef.current) remains type-safe and no any is
used; ensure the useKeyboardHideBlur call still receives the typed inputRef.
- Around line 133-145: The example calls useKeyboardHideBlur conditionally which
violates the Rules of Hooks; update EnhancedTextInput to call
useKeyboardHideBlur unconditionally (e.g., useKeyboardHideBlur(inputRef, {
enabled: autoKeyboardDismiss })) and change the hook signature of
useKeyboardHideBlur to accept an enabled flag and become a no-op when enabled is
false; reference the EnhancedTextInput component, the inputRef variable, the
autoKeyboardDismiss prop, and the useKeyboardHideBlur hook so the hook is always
invoked in the same order while its internal behavior is toggled by the enabled
flag.
In `@docs/android-keyboard-prevention-implementation-summary.md`:
- Around line 47-52: The statement "No direct React Native TextInput imports
found" is stale and should be reworded or removed; update the docs entry under
"Existing Code is Already Compliant" to either state "Migration complete as of
<date/version>" with a date or release tag, or remove the claim entirely so it
doesn't become misleading as the codebase evolves—edit the section referencing
the Enhanced `TextInput`, `SearchInput`, and `useKeyboardHideBlur` to reflect
the chosen approach.
In `@docs/solutions/ui-bugs/android-textinput-keyboard-focus-loss.md`:
- Around line 63-146: Update the docs so the sample hook and components match
the shipped implementations: change the hook parameter type in
useKeyboardHideBlur from React.RefObject<any> to React.RefObject<{ blur?: () =>
void }>, in TextInput use asNonNullableRef(...) when creating the forwarded ref
and call assertPresent(...) inside useImperativeHandle for the internal
textInputRef (matching the real apps/expo/components/TextInput.tsx), and in
SearchInput replace any usages with React.ComponentRef<typeof
NativeWindUISearchInput>, use asNonNullableRef for the forwarded ref and include
assertPresent(...) in useImperativeHandle so the examples no longer use `any`
and mirror the real code.
🪄 Autofix (Beta)
✅ Autofix completed
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 1b1bff08-256c-41c3-876b-638075953b20
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (47)
apps/admin/package.jsonapps/expo/app.config.tsapps/expo/app/(app)/ai-chat.tsxapps/expo/app/(app)/messages/chat.android.tsxapps/expo/app/(app)/messages/chat.tsxapps/expo/app/(app)/textinputdebug.tsxapps/expo/app/(app)/trip/location-search.tsxapps/expo/app/auth/one-time-password.tsxapps/expo/components/SearchInput.tsxapps/expo/components/TextInput.tsxapps/expo/features/ai-packs/screens/AIPacksScreen.tsxapps/expo/features/ai/components/ReportModal.tsxapps/expo/features/catalog/components/CatalogBrowserModal.tsxapps/expo/features/catalog/screens/AddCatalogItemDetailsScreen.tsxapps/expo/features/catalog/screens/PackSelectionScreen.tsxapps/expo/features/feed/screens/CreatePostScreen.tsxapps/expo/features/feed/screens/PostDetailScreen.tsxapps/expo/features/guides/screens/GuidesListScreen.tsxapps/expo/features/pack-templates/screens/PackTemplateListScreen.tsxapps/expo/features/trail-conditions/components/SubmitConditionReportForm.tsxapps/expo/features/trips/components/TripForm.tsxapps/expo/features/trips/screens/TripListScreen.tsxapps/expo/features/weather/screens/LocationSearchScreen.tsxapps/expo/features/weather/screens/LocationsScreen.tsxapps/expo/features/wildlife/screens/IdentificationScreen.tsxapps/expo/lib/hooks/useKeyboardHideBlur.tsxapps/expo/lib/i18n/locales/en.jsonapps/expo/package.jsonapps/guides/package.jsonapps/landing/package.jsondocs/android-keyboard-focus-prevention-strategies.mddocs/android-keyboard-prevention-implementation-summary.mddocs/android-textinput-checklist.mddocs/solutions/ui-bugs/android-textinput-keyboard-focus-loss.mdpackage.jsonpackages/analytics/package.jsonpackages/api-client/package.jsonpackages/api/container_src/package.jsonpackages/api/package.jsonpackages/checks/package.jsonpackages/cli/package.jsonpackages/config/package.jsonpackages/env/package.jsonpackages/guards/package.jsonpackages/mcp/package.jsonpackages/ui/package.jsonpackages/web-ui/package.json
💤 Files with no reviewable changes (1)
- apps/expo/app/(app)/textinputdebug.tsx
…prevention-strategies.md Agent-Logs-Url: https://github.com/PackRat-AI/PackRat/sessions/c75eaa7f-9950-4180-8099-f638817ae495 Co-authored-by: mikib0 <54102880+mikib0@users.noreply.github.com>
|
Note Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it. Fixes Applied SuccessfullyFixed 8 file(s) based on 8 unresolved review comments. Files modified:
Commit: The changes have been pushed to the Time taken: |
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (2)
docs/android-keyboard-focus-prevention-strategies.md (2)
32-34:⚠️ Potential issue | 🟡 MinorReplace
useRef<any>with a concrete ref type in the example.This snippet still documents
useRef<any>(null), which conflicts with the same doc’s guidance at Line 122 and weakens type safety in ref forwarding examples.Suggested doc fix
-export const CustomInput = forwardRef<InputRef, InputProps>((props, ref) => { - const inputRef = useRef<any>(null); +export const CustomInput = forwardRef<InputRef, InputProps>((props, ref) => { + const inputRef = useRef<InputRef>(null);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/android-keyboard-focus-prevention-strategies.md` around lines 32 - 34, The example uses inputRef = useRef<any>(null) which weakens type safety; change it to a concrete ref type that matches the forwarded ref type (e.g., useRef<InputRef | null>(null) or the specific DOM/input type you expect) and ensure CustomInput (the forwardRef component) and its InputRef/InputProps types remain consistent so inputRef has the correct typed shape instead of any.
133-140:⚠️ Potential issue | 🟠 MajorExample violates Rules of Hooks by calling hook conditionally.
useKeyboardHideBluris invoked insideif (autoKeyboardDismiss), which can break hook ordering when the prop changes between renders.Suggested doc fix
export const EnhancedTextInput = forwardRef<TextInput, EnhancedTextInputProps>( ({ autoKeyboardDismiss = true, ...props }, ref) => { const inputRef = useRef<TextInput>(null); - // Conditional application for rare edge cases - if (autoKeyboardDismiss) { - useKeyboardHideBlur(inputRef); - } + // Always call hooks in stable order + useKeyboardHideBlur(autoKeyboardDismiss ? inputRef : { current: null });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/android-keyboard-focus-prevention-strategies.md` around lines 133 - 140, The hook call is conditional and violates the Rules of Hooks; update EnhancedTextInput so useKeyboardHideBlur is invoked unconditionally (always call useKeyboardHideBlur(inputRef, autoKeyboardDismiss) or always call useKeyboardHideBlur(inputRef) and let the hook internally no-op when autoKeyboardDismiss is false) instead of wrapping it in if (autoKeyboardDismiss), keeping inputRef and the prop names (EnhancedTextInput, inputRef, autoKeyboardDismiss, useKeyboardHideBlur) to locate and modify the code.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/android-keyboard-focus-prevention-strategies.md`:
- Around line 69-77: The ESLint example uses invalid rule syntax for
"no-direct-textinput-import"; replace the object with a valid ESLint rule entry
such as "no-direct-textinput-import": "error" or an array like
"no-direct-textinput-import": ["error", { "someOption": true }], and remove the
non-schema keys ("rule" and "message"); if you need a custom error message,
implement it inside the rule definition for no-direct-textinput-import rather
than in the config.
- Around line 209-223: The test is asserting on input.props.onBlur which is not
provided and the hook useKeyboardHideBlur calls ref.current.blur() imperatively;
update the test to mock the input blur method and assert it was invoked: obtain
the rendered element via getByTestId('input'), attach a mock blur function to
the element/ref (e.g., input.ref.current.blur or input.blur depending on test
renderer), invoke the capturedCallback (the keyboard dismissal handler), and
expect the mocked blur toHaveBeenCalled() instead of checking
input.props.onBlur; alternatively, if you want to assert prop callbacks, render
TextInput with an onBlur prop and assert that is called via a proper
fireEvent('blur').
---
Duplicate comments:
In `@docs/android-keyboard-focus-prevention-strategies.md`:
- Around line 32-34: The example uses inputRef = useRef<any>(null) which weakens
type safety; change it to a concrete ref type that matches the forwarded ref
type (e.g., useRef<InputRef | null>(null) or the specific DOM/input type you
expect) and ensure CustomInput (the forwardRef component) and its
InputRef/InputProps types remain consistent so inputRef has the correct typed
shape instead of any.
- Around line 133-140: The hook call is conditional and violates the Rules of
Hooks; update EnhancedTextInput so useKeyboardHideBlur is invoked
unconditionally (always call useKeyboardHideBlur(inputRef, autoKeyboardDismiss)
or always call useKeyboardHideBlur(inputRef) and let the hook internally no-op
when autoKeyboardDismiss is false) instead of wrapping it in if
(autoKeyboardDismiss), keeping inputRef and the prop names (EnhancedTextInput,
inputRef, autoKeyboardDismiss, useKeyboardHideBlur) to locate and modify the
code.
🪄 Autofix (Beta)
✅ Autofix completed
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 74b72293-fe2a-4ff3-b405-8933fa7dd3e6
📒 Files selected for processing (1)
docs/android-keyboard-focus-prevention-strategies.md
Fixed 8 file(s) based on 8 unresolved review comments. Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
|
Note Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it. Fixes Applied SuccessfullyFixed 1 file(s) based on 1 unresolved review comment. Files modified:
Commit: The changes have been pushed to the Time taken: |
Fixed 1 file(s) based on 1 unresolved review comment. Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
There was a problem hiding this comment.
Actionable comments posted: 5
♻️ Duplicate comments (2)
docs/android-keyboard-focus-prevention-strategies.md (2)
192-223:⚠️ Potential issue | 🟠 MajorAssert the imperative
blur()call, not an undefinedonBlurprop.The rendered
TextInputdoes not receive anonBlurmock, and the hook callsref.current.blur()imperatively.expect(input.props.onBlur).toHaveBeenCalled()will throw instead of testing the behavior.Direction for the example
- // Verify blur was called (you may need to mock the blur method) - expect(input.props.onBlur).toHaveBeenCalled(); + // Verify the ref's imperative blur method was called, or test an explicit + // onBlur prop separately with fireEvent(input, 'blur'). + expect(blurMock).toHaveBeenCalled();React Native Testing Library test TextInput blur imperative ref blur method🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/android-keyboard-focus-prevention-strategies.md` around lines 192 - 223, The test is asserting a non-existent onBlur prop instead of the imperative blur call the hook uses; update the __tests__/TextInput.integration.test.tsx to spy on or mock the actual blur implementation and assert it was called when capturedCallback is invoked (for example, spy on the native TextInput blur method or provide a ref-forwarding TextInput that exposes blur), targeting the TextInput component used in the test and the capturedCallback listener so the assertion verifies ref.current.blur() was invoked rather than checking input.props.onBlur.
69-80:⚠️ Potential issue | 🟠 MajorUse valid ESLint rule config syntax.
This is still using the invalid
"rule"/"message"object shape; ESLint rules must be configured as a severity or[severity, options]. Custom messages belong in the rule implementation.Corrected example
{ "rules": { - "no-direct-textinput-import": { - "rule": "error", - "message": "Use enhanced TextInput component instead of direct react-native import" - } + "no-direct-textinput-import": "error" } }ESLint rules configuration schema severity array options custom rule message🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/android-keyboard-focus-prevention-strategies.md` around lines 69 - 80, The ESLint rule snippet uses an invalid object shape with "rule" and "message"; update the "no-direct-textinput-import" entry to use a valid ESLint configuration (a severity string/number or [severity, options]) and remove the "rule" and "message" keys—e.g., set "no-direct-textinput-import": "error" or "no-direct-textinput-import": ["error", { /* options if any */ }], and move any custom message into the rule implementation for the "no-direct-textinput-import" rule.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/expo/features/guides/screens/GuidesListScreen.tsx`:
- Around line 118-176: renderSearchContent renders a FlatList for search results
but the main FlatList is still fed the same guides array, causing duplicate
lists and double-pagination; update the main FlatList so when isSearchMode is
true it does not render or receives an empty data set (or a non-search data
source), and ensure pagination handlers are gated by mode: only call
fetchNextPageSearch/hasNextPageSearch/isFetchingNextPageSearch when
isSearchMode, and only call the main list's pagination when !isSearchMode; look
for renderSearchContent, the main FlatList data={guides} usage, and the
pagination functions
fetchNextPageSearch/hasNextPageSearch/isFetchingNextPageSearch to apply this
conditional wiring.
- Line 10: Remove the unused ScrollView import from the import list in
GuidesListScreen.tsx (the line importing ActivityIndicator, FlatList,
RefreshControl, ScrollView, View) so only actually used symbols remain, then run
the formatter (e.g., biome format --write
apps/expo/features/guides/screens/GuidesListScreen.tsx) to fix the EOF/format
mismatch; verify components like ActivityIndicator, FlatList, RefreshControl,
and View are still imported correctly after the change.
In `@apps/expo/lib/hooks/useKeyboardHideBlur.tsx`:
- Around line 34-38: CI reports a formatting mismatch at the end of
useKeyboardHideBlur; run the Biome formatter to normalize EOF/formatting for
apps/expo/lib/hooks/useKeyboardHideBlur.tsx (e.g., run `biome format --write
apps/expo/lib/hooks/useKeyboardHideBlur.tsx`) and ensure the file ends with a
single trailing newline and the cleanup return using
keyboardDidHideSubscription?.remove() remains unchanged.
In `@docs/android-keyboard-focus-prevention-strategies.md`:
- Around line 33-40: The wrapper forwards a nullable ref (inputRef.current)
directly which fails under strict null checks; update the CustomInput
implementation to import and call assertPresent from `@packrat/guards` to narrow
the nullable inputRef.current before forwarding it via useImperativeHandle and
before passing to SomeInputComponent, e.g., call assertPresent(inputRef.current)
inside the useImperativeHandle callback (and assert before using the ref for any
imperative calls) so inputRef.current is non-null when returned; also ensure
useKeyboardHideBlur(inputRef) remains but that you assert presence where you
forward or use the ref.
In `@docs/solutions/ui-bugs/android-textinput-keyboard-focus-loss.md`:
- Around line 241-246: The docs list incorrectly includes
apps/expo/app/(app)/textinputdebug.tsx under "Files Updated" even though it was
removed; update the PR summary block by taking
apps/expo/app/(app)/textinputdebug.tsx out of the "Files Updated" list and
either place it under a new "Removed Files" section or delete it from the list
entirely so the Files Updated section only contains the actually modified files
(`ai-chat.tsx`, `chat.tsx`, `chat.android.tsx`, `one-time-password.tsx`).
---
Duplicate comments:
In `@docs/android-keyboard-focus-prevention-strategies.md`:
- Around line 192-223: The test is asserting a non-existent onBlur prop instead
of the imperative blur call the hook uses; update the
__tests__/TextInput.integration.test.tsx to spy on or mock the actual blur
implementation and assert it was called when capturedCallback is invoked (for
example, spy on the native TextInput blur method or provide a ref-forwarding
TextInput that exposes blur), targeting the TextInput component used in the test
and the capturedCallback listener so the assertion verifies ref.current.blur()
was invoked rather than checking input.props.onBlur.
- Around line 69-80: The ESLint rule snippet uses an invalid object shape with
"rule" and "message"; update the "no-direct-textinput-import" entry to use a
valid ESLint configuration (a severity string/number or [severity, options]) and
remove the "rule" and "message" keys—e.g., set "no-direct-textinput-import":
"error" or "no-direct-textinput-import": ["error", { /* options if any */ }],
and move any custom message into the rule implementation for the
"no-direct-textinput-import" rule.
🪄 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
Run ID: 60fbecb2-96ee-4518-8ddd-05da343b675e
📒 Files selected for processing (8)
apps/expo/features/guides/screens/GuidesListScreen.tsxapps/expo/features/pack-templates/screens/PackTemplateListScreen.tsxapps/expo/features/trips/components/TripForm.tsxapps/expo/lib/hooks/useKeyboardHideBlur.tsxdocs/android-keyboard-focus-prevention-strategies.mddocs/android-keyboard-prevention-implementation-summary.mddocs/solutions/ui-bugs/android-textinput-keyboard-focus-loss.mdtsconfig.json
✅ Files skipped from review due to trivial changes (1)
- tsconfig.json
🚧 Files skipped from review as they are similar to previous changes (3)
- docs/android-keyboard-prevention-implementation-summary.md
- apps/expo/features/trips/components/TripForm.tsx
- apps/expo/features/pack-templates/screens/PackTemplateListScreen.tsx
Agent-Logs-Url: https://github.com/PackRat-AI/PackRat/sessions/dc3113b9-cce0-4714-9844-ab04fd5624cb Co-authored-by: andrew-bierman <94939237+andrew-bierman@users.noreply.github.com>
There was a problem hiding this comment.
♻️ Duplicate comments (3)
docs/android-keyboard-focus-prevention-strategies.md (2)
217-218:⚠️ Potential issue | 🟠 MajorIntegration test assertion targets the wrong behavior.
useKeyboardHideBlurblurs viaref.current.blur(); it does not guaranteeinput.props.onBlurexists or is called in this test setup. The snippet should mock/assert the imperativeblur()call (or explicitly render withonBlurand usefireEvent('blur')).Suggested fix
- // Verify blur was called (you may need to mock the blur method) - expect(input.props.onBlur).toHaveBeenCalled(); + const blurMock = jest.fn(); + // attach to whichever ref/instance shape your test renderer exposes + (mockRef.current as { blur: () => void }).blur = blurMock; + capturedCallback?.(); + expect(blurMock).toHaveBeenCalled();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/android-keyboard-focus-prevention-strategies.md` around lines 217 - 218, The test is asserting the wrong thing: useKeyboardHideBlur triggers an imperative ref.current.blur() call, not necessarily input.props.onBlur; update the test for the useKeyboardHideBlur behavior by either (A) spying/mocking the input element's blur method and asserting that ref.current.blur() (the element's blur) was called, or (B) explicitly render the input with an onBlur prop and use fireEvent('blur') to simulate and assert the onBlur handler; locate the test referencing input.props.onBlur and adjust it to spy on the DOM element's blur or to render+fireEvent so the assertion matches useKeyboardHideBlur's ref.current.blur() behavior.
33-40:⚠️ Potential issue | 🟠 MajorRef-forwarding examples are not strict-null-safe and diverge from shipped pattern.
These snippets forward nullable refs directly (
inputRef.currentor!), while the actual components useassertPresent(...)andasNonNullableRef(...). Please align docs to the real safe pattern so copied code type-checks under strict null checks.Suggested doc-aligned pattern
+import { assertPresent } from '@packrat/guards'; +import { asNonNullableRef } from 'expo-app/lib/utils/asNonNullableRef'; - useKeyboardHideBlur(inputRef); + useKeyboardHideBlur(asNonNullableRef(inputRef)); - useImperativeHandle(ref, () => inputRef.current); + useImperativeHandle(ref, () => { + assertPresent(inputRef.current); + return inputRef.current; + }, []);Based on learnings: "Enforce strict null checks and no unchecked indexed access in TypeScript".
Also applies to: 49-53, 84-94, 117-119, 131-139
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/android-keyboard-focus-prevention-strategies.md` around lines 33 - 40, The examples forward nullable refs directly (e.g., returning inputRef.current) which fails strict-null checks; update the pattern to assert and convert the internal ref to a non-nullable ref before exposing it: keep inputRef (useRef<TextInput | null>), continue calling useKeyboardHideBlur(inputRef), then use assertPresent(inputRef.current) and asNonNullableRef(ref) (or the project’s equivalent helpers) inside useImperativeHandle to return a guaranteed non-null ref; reference CustomInput, inputRef, useKeyboardHideBlur, useImperativeHandle, assertPresent, and asNonNullableRef when making the change so the doc snippets match shipped code and type-check under strict null checks.apps/expo/features/guides/screens/GuidesListScreen.tsx (1)
212-233:⚠️ Potential issue | 🟠 MajorMain
FlatListstill renders (and paginates) search results concurrently.While
isSearchMode,guidesresolves to the flattenedsearchDatapages (Line 61, 69), and this mainFlatListreceives the exact same array as the one insiderenderSearchContent(Line 137).onEndReachedhere also resolves tofetchNextPageSearchvia the conditional hoists at Lines 65–67, so both lists can trigger pagination on the same query and both render the same rows. Gate the main list on!isSearchMode(or feed it onlyguidesData) so the search surface is the sole owner of search results and pagination.🐛 Suggested direction
- <FlatList - data={guides} + {!isSearchMode && ( + <FlatList + data={data?.pages.flatMap((page) => page.items) || []} keyExtractor={(item) => item.id} renderItem={renderGuide} ListHeaderComponent={listHeader} contentContainerStyle={{ paddingHorizontal: 16, flexGrow: 1 }} refreshControl={ <RefreshControl - refreshing={isRefetching} - onRefresh={refetch} + refreshing={isRefetchingGuides} + onRefresh={refetchGuides} tintColor={colors.primary} /> } onEndReached={() => { - if (hasNextPage && !isFetchingNextPage) { - fetchNextPage(); + if (hasNextPageGuides && !isFetchingNextPageGuides) { + fetchNextPageGuides(); } }} onEndReachedThreshold={0.5} ListFooterComponent={renderFooter} ListEmptyComponent={renderEmpty} - /> + />)}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/expo/features/guides/screens/GuidesListScreen.tsx` around lines 212 - 233, The main FlatList is still used when isSearchMode is true causing duplicate rendering and pagination; modify the FlatList usage so it only renders when !isSearchMode (or pass only guidesData into its data prop) and ensure its onEndReached calls fetchNextPage (not fetchNextPageSearch) by gating the handler; update references around the FlatList component, the guides variable, and the onEndReached callback (and the renderSearchContent/search FlatList that uses fetchNextPageSearch) so the search UI (renderSearchContent) is the sole owner of search results and pagination.
🧹 Nitpick comments (2)
docs/android-keyboard-focus-prevention-strategies.md (1)
19-25: Contradictory import guidance will confuse implementers.Section 1.1 says direct
react-nativeTextInputimports are forbidden, but Section 1.2 immediately demonstrates exactly that. Please clarify the exception (e.g., only allowed inside wrapper implementation files) or update the snippet to consistently use enhanced components.Also applies to: 31-32
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/android-keyboard-focus-prevention-strategies.md` around lines 19 - 25, The snippet is contradictory: it forbids importing TextInput from 'react-native' but the next example shows exactly that; update the examples so they consistently import enhanced components (use expo-app/components/TextInput and SearchInput everywhere) or add a clear inline note stating the exception (e.g., "Allowed only inside wrapper implementation files") and remove the forbidden example; adjust the identical issue at the second instance (lines showing react-native TextInput) so both examples either use TextInput/SearchInput from expo-app/components or include the explicit exception text.apps/expo/features/pack-templates/screens/PackTemplateListScreen.tsx (1)
232-234: MemoizelistHeader()to avoid invoking it twice per render.
listHeader()is called once to decidestickyHeaderIndicesand again forListHeaderComponent. Beyond the redundant work (featured packs + translations + length lookups run twice), a future change that makes the helper non-idempotent (e.g., adds a memoized child oruseRef-like behavior) would cause the two calls to drift. Compute once and reuse.♻️ Proposed refactor
+ {(() => null)()} <FlatList data={filteredTemplates} keyExtractor={(item) => item.id} renderItem={({ item }) => ( <View className="px-4 pt-4"> <PackTemplateCard templateId={item.id} onPress={handleTemplatePress} /> </View> )} - stickyHeaderIndices={listHeader() ? [0] : undefined} + stickyHeaderIndices={headerNode ? [0] : undefined} stickyHeaderHiddenOnScroll - ListHeaderComponent={listHeader()} + ListHeaderComponent={headerNode}And hoist the node near the other derived values:
+ const headerNode = listHeader();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/expo/features/pack-templates/screens/PackTemplateListScreen.tsx` around lines 232 - 234, The component currently calls listHeader() twice (once for stickyHeaderIndices and once for ListHeaderComponent), causing redundant work and risk of drifting behavior; call listHeader() once, store the result in a local constant (e.g., const header = listHeader()) near other derived values, then use header in both places and compute stickyHeaderIndices as header ? [0] : undefined so the header node is created only once and reused for ListHeaderComponent and stickyHeaderIndices.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@apps/expo/features/guides/screens/GuidesListScreen.tsx`:
- Around line 212-233: The main FlatList is still used when isSearchMode is true
causing duplicate rendering and pagination; modify the FlatList usage so it only
renders when !isSearchMode (or pass only guidesData into its data prop) and
ensure its onEndReached calls fetchNextPage (not fetchNextPageSearch) by gating
the handler; update references around the FlatList component, the guides
variable, and the onEndReached callback (and the renderSearchContent/search
FlatList that uses fetchNextPageSearch) so the search UI (renderSearchContent)
is the sole owner of search results and pagination.
In `@docs/android-keyboard-focus-prevention-strategies.md`:
- Around line 217-218: The test is asserting the wrong thing:
useKeyboardHideBlur triggers an imperative ref.current.blur() call, not
necessarily input.props.onBlur; update the test for the useKeyboardHideBlur
behavior by either (A) spying/mocking the input element's blur method and
asserting that ref.current.blur() (the element's blur) was called, or (B)
explicitly render the input with an onBlur prop and use fireEvent('blur') to
simulate and assert the onBlur handler; locate the test referencing
input.props.onBlur and adjust it to spy on the DOM element's blur or to
render+fireEvent so the assertion matches useKeyboardHideBlur's
ref.current.blur() behavior.
- Around line 33-40: The examples forward nullable refs directly (e.g.,
returning inputRef.current) which fails strict-null checks; update the pattern
to assert and convert the internal ref to a non-nullable ref before exposing it:
keep inputRef (useRef<TextInput | null>), continue calling
useKeyboardHideBlur(inputRef), then use assertPresent(inputRef.current) and
asNonNullableRef(ref) (or the project’s equivalent helpers) inside
useImperativeHandle to return a guaranteed non-null ref; reference CustomInput,
inputRef, useKeyboardHideBlur, useImperativeHandle, assertPresent, and
asNonNullableRef when making the change so the doc snippets match shipped code
and type-check under strict null checks.
---
Nitpick comments:
In `@apps/expo/features/pack-templates/screens/PackTemplateListScreen.tsx`:
- Around line 232-234: The component currently calls listHeader() twice (once
for stickyHeaderIndices and once for ListHeaderComponent), causing redundant
work and risk of drifting behavior; call listHeader() once, store the result in
a local constant (e.g., const header = listHeader()) near other derived values,
then use header in both places and compute stickyHeaderIndices as header ? [0] :
undefined so the header node is created only once and reused for
ListHeaderComponent and stickyHeaderIndices.
In `@docs/android-keyboard-focus-prevention-strategies.md`:
- Around line 19-25: The snippet is contradictory: it forbids importing
TextInput from 'react-native' but the next example shows exactly that; update
the examples so they consistently import enhanced components (use
expo-app/components/TextInput and SearchInput everywhere) or add a clear inline
note stating the exception (e.g., "Allowed only inside wrapper implementation
files") and remove the forbidden example; adjust the identical issue at the
second instance (lines showing react-native TextInput) so both examples either
use TextInput/SearchInput from expo-app/components or include the explicit
exception text.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 4ffdc6be-6273-4950-8838-6f5bf17f110d
📒 Files selected for processing (6)
apps/expo/features/guides/screens/GuidesListScreen.tsxapps/expo/features/pack-templates/screens/PackTemplateListScreen.tsxapps/expo/features/trips/components/TripForm.tsxapps/expo/lib/hooks/useKeyboardHideBlur.tsxdocs/android-keyboard-focus-prevention-strategies.mdtsconfig.json
✅ Files skipped from review due to trivial changes (1)
- tsconfig.json
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/expo/lib/hooks/useKeyboardHideBlur.tsx
TypeScript 5.9.3 (pinned in bun.lock) rejects "6.0" as an invalid value for ignoreDeprecations, causing TS5103 errors in CI. The "6.0" value was added to silence the baseUrl deprecation warning which only appears in TypeScript 6.x, not in the 5.9.x series used by CI. https://claude.ai/code/session_01SypcK5vavfiCGcM8BPtTmY
fix: revert ignoreDeprecations to "5.0" to fix TS5103 in CI
This pull request introduces enhanced custom input components to the Expo app, specifically new
TextInputandSearchInputwrappers that automatically handle Android keyboard blur issues. The changes also refactor the codebase to consistently use these new components throughout the app, replacing direct usage of React Native's and NativeWindUI's input components. Additionally, the PR includes minor version bumps for both the admin and Expo apps.Input component enhancements and refactor:
TextInputandSearchInputcomponents inexpo-app/components, which wrap the original input components and apply a keyboard blur fix for Android. These components use theuseKeyboardHideBlurhook and forward refs correctly. (apps/expo/components/TextInput.tsx[1]apps/expo/components/SearchInput.tsx[2]TextInputandSearchInputacross the Expo app to use the new components instead of importing fromreact-nativeor@packrat/ui/nativewindui. This includes updates in chat screens, feed screens, catalog screens, AI features, and others. [1] [2] [3] [4] [5] [6] [7] [8] F02a4ca1L1, [9] [10] [11] [12] [13] [14] [15] [16] [17]Bug fixes and improvements:
useKeyboardHideBlurhook into OTP and search input flows to improve keyboard handling on Android and avoid UI glitches when the keyboard is dismissed. (apps/expo/app/auth/one-time-password.tsx[1] [2]textinputdebug.tsxfile, which was previously used for debugging the oldTextInputcomponent. (apps/expo/app/(app)/textinputdebug.tsxapps/expo/app/(app)/textinputdebug.tsxL1-L13)Version updates:
2.0.22to2.0.23. (apps/admin/package.jsonapps/admin/package.jsonL3-R3)2.0.22to2.0.23. (apps/expo/app.config.tsapps/expo/app.config.tsL40-R40)Guide and catalog UI improvements:
apps/expo/features/guides/screens/GuidesListScreen.tsx[1] [2]These changes improve the maintainability and consistency of input handling across the app, especially addressing Android-specific keyboard issues.
Summary by CodeRabbit
New Features
Bug Fixes
Documentation
Chores