From b35c63fa4ec5e91f89797c5fe3fc1fcd6b036143 Mon Sep 17 00:00:00 2001 From: Ibrahim Isa Jajere Date: Thu, 23 Apr 2026 08:26:54 +0100 Subject: [PATCH 1/7] chore: bump version to v2.0.23 --- apps/admin/package.json | 2 +- apps/expo/app.config.ts | 2 +- apps/expo/package.json | 2 +- apps/guides/package.json | 2 +- apps/landing/package.json | 2 +- package.json | 2 +- packages/analytics/package.json | 2 +- packages/api-client/package.json | 2 +- packages/api/container_src/package.json | 2 +- packages/api/package.json | 2 +- packages/checks/package.json | 2 +- packages/cli/package.json | 2 +- packages/config/package.json | 2 +- packages/env/package.json | 2 +- packages/guards/package.json | 2 +- packages/mcp/package.json | 2 +- packages/ui/package.json | 2 +- packages/web-ui/package.json | 2 +- 18 files changed, 18 insertions(+), 18 deletions(-) diff --git a/apps/admin/package.json b/apps/admin/package.json index 8a5e7a5c1d..7f55e37531 100644 --- a/apps/admin/package.json +++ b/apps/admin/package.json @@ -1,6 +1,6 @@ { "name": "packrat-admin-app", - "version": "2.0.22", + "version": "2.0.23", "private": true, "scripts": { "build": "next build", diff --git a/apps/expo/app.config.ts b/apps/expo/app.config.ts index 9ed48acde2..ea6c39bc6d 100644 --- a/apps/expo/app.config.ts +++ b/apps/expo/app.config.ts @@ -37,7 +37,7 @@ export default (): ExpoConfig => { name: getAppName(), slug: 'packrat', - version: '2.0.22', + version: '2.0.23', scheme: 'packrat', web: { bundler: 'metro', diff --git a/apps/expo/package.json b/apps/expo/package.json index 9c2655ee02..f6cf4023a7 100644 --- a/apps/expo/package.json +++ b/apps/expo/package.json @@ -1,6 +1,6 @@ { "name": "packrat-expo-app", - "version": "2.0.22", + "version": "2.0.23", "private": true, "main": "expo-router/entry", "scripts": { diff --git a/apps/guides/package.json b/apps/guides/package.json index 064d02cb51..ff88d1fbb8 100644 --- a/apps/guides/package.json +++ b/apps/guides/package.json @@ -1,6 +1,6 @@ { "name": "packrat-guides-app", - "version": "2.0.22", + "version": "2.0.23", "private": true, "scripts": { "build": "bun run build-content && next build", diff --git a/apps/landing/package.json b/apps/landing/package.json index 7819a6e559..a83bb1fc33 100644 --- a/apps/landing/package.json +++ b/apps/landing/package.json @@ -1,6 +1,6 @@ { "name": "packrat-landing-app", - "version": "2.0.22", + "version": "2.0.23", "private": true, "scripts": { "build": "next build", diff --git a/package.json b/package.json index 67ca422219..b7b940626b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "packrat-monorepo", - "version": "2.0.22", + "version": "2.0.23", "workspaces": [ "apps/*", "packages/*" diff --git a/packages/analytics/package.json b/packages/analytics/package.json index acc2397bd3..deada916c8 100644 --- a/packages/analytics/package.json +++ b/packages/analytics/package.json @@ -1,6 +1,6 @@ { "name": "@packrat/analytics", - "version": "2.0.22", + "version": "2.0.23", "private": true, "type": "module", "scripts": { diff --git a/packages/api-client/package.json b/packages/api-client/package.json index f404be3a77..460e3a5584 100644 --- a/packages/api-client/package.json +++ b/packages/api-client/package.json @@ -1,6 +1,6 @@ { "name": "@packrat/api-client", - "version": "2.0.22", + "version": "2.0.23", "private": true, "description": "PackRat typed API client — authenticated HTTP client with error handling and MCP result helpers", "type": "module", diff --git a/packages/api/container_src/package.json b/packages/api/container_src/package.json index 01b7d02499..fce743949b 100644 --- a/packages/api/container_src/package.json +++ b/packages/api/container_src/package.json @@ -1,6 +1,6 @@ { "name": "container", - "version": "2.0.22", + "version": "2.0.23", "type": "module", "dependencies": { "@aws-sdk/client-s3": "^3.0.0", diff --git a/packages/api/package.json b/packages/api/package.json index 8fc2527c4b..780335248e 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -1,6 +1,6 @@ { "name": "@packrat/api", - "version": "2.0.22", + "version": "2.0.23", "scripts": { "check-types": "tsc --noEmit", "check-types-watch": "tsc --noEmit --watch", diff --git a/packages/checks/package.json b/packages/checks/package.json index 3ef42b55dd..901d72f3a3 100644 --- a/packages/checks/package.json +++ b/packages/checks/package.json @@ -1,6 +1,6 @@ { "name": "@packrat/checks", - "version": "2.0.22", + "version": "2.0.23", "private": true, "type": "module", "scripts": { diff --git a/packages/cli/package.json b/packages/cli/package.json index cc504e6ed4..2da074fafe 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@packrat/cli", - "version": "2.0.22", + "version": "2.0.23", "private": true, "type": "module", "bin": { diff --git a/packages/config/package.json b/packages/config/package.json index 92fa2d1ad1..c61eae76ee 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -1,6 +1,6 @@ { "name": "@packrat/config", - "version": "2.0.22", + "version": "2.0.23", "private": true, "type": "module", "exports": { diff --git a/packages/env/package.json b/packages/env/package.json index 89685a0bdb..7b33a175f8 100644 --- a/packages/env/package.json +++ b/packages/env/package.json @@ -1,6 +1,6 @@ { "name": "@packrat/env", - "version": "2.0.22", + "version": "2.0.23", "private": true, "type": "module", "exports": { diff --git a/packages/guards/package.json b/packages/guards/package.json index ea7faa6479..7d5140be8d 100644 --- a/packages/guards/package.json +++ b/packages/guards/package.json @@ -1,6 +1,6 @@ { "name": "@packrat/guards", - "version": "2.0.22", + "version": "2.0.23", "private": true, "type": "module", "exports": { diff --git a/packages/mcp/package.json b/packages/mcp/package.json index b80681b7f7..24afef7754 100644 --- a/packages/mcp/package.json +++ b/packages/mcp/package.json @@ -1,6 +1,6 @@ { "name": "@packrat/mcp", - "version": "2.0.22", + "version": "2.0.23", "private": true, "description": "PackRat MCP Server — outdoor adventure planning via Model Context Protocol", "scripts": { diff --git a/packages/ui/package.json b/packages/ui/package.json index be3b727eac..b9ff4b6041 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@packrat/ui", - "version": "2.0.22", + "version": "2.0.23", "private": true, "dependencies": { "@packrat-ai/nativewindui": "^2.0.2" diff --git a/packages/web-ui/package.json b/packages/web-ui/package.json index 4a607ca5cc..08a2b64450 100644 --- a/packages/web-ui/package.json +++ b/packages/web-ui/package.json @@ -1,6 +1,6 @@ { "name": "@packrat/web-ui", - "version": "2.0.22", + "version": "2.0.23", "private": true, "type": "module", "exports": { From cd651bace194b00bd136429015a20c79981455d6 Mon Sep 17 00:00:00 2001 From: Ibrahim Isa Jajere Date: Thu, 23 Apr 2026 08:32:17 +0100 Subject: [PATCH 2/7] chore: update bun lockfile --- bun.lock | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/bun.lock b/bun.lock index 63be0cdbea..7103e09422 100644 --- a/bun.lock +++ b/bun.lock @@ -19,7 +19,7 @@ }, "apps/admin": { "name": "packrat-admin-app", - "version": "2.0.22", + "version": "2.0.23", "dependencies": { "@packrat/web-ui": "workspace:*", "@radix-ui/react-alert-dialog": "catalog:", @@ -59,7 +59,7 @@ }, "apps/expo": { "name": "packrat-expo-app", - "version": "2.0.22", + "version": "2.0.23", "dependencies": { "@ai-sdk/react": "^2.0.11", "@expo/react-native-action-sheet": "^4.1.1", @@ -182,7 +182,7 @@ }, "apps/guides": { "name": "packrat-guides-app", - "version": "2.0.22", + "version": "2.0.23", "dependencies": { "@ai-sdk/openai": "^2.0.11", "@hookform/resolvers": "^3.10.0", @@ -265,7 +265,7 @@ }, "apps/landing": { "name": "packrat-landing-app", - "version": "2.0.22", + "version": "2.0.23", "dependencies": { "@emotion/is-prop-valid": "^1.3.1", "@hookform/resolvers": "^3.10.0", @@ -330,7 +330,7 @@ }, "packages/analytics": { "name": "@packrat/analytics", - "version": "2.0.22", + "version": "2.0.23", "dependencies": { "@duckdb/node-api": "1.5.0-r.1", "@packrat/env": "workspace:*", @@ -346,7 +346,7 @@ }, "packages/api": { "name": "@packrat/api", - "version": "2.0.22", + "version": "2.0.23", "dependencies": { "@ai-sdk/google": "^2.0.62", "@ai-sdk/openai": "^2.0.11", @@ -400,18 +400,18 @@ }, "packages/api-client": { "name": "@packrat/api-client", - "version": "2.0.22", + "version": "2.0.23", "devDependencies": { "typescript": "catalog:", }, }, "packages/checks": { "name": "@packrat/checks", - "version": "2.0.22", + "version": "2.0.23", }, "packages/cli": { "name": "@packrat/cli", - "version": "2.0.22", + "version": "2.0.23", "bin": { "packrat": "./src/index.ts", }, @@ -431,25 +431,25 @@ }, "packages/config": { "name": "@packrat/config", - "version": "2.0.22", + "version": "2.0.23", }, "packages/env": { "name": "@packrat/env", - "version": "2.0.22", + "version": "2.0.23", "dependencies": { "zod": "catalog:", }, }, "packages/guards": { "name": "@packrat/guards", - "version": "2.0.22", + "version": "2.0.23", "dependencies": { "radash": "catalog:", }, }, "packages/mcp": { "name": "@packrat/mcp", - "version": "2.0.22", + "version": "2.0.23", "dependencies": { "@modelcontextprotocol/sdk": "^1.11.0", "@packrat/api-client": "workspace:*", @@ -467,14 +467,14 @@ }, "packages/ui": { "name": "@packrat/ui", - "version": "2.0.22", + "version": "2.0.23", "dependencies": { "@packrat-ai/nativewindui": "^2.0.2", }, }, "packages/web-ui": { "name": "@packrat/web-ui", - "version": "2.0.22", + "version": "2.0.23", "dependencies": { "@packrat/guards": "workspace:*", "@radix-ui/react-accordion": "catalog:", @@ -4406,11 +4406,11 @@ "@modelcontextprotocol/sdk/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], - "@packrat/analytics/@types/bun": ["@types/bun@1.3.12", "", { "dependencies": { "bun-types": "1.3.12" } }, "sha512-DBv81elK+/VSwXHDlnH3Qduw+KxkTIWi7TXkAeh24zpi5l0B2kUg9Ga3tb4nJaPcOFswflgi/yAvMVBPrxMB+A=="], + "@packrat/analytics/@types/bun": ["@types/bun@1.3.13", "", { "dependencies": { "bun-types": "1.3.13" } }, "sha512-9fqXWk5YIHGGnUau9TEi+qdlTYDAnOj+xLCmSTwXfAIqXr2x4tytJb43E9uCvt09zJURKXwAtkoH4nLQfzeTXw=="], - "@packrat/api/@types/bun": ["@types/bun@1.3.12", "", { "dependencies": { "bun-types": "1.3.12" } }, "sha512-DBv81elK+/VSwXHDlnH3Qduw+KxkTIWi7TXkAeh24zpi5l0B2kUg9Ga3tb4nJaPcOFswflgi/yAvMVBPrxMB+A=="], + "@packrat/api/@types/bun": ["@types/bun@1.3.13", "", { "dependencies": { "bun-types": "1.3.13" } }, "sha512-9fqXWk5YIHGGnUau9TEi+qdlTYDAnOj+xLCmSTwXfAIqXr2x4tytJb43E9uCvt09zJURKXwAtkoH4nLQfzeTXw=="], - "@packrat/cli/@types/bun": ["@types/bun@1.3.12", "", { "dependencies": { "bun-types": "1.3.12" } }, "sha512-DBv81elK+/VSwXHDlnH3Qduw+KxkTIWi7TXkAeh24zpi5l0B2kUg9Ga3tb4nJaPcOFswflgi/yAvMVBPrxMB+A=="], + "@packrat/cli/@types/bun": ["@types/bun@1.3.13", "", { "dependencies": { "bun-types": "1.3.13" } }, "sha512-9fqXWk5YIHGGnUau9TEi+qdlTYDAnOj+xLCmSTwXfAIqXr2x4tytJb43E9uCvt09zJURKXwAtkoH4nLQfzeTXw=="], "@pnpm/network.ca-file/graceful-fs": ["graceful-fs@4.2.10", "", {}, "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="], @@ -5124,11 +5124,11 @@ "@manypkg/tools/tinyglobby/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], - "@packrat/analytics/@types/bun/bun-types": ["bun-types@1.3.12", "", { "dependencies": { "@types/node": "*" } }, "sha512-HqOLj5PoFajAQciOMRiIZGNoKxDJSr6qigAttOX40vJuSp6DN/CxWp9s3C1Xwm4oH7ybueITwiaOcWXoYVoRkA=="], + "@packrat/analytics/@types/bun/bun-types": ["bun-types@1.3.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-QXKeHLlOLqQX9LgYaHJfzdBaV21T63HhFJnvuRCcjZiaUDpbs5ED1MgxbMra71CsryN/1dAoXuJJJwIv/2drVA=="], - "@packrat/api/@types/bun/bun-types": ["bun-types@1.3.12", "", { "dependencies": { "@types/node": "*" } }, "sha512-HqOLj5PoFajAQciOMRiIZGNoKxDJSr6qigAttOX40vJuSp6DN/CxWp9s3C1Xwm4oH7ybueITwiaOcWXoYVoRkA=="], + "@packrat/api/@types/bun/bun-types": ["bun-types@1.3.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-QXKeHLlOLqQX9LgYaHJfzdBaV21T63HhFJnvuRCcjZiaUDpbs5ED1MgxbMra71CsryN/1dAoXuJJJwIv/2drVA=="], - "@packrat/cli/@types/bun/bun-types": ["bun-types@1.3.12", "", { "dependencies": { "@types/node": "*" } }, "sha512-HqOLj5PoFajAQciOMRiIZGNoKxDJSr6qigAttOX40vJuSp6DN/CxWp9s3C1Xwm4oH7ybueITwiaOcWXoYVoRkA=="], + "@packrat/cli/@types/bun/bun-types": ["bun-types@1.3.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-QXKeHLlOLqQX9LgYaHJfzdBaV21T63HhFJnvuRCcjZiaUDpbs5ED1MgxbMra71CsryN/1dAoXuJJJwIv/2drVA=="], "@react-native/babel-preset/@babel/plugin-transform-classes/@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.27.1", "", {}, "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw=="], From 85b6883e0f818a4cdcbe34489eb48b72039daaf7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Apr 2026 12:48:10 +0000 Subject: [PATCH 3/7] Fix broken markdown inline code formatting in android-keyboard-focus-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> --- docs/android-keyboard-focus-prevention-strategies.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/android-keyboard-focus-prevention-strategies.md b/docs/android-keyboard-focus-prevention-strategies.md index e834feacfd..0a9d44e37c 100644 --- a/docs/android-keyboard-focus-prevention-strategies.md +++ b/docs/android-keyboard-focus-prevention-strategies.md @@ -58,7 +58,7 @@ export const WrappedThirdPartyInput = forwardRef((props, ref) => { #### 2.1 Code Review Checklist **Mandatory checks for any PR containing input elements:** -- [ ] Does the component use the enhanced `TextInput`/`SearchInput` from `expo-app/components/? +- [ ] Does the component use the enhanced `TextInput`/`SearchInput` from `expo-app/components/`? - [ ] Is there any direct import from `react-native` for TextInput? - [ ] If creating a new input component, does it use `useKeyboardHideBlur`? - [ ] Are there any third-party input components that need wrapping? From 831a25fc0ca1534e8cc8f2b6102490cf0d3a39d1 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Thu, 23 Apr 2026 12:55:14 +0000 Subject: [PATCH 4/7] fix: apply CodeRabbit auto-fixes Fixed 8 file(s) based on 8 unresolved review comments. Co-authored-by: CodeRabbit --- .../guides/screens/GuidesListScreen.tsx | 52 ++++++++++------ .../screens/PackTemplateListScreen.tsx | 4 +- .../features/trips/components/TripForm.tsx | 4 +- apps/expo/lib/hooks/useKeyboardHideBlur.tsx | 17 +++++- ...id-keyboard-focus-prevention-strategies.md | 17 +++--- ...board-prevention-implementation-summary.md | 2 +- .../android-textinput-keyboard-focus-loss.md | 60 +++++++++++++------ tsconfig.json | 4 +- 8 files changed, 106 insertions(+), 54 deletions(-) diff --git a/apps/expo/features/guides/screens/GuidesListScreen.tsx b/apps/expo/features/guides/screens/GuidesListScreen.tsx index 0dec568ac1..75dca7d863 100644 --- a/apps/expo/features/guides/screens/GuidesListScreen.tsx +++ b/apps/expo/features/guides/screens/GuidesListScreen.tsx @@ -133,29 +133,45 @@ export const GuidesListScreen = () => { } return ( - - - {guides.length > 0 && ( - - {guides.length} {guides.length === 1 ? t('guides.result') : t('guides.results')} - - )} - - - {guides.map((guide: Guide) => ( - - handleGuidePress(guide)} /> + item.id} + renderItem={({ item }) => ( + + handleGuidePress(item)} /> - ))} - - {guides.length === 0 && ( + )} + ListHeaderComponent={ + guides.length > 0 ? ( + + + {guides.length} {guides.length === 1 ? t('guides.result') : t('guides.results')} + + + ) : null + } + ListEmptyComponent={ {t('guides.noGuidesFound', { query: searchQuery })} - )} - + } + ListFooterComponent={ + isFetchingNextPageSearch ? ( + + + + ) : null + } + onEndReached={() => { + if (hasNextPageSearch && !isFetchingNextPageSearch) { + fetchNextPageSearch(); + } + }} + onEndReachedThreshold={0.5} + contentContainerStyle={{ paddingBottom: 40, flexGrow: 1 }} + /> ); }; @@ -217,4 +233,4 @@ export const GuidesListScreen = () => { /> ); -}; +}; \ No newline at end of file diff --git a/apps/expo/features/pack-templates/screens/PackTemplateListScreen.tsx b/apps/expo/features/pack-templates/screens/PackTemplateListScreen.tsx index c3ab95ba10..a9b5391a7d 100644 --- a/apps/expo/features/pack-templates/screens/PackTemplateListScreen.tsx +++ b/apps/expo/features/pack-templates/screens/PackTemplateListScreen.tsx @@ -229,7 +229,7 @@ export function PackTemplateListScreen() { )} - stickyHeaderIndices={[0]} + stickyHeaderIndices={listHeader() ? [0] : undefined} stickyHeaderHiddenOnScroll ListHeaderComponent={listHeader()} ListEmptyComponent={ @@ -262,4 +262,4 @@ export function PackTemplateListScreen() { ); -} +} \ No newline at end of file diff --git a/apps/expo/features/trips/components/TripForm.tsx b/apps/expo/features/trips/components/TripForm.tsx index 152efb9df2..222c5faee8 100644 --- a/apps/expo/features/trips/components/TripForm.tsx +++ b/apps/expo/features/trips/components/TripForm.tsx @@ -115,7 +115,7 @@ export const TripForm = ({ trip }: { trip?: Trip }) => { const submitData = { ...value, location: location ?? value.location, - packId: value.packId ?? undefined, + packId: value.packId === '' ? undefined : (value.packId ?? undefined), }; try { if (isEditingExistingTrip) { @@ -406,4 +406,4 @@ export const TripForm = ({ trip }: { trip?: Trip }) => { ); -}; +}; \ No newline at end of file diff --git a/apps/expo/lib/hooks/useKeyboardHideBlur.tsx b/apps/expo/lib/hooks/useKeyboardHideBlur.tsx index 5744f55e7f..32bbec6faa 100644 --- a/apps/expo/lib/hooks/useKeyboardHideBlur.tsx +++ b/apps/expo/lib/hooks/useKeyboardHideBlur.tsx @@ -6,9 +6,20 @@ import { Keyboard } from 'react-native'; * Useful for fixing keyboard behavior issues on Android. * * @param textInputRef - Ref to the TextInput or SearchInput component + * @param options - Optional configuration + * @param options.enabled - Whether the hook should be active (default: true) */ -export function useKeyboardHideBlur(textInputRef: React.RefObject<{ blur?: () => void }>) { +export function useKeyboardHideBlur( + textInputRef: React.RefObject<{ blur?: () => void } | null>, + options?: { enabled?: boolean }, +) { + const { enabled = true } = options ?? {}; + useEffect(() => { + if (!enabled) { + return; + } + const keyboardDidHideCallback = () => { if (textInputRef.current?.blur) { textInputRef.current.blur(); @@ -23,5 +34,5 @@ export function useKeyboardHideBlur(textInputRef: React.RefObject<{ blur?: () => return () => { keyboardDidHideSubscription?.remove(); }; - }, [textInputRef]); -} + }, [textInputRef, enabled]); +} \ No newline at end of file diff --git a/docs/android-keyboard-focus-prevention-strategies.md b/docs/android-keyboard-focus-prevention-strategies.md index 0a9d44e37c..726d2a3521 100644 --- a/docs/android-keyboard-focus-prevention-strategies.md +++ b/docs/android-keyboard-focus-prevention-strategies.md @@ -28,13 +28,14 @@ import { TextInput } from 'react-native'; // FORBIDDEN ```typescript // For any new input-related components, always include the hook import { useKeyboardHideBlur } from 'expo-app/lib/hooks/useKeyboardHideBlur'; +import { TextInput } from 'react-native'; export const CustomInput = forwardRef((props, ref) => { - const inputRef = useRef(null); - + const inputRef = useRef(null); + // REQUIRED: Apply keyboard hide blur fix useKeyboardHideBlur(inputRef); - + useImperativeHandle(ref, () => inputRef.current); return ; }); @@ -133,12 +134,10 @@ interface EnhancedTextInputProps extends TextInputProps { export const EnhancedTextInput = forwardRef( ({ autoKeyboardDismiss = true, ...props }, ref) => { const inputRef = useRef(null); - - // Conditional application for rare edge cases - if (autoKeyboardDismiss) { - useKeyboardHideBlur(inputRef); - } - + + // Always call hook unconditionally, use enabled flag to control behavior + useKeyboardHideBlur(inputRef, { enabled: autoKeyboardDismiss }); + useImperativeHandle(ref, () => inputRef.current!); return ; } diff --git a/docs/android-keyboard-prevention-implementation-summary.md b/docs/android-keyboard-prevention-implementation-summary.md index 88c012743a..a87d6e8520 100644 --- a/docs/android-keyboard-prevention-implementation-summary.md +++ b/docs/android-keyboard-prevention-implementation-summary.md @@ -48,7 +48,7 @@ The lefthook configuration runs Biome linting automatically on commit. No additi Your codebase already follows good patterns: - ✅ Enhanced `TextInput` and `SearchInput` components exist - ✅ `useKeyboardHideBlur` hook is properly implemented -- ✅ No direct React Native TextInput imports found +- ✅ Migration complete as of 2026-04-21 - all TextInput imports updated to use enhanced components - ✅ Consistent component patterns in use ### No Breaking Changes diff --git a/docs/solutions/ui-bugs/android-textinput-keyboard-focus-loss.md b/docs/solutions/ui-bugs/android-textinput-keyboard-focus-loss.md index 5f5426fc12..7af27b181c 100644 --- a/docs/solutions/ui-bugs/android-textinput-keyboard-focus-loss.md +++ b/docs/solutions/ui-bugs/android-textinput-keyboard-focus-loss.md @@ -70,11 +70,24 @@ import { Keyboard } from 'react-native'; * Useful for fixing keyboard behavior issues on Android. * * @param textInputRef - Ref to the TextInput or SearchInput component + * @param options - Optional configuration + * @param options.enabled - Whether the hook should be active (default: true) */ -export function useKeyboardHideBlur(textInputRef: React.RefObject) { +export function useKeyboardHideBlur( + textInputRef: React.RefObject<{ blur?: () => void } | null>, + options?: { enabled?: boolean }, +) { + const { enabled = true } = options ?? {}; + useEffect(() => { + if (!enabled) { + return; + } + const keyboardDidHideCallback = () => { - textInputRef.current?.blur(); + if (textInputRef.current?.blur) { + textInputRef.current.blur(); + } }; const keyboardDidHideSubscription = Keyboard.addListener( @@ -85,7 +98,7 @@ export function useKeyboardHideBlur(textInputRef: React.RefObject) { return () => { keyboardDidHideSubscription?.remove(); }; - }, [textInputRef]); + }, [textInputRef, enabled]); } ``` @@ -95,9 +108,11 @@ export function useKeyboardHideBlur(textInputRef: React.RefObject) { ```tsx // apps/expo/components/TextInput.tsx +import { assertPresent } from '@packrat/guards'; +import { useKeyboardHideBlur } from 'expo-app/lib/hooks/useKeyboardHideBlur'; +import { asNonNullableRef } from 'expo-app/lib/utils/asNonNullableRef'; import { forwardRef, useImperativeHandle, useRef } from 'react'; import { TextInput as RNTextInput, type TextInputProps } from 'react-native'; -import { useKeyboardHideBlur } from 'expo-app/lib/hooks/useKeyboardHideBlur'; /** * Enhanced TextInput component that automatically handles keyboard hide blur fix. @@ -105,13 +120,16 @@ import { useKeyboardHideBlur } from 'expo-app/lib/hooks/useKeyboardHideBlur'; */ export const TextInput = forwardRef((props, ref) => { const textInputRef = useRef(null); - + // Apply keyboard hide blur fix - useKeyboardHideBlur(textInputRef); - + useKeyboardHideBlur(asNonNullableRef(textInputRef)); + // Forward ref methods to the internal ref - useImperativeHandle(ref, () => textInputRef.current!); - + useImperativeHandle(ref, () => { + assertPresent(textInputRef.current); + return textInputRef.current; + }, []); + return ; }); @@ -122,23 +140,31 @@ TextInput.displayName = 'TextInput'; ```tsx // apps/expo/components/SearchInput.tsx -import { forwardRef, useImperativeHandle, useRef } from 'react'; +import { assertPresent } from '@packrat/guards'; import { SearchInput as NativeWindUISearchInput } from '@packrat/ui/nativewindui'; import { useKeyboardHideBlur } from 'expo-app/lib/hooks/useKeyboardHideBlur'; +import { asNonNullableRef } from 'expo-app/lib/utils/asNonNullableRef'; +import { forwardRef, useImperativeHandle, useRef } from 'react'; /** * Enhanced SearchInput component that automatically handles keyboard hide blur fix. * Drop-in replacement for NativeWindUI's SearchInput with built-in Android keyboard behavior fix. */ -export const SearchInput = forwardRef>((props, ref) => { - const searchInputRef = useRef(null); - +export const SearchInput = forwardRef< + React.ComponentRef, + React.ComponentProps +>((props, ref) => { + const searchInputRef = useRef>(null); + // Apply keyboard hide blur fix - useKeyboardHideBlur(searchInputRef); - + useKeyboardHideBlur(asNonNullableRef(searchInputRef)); + // Forward ref methods to the internal ref - useImperativeHandle(ref, () => searchInputRef.current); - + useImperativeHandle(ref, () => { + assertPresent(searchInputRef.current); + return searchInputRef.current; + }, []); + return ; }); diff --git a/tsconfig.json b/tsconfig.json index c1c8e5208a..10d0d183a9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,7 @@ "allowJs": true, "baseUrl": ".", "esModuleInterop": true, - "ignoreDeprecations": "5.0", + "ignoreDeprecations": "6.0", "jsx": "react-native", "lib": ["DOM", "ESNext"], "module": "preserve", @@ -56,4 +56,4 @@ "packages/api/container_src", "packages/mcp" ] -} +} \ No newline at end of file From eae1a33b9f387feae35ea8769744891b303e3094 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Thu, 23 Apr 2026 12:58:28 +0000 Subject: [PATCH 5/7] fix: apply CodeRabbit auto-fixes Fixed 1 file(s) based on 1 unresolved review comment. Co-authored-by: CodeRabbit --- docs/android-keyboard-focus-prevention-strategies.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/docs/android-keyboard-focus-prevention-strategies.md b/docs/android-keyboard-focus-prevention-strategies.md index 726d2a3521..db114e532f 100644 --- a/docs/android-keyboard-focus-prevention-strategies.md +++ b/docs/android-keyboard-focus-prevention-strategies.md @@ -71,10 +71,7 @@ Add to ESLint config: ```json { "rules": { - "no-direct-textinput-import": { - "rule": "error", - "message": "Use enhanced TextInput component instead of direct react-native import" - } + "no-direct-textinput-import": "error" } } ``` From 10f12c2b4cdf49ee19edd57867971c081f353804 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 24 Apr 2026 03:55:27 +0000 Subject: [PATCH 6/7] fix: resolve biome CI check failures (format + unused import) 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> --- apps/expo/features/guides/screens/GuidesListScreen.tsx | 4 ++-- .../pack-templates/screens/PackTemplateListScreen.tsx | 2 +- apps/expo/features/trips/components/TripForm.tsx | 2 +- apps/expo/lib/hooks/useKeyboardHideBlur.tsx | 2 +- tsconfig.json | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/expo/features/guides/screens/GuidesListScreen.tsx b/apps/expo/features/guides/screens/GuidesListScreen.tsx index 75dca7d863..355c82ff03 100644 --- a/apps/expo/features/guides/screens/GuidesListScreen.tsx +++ b/apps/expo/features/guides/screens/GuidesListScreen.tsx @@ -7,7 +7,7 @@ import { useTranslation } from 'expo-app/lib/hooks/useTranslation'; import { asNonNullableRef } from 'expo-app/lib/utils/asNonNullableRef'; import { useRouter } from 'expo-router'; import { useCallback, useRef, useState } from 'react'; -import { ActivityIndicator, FlatList, RefreshControl, ScrollView, View } from 'react-native'; +import { ActivityIndicator, FlatList, RefreshControl, View } from 'react-native'; import { GuideCard } from '../components/GuideCard'; import { useGuideCategories, useGuides, useSearchGuides } from '../hooks'; import type { Guide } from '../types'; @@ -233,4 +233,4 @@ export const GuidesListScreen = () => { /> ); -}; \ No newline at end of file +}; diff --git a/apps/expo/features/pack-templates/screens/PackTemplateListScreen.tsx b/apps/expo/features/pack-templates/screens/PackTemplateListScreen.tsx index a9b5391a7d..0802d0f957 100644 --- a/apps/expo/features/pack-templates/screens/PackTemplateListScreen.tsx +++ b/apps/expo/features/pack-templates/screens/PackTemplateListScreen.tsx @@ -262,4 +262,4 @@ export function PackTemplateListScreen() { ); -} \ No newline at end of file +} diff --git a/apps/expo/features/trips/components/TripForm.tsx b/apps/expo/features/trips/components/TripForm.tsx index 222c5faee8..74f59f0595 100644 --- a/apps/expo/features/trips/components/TripForm.tsx +++ b/apps/expo/features/trips/components/TripForm.tsx @@ -406,4 +406,4 @@ export const TripForm = ({ trip }: { trip?: Trip }) => { ); -}; \ No newline at end of file +}; diff --git a/apps/expo/lib/hooks/useKeyboardHideBlur.tsx b/apps/expo/lib/hooks/useKeyboardHideBlur.tsx index 32bbec6faa..2f0ef2bf00 100644 --- a/apps/expo/lib/hooks/useKeyboardHideBlur.tsx +++ b/apps/expo/lib/hooks/useKeyboardHideBlur.tsx @@ -35,4 +35,4 @@ export function useKeyboardHideBlur( keyboardDidHideSubscription?.remove(); }; }, [textInputRef, enabled]); -} \ No newline at end of file +} diff --git a/tsconfig.json b/tsconfig.json index 10d0d183a9..d9276fbcd2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -56,4 +56,4 @@ "packages/api/container_src", "packages/mcp" ] -} \ No newline at end of file +} From 5df373a1745b4c5f099a40085fb81c4edba23783 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 24 Apr 2026 21:15:03 +0000 Subject: [PATCH 7/7] fix: revert ignoreDeprecations to "5.0" to fix TS5103 in CI 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 --- tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index d9276fbcd2..c1c8e5208a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,7 @@ "allowJs": true, "baseUrl": ".", "esModuleInterop": true, - "ignoreDeprecations": "6.0", + "ignoreDeprecations": "5.0", "jsx": "react-native", "lib": ["DOM", "ESNext"], "module": "preserve",