Skip to content

Release/2.0.23#2271

Merged
mikib0 merged 25 commits into
mainfrom
release/2.0.23
Apr 25, 2026
Merged

Release/2.0.23#2271
mikib0 merged 25 commits into
mainfrom
release/2.0.23

Conversation

@mikib0
Copy link
Copy Markdown
Collaborator

@mikib0 mikib0 commented Apr 23, 2026

This pull request introduces enhanced custom input components to the Expo app, specifically new TextInput and SearchInput wrappers 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:

  • Introduced TextInput and SearchInput components in expo-app/components, which wrap the original input components and apply a keyboard blur fix for Android. These components use the useKeyboardHideBlur hook and forward refs correctly. (apps/expo/components/TextInput.tsx [1] apps/expo/components/SearchInput.tsx [2]
  • Refactored all usage of TextInput and SearchInput across the Expo app to use the new components instead of importing from react-native or @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:

  • Integrated the useKeyboardHideBlur hook 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]
  • Removed the textinputdebug.tsx file, which was previously used for debugging the old TextInput component. (apps/expo/app/(app)/textinputdebug.tsx apps/expo/app/(app)/textinputdebug.tsxL1-L13)

Version updates:

Guide and catalog UI improvements:

  • Updated guide and catalog screens to use the new input components and improved ref handling for search bars. (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

    • Improved in-app search UX across trips, guides, and templates with inline results, counts, and better empty states; unified input components used throughout the app.
  • Bug Fixes

    • Android keyboard focus behavior fixed so inputs reliably blur when the keyboard is dismissed.
  • Documentation

    • Added comprehensive Android keyboard handling guidance, checklists, and migration notes.
  • Chores

    • Project packages/apps version bumped to 2.0.23; TypeScript config adjusted.

Copilot AI and others added 18 commits April 20, 2026 14:53
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>
…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
@github-actions github-actions Bot added documentation Improvements or additions to documentation dependencies Pull requests that update a dependency file api mobile web labels Apr 23, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 23, 2026

📝 Walkthrough

Walkthrough

Adds 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

Cohort / File(s) Summary
Version Bumps
package.json, apps/admin/package.json, apps/expo/package.json, apps/expo/app.config.ts, apps/guides/package.json, apps/landing/package.json, packages/analytics/package.json, packages/api-client/package.json, packages/api/container_src/package.json, packages/api/package.json, packages/checks/package.json, packages/cli/package.json, packages/config/package.json, packages/env/package.json, packages/guards/package.json, packages/mcp/package.json, packages/ui/package.json, packages/web-ui/package.json
Bumped package/app versions from 2.0.222.0.23.
Keyboard Focus Fix Infrastructure
apps/expo/lib/hooks/useKeyboardHideBlur.tsx, apps/expo/components/TextInput.tsx, apps/expo/components/SearchInput.tsx
Added useKeyboardHideBlur hook and two ref-forwarding wrapper components that apply the hook and forward imperative refs.
TextInput Migration
apps/expo/app/(app)/ai-chat.tsx, apps/expo/app/(app)/messages/chat.android.tsx, apps/expo/app/(app)/messages/chat.tsx, apps/expo/features/ai-packs/screens/AIPacksScreen.tsx, apps/expo/features/ai/components/ReportModal.tsx, apps/expo/features/catalog/screens/AddCatalogItemDetailsScreen.tsx, apps/expo/features/feed/screens/CreatePostScreen.tsx, apps/expo/features/feed/screens/PostDetailScreen.tsx, apps/expo/features/trail-conditions/components/SubmitConditionReportForm.tsx, apps/expo/features/wildlife/screens/IdentificationScreen.tsx
Replaced direct react-native TextInput imports with the new expo-app/components/TextInput wrapper.
SearchInput Migration
apps/expo/app/(app)/trip/location-search.tsx, apps/expo/features/catalog/components/CatalogBrowserModal.tsx, apps/expo/features/catalog/screens/PackSelectionScreen.tsx, apps/expo/features/weather/screens/LocationSearchScreen.tsx, apps/expo/features/weather/screens/LocationsScreen.tsx
Replaced @packrat/ui/nativewindui SearchInput imports with local expo-app/components/SearchInput.
Search UX Improvements
apps/expo/features/guides/screens/GuidesListScreen.tsx, apps/expo/features/pack-templates/screens/PackTemplateListScreen.tsx, apps/expo/features/trips/screens/TripListScreen.tsx
Embedded stateful/scrollable search content into LargeTitleHeader: localized placeholders, result counts, mapped results lists, empty states, and adjusted header/list behavior for search mode.
OTP & Form Changes
apps/expo/app/auth/one-time-password.tsx, apps/expo/features/trips/components/TripForm.tsx
OTPField now uses useKeyboardHideBlur with a non-nullable ref; TripForm normalizes empty packId to undefined before submit.
Cleanup & Localization
apps/expo/app/(app)/textinputdebug.tsx, apps/expo/lib/i18n/locales/en.json
Removed a debug screen; added English localization keys for search placeholders and singular/plural result messaging.
Documentation
docs/android-keyboard-focus-prevention-strategies.md, docs/android-keyboard-prevention-implementation-summary.md, docs/android-textinput-checklist.md, docs/solutions/ui-bugs/android-textinput-keyboard-focus-loss.md
New docs detailing the hook/wrappers, integration patterns, PR checklist, testing guidance, migration plan, and monitoring/rollback recommendations.

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)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Suggested reviewers

  • Isthisanmol

Poem

🐰 I hopped to blur the keyboard's sting,
Wrapped inputs snug and taught them to spring.
When Android hides, I give a gentle nudge—
A tiny blur, no more focus grudge.
Now taps return, and I hop with a grin.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Release/2.0.23' accurately summarizes the primary change: a version bump release. It is concise, clear, and directly reflects the main objective of the pull request.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch release/2.0.23

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

❤️ Share

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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 23, 2026

Coverage Report for Expo Unit Tests Coverage (./apps/expo)

Status Category Percentage Covered / Total
🔵 Lines 79.9% 517 / 647
🔵 Statements 79.9% (🎯 75%) 517 / 647
🔵 Functions 92.85% 52 / 56
🔵 Branches 92.55% 199 / 215
File CoverageNo changed files found.
Generated in workflow #735 for commit 7f5fe68 by the Vitest Coverage Report Action

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 23, 2026

Coverage Report for API Unit Tests Coverage (./packages/api)

Status Category Percentage Covered / Total
🔵 Lines 85.86% 905 / 1054
🔵 Statements 85.86% (🎯 80%) 905 / 1054
🔵 Functions 94.11% 48 / 51
🔵 Branches 89.2% 281 / 315
File CoverageNo changed files found.
Generated in workflow #735 for commit 7f5fe68 by the Vitest Coverage Report Action

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

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 useKeyboardHideBlur plus Expo TextInput/SearchInput wrappers 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.

Comment thread apps/expo/features/trips/components/TripForm.tsx
Comment thread apps/expo/features/pack-templates/screens/PackTemplateListScreen.tsx Outdated
Comment thread docs/android-keyboard-focus-prevention-strategies.md Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 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

📥 Commits

Reviewing files that changed from the base of the PR and between a49d8a0 and cd651ba.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (47)
  • apps/admin/package.json
  • apps/expo/app.config.ts
  • apps/expo/app/(app)/ai-chat.tsx
  • apps/expo/app/(app)/messages/chat.android.tsx
  • apps/expo/app/(app)/messages/chat.tsx
  • apps/expo/app/(app)/textinputdebug.tsx
  • apps/expo/app/(app)/trip/location-search.tsx
  • apps/expo/app/auth/one-time-password.tsx
  • apps/expo/components/SearchInput.tsx
  • apps/expo/components/TextInput.tsx
  • apps/expo/features/ai-packs/screens/AIPacksScreen.tsx
  • apps/expo/features/ai/components/ReportModal.tsx
  • apps/expo/features/catalog/components/CatalogBrowserModal.tsx
  • apps/expo/features/catalog/screens/AddCatalogItemDetailsScreen.tsx
  • apps/expo/features/catalog/screens/PackSelectionScreen.tsx
  • apps/expo/features/feed/screens/CreatePostScreen.tsx
  • apps/expo/features/feed/screens/PostDetailScreen.tsx
  • apps/expo/features/guides/screens/GuidesListScreen.tsx
  • apps/expo/features/pack-templates/screens/PackTemplateListScreen.tsx
  • apps/expo/features/trail-conditions/components/SubmitConditionReportForm.tsx
  • apps/expo/features/trips/components/TripForm.tsx
  • apps/expo/features/trips/screens/TripListScreen.tsx
  • apps/expo/features/weather/screens/LocationSearchScreen.tsx
  • apps/expo/features/weather/screens/LocationsScreen.tsx
  • apps/expo/features/wildlife/screens/IdentificationScreen.tsx
  • apps/expo/lib/hooks/useKeyboardHideBlur.tsx
  • apps/expo/lib/i18n/locales/en.json
  • apps/expo/package.json
  • apps/guides/package.json
  • apps/landing/package.json
  • docs/android-keyboard-focus-prevention-strategies.md
  • docs/android-keyboard-prevention-implementation-summary.md
  • docs/android-textinput-checklist.md
  • docs/solutions/ui-bugs/android-textinput-keyboard-focus-loss.md
  • package.json
  • packages/analytics/package.json
  • packages/api-client/package.json
  • packages/api/container_src/package.json
  • packages/api/package.json
  • packages/checks/package.json
  • packages/cli/package.json
  • packages/config/package.json
  • packages/env/package.json
  • packages/guards/package.json
  • packages/mcp/package.json
  • packages/ui/package.json
  • packages/web-ui/package.json
💤 Files with no reviewable changes (1)
  • apps/expo/app/(app)/textinputdebug.tsx

Comment thread apps/expo/features/guides/screens/GuidesListScreen.tsx Outdated
Comment thread apps/expo/features/trips/components/TripForm.tsx
Comment thread apps/expo/lib/hooks/useKeyboardHideBlur.tsx Outdated
Comment thread docs/android-keyboard-focus-prevention-strategies.md
Comment thread docs/android-keyboard-focus-prevention-strategies.md Outdated
Comment thread docs/android-keyboard-focus-prevention-strategies.md
Comment thread docs/android-keyboard-prevention-implementation-summary.md
Comment thread docs/solutions/ui-bugs/android-textinput-keyboard-focus-loss.md
…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>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 23, 2026

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

Fixes Applied Successfully

Fixed 8 file(s) based on 8 unresolved review comments.

Files modified:

  • apps/expo/features/guides/screens/GuidesListScreen.tsx
  • apps/expo/features/pack-templates/screens/PackTemplateListScreen.tsx
  • apps/expo/features/trips/components/TripForm.tsx
  • apps/expo/lib/hooks/useKeyboardHideBlur.tsx
  • docs/android-keyboard-focus-prevention-strategies.md
  • docs/android-keyboard-prevention-implementation-summary.md
  • docs/solutions/ui-bugs/android-textinput-keyboard-focus-loss.md
  • tsconfig.json

Commit: 831a25fc0ca1534e8cc8f2b6102490cf0d3a39d1

The changes have been pushed to the release/2.0.23 branch.

Time taken: 4m 33s

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (2)
docs/android-keyboard-focus-prevention-strategies.md (2)

32-34: ⚠️ Potential issue | 🟡 Minor

Replace 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 | 🟠 Major

Example violates Rules of Hooks by calling hook conditionally.

useKeyboardHideBlur is invoked inside if (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

📥 Commits

Reviewing files that changed from the base of the PR and between cd651ba and 85b6883.

📒 Files selected for processing (1)
  • docs/android-keyboard-focus-prevention-strategies.md

Comment thread docs/android-keyboard-focus-prevention-strategies.md
Comment thread docs/android-keyboard-focus-prevention-strategies.md
Fixed 8 file(s) based on 8 unresolved review comments.

Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 23, 2026

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

Fixes Applied Successfully

Fixed 1 file(s) based on 1 unresolved review comment.

Files modified:

  • docs/android-keyboard-focus-prevention-strategies.md

Commit: eae1a33b9f387feae35ea8769744891b303e3094

The changes have been pushed to the release/2.0.23 branch.

Time taken: 1m 39s

Fixed 1 file(s) based on 1 unresolved review comment.

Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

♻️ Duplicate comments (2)
docs/android-keyboard-focus-prevention-strategies.md (2)

192-223: ⚠️ Potential issue | 🟠 Major

Assert the imperative blur() call, not an undefined onBlur prop.

The rendered TextInput does not receive an onBlur mock, and the hook calls ref.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 | 🟠 Major

Use 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

📥 Commits

Reviewing files that changed from the base of the PR and between 85b6883 and 831a25f.

📒 Files selected for processing (8)
  • apps/expo/features/guides/screens/GuidesListScreen.tsx
  • apps/expo/features/pack-templates/screens/PackTemplateListScreen.tsx
  • apps/expo/features/trips/components/TripForm.tsx
  • apps/expo/lib/hooks/useKeyboardHideBlur.tsx
  • docs/android-keyboard-focus-prevention-strategies.md
  • docs/android-keyboard-prevention-implementation-summary.md
  • docs/solutions/ui-bugs/android-textinput-keyboard-focus-loss.md
  • tsconfig.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

Comment thread apps/expo/features/guides/screens/GuidesListScreen.tsx Outdated
Comment thread apps/expo/features/guides/screens/GuidesListScreen.tsx
Comment thread apps/expo/lib/hooks/useKeyboardHideBlur.tsx Outdated
Comment thread docs/android-keyboard-focus-prevention-strategies.md
Comment thread docs/solutions/ui-bugs/android-textinput-keyboard-focus-loss.md
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (3)
docs/android-keyboard-focus-prevention-strategies.md (2)

217-218: ⚠️ Potential issue | 🟠 Major

Integration test assertion targets the wrong behavior.

useKeyboardHideBlur blurs via ref.current.blur(); it does not guarantee input.props.onBlur exists or is called in this test setup. The snippet should mock/assert the imperative blur() call (or explicitly render with onBlur and use fireEvent('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 | 🟠 Major

Ref-forwarding examples are not strict-null-safe and diverge from shipped pattern.

These snippets forward nullable refs directly (inputRef.current or !), while the actual components use assertPresent(...) and asNonNullableRef(...). 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 | 🟠 Major

Main FlatList still renders (and paginates) search results concurrently.

While isSearchMode, guides resolves to the flattened searchData pages (Line 61, 69), and this main FlatList receives the exact same array as the one inside renderSearchContent (Line 137). onEndReached here also resolves to fetchNextPageSearch via 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 only guidesData) 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-native TextInput imports 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: Memoize listHeader() to avoid invoking it twice per render.

listHeader() is called once to decide stickyHeaderIndices and again for ListHeaderComponent. 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 or useRef-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

📥 Commits

Reviewing files that changed from the base of the PR and between 831a25f and 19709b2.

📒 Files selected for processing (6)
  • apps/expo/features/guides/screens/GuidesListScreen.tsx
  • apps/expo/features/pack-templates/screens/PackTemplateListScreen.tsx
  • apps/expo/features/trips/components/TripForm.tsx
  • apps/expo/lib/hooks/useKeyboardHideBlur.tsx
  • docs/android-keyboard-focus-prevention-strategies.md
  • tsconfig.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

@mikib0
Copy link
Copy Markdown
Collaborator Author

mikib0 commented Apr 24, 2026

@copilot fix failing type check https://github.com/PackRat-AI/PackRat/actions/runs/24871478515/job/72818711250?pr=2271

claude and others added 2 commits April 24, 2026 21:15
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
@mikib0 mikib0 merged commit 72aa953 into main Apr 25, 2026
10 of 12 checks passed
@mikib0 mikib0 deleted the release/2.0.23 branch April 25, 2026 06:30
andrew-bierman pushed a commit that referenced this pull request May 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation mobile web

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants