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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/admin/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "packrat-admin-app",
"version": "2.0.22",
"version": "2.0.23",
"private": true,
"scripts": {
"build": "next build",
Expand Down
2 changes: 1 addition & 1 deletion apps/expo/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export default (): ExpoConfig =>
{
name: getAppName(),
slug: 'packrat',
version: '2.0.22',
version: '2.0.23',
scheme: 'packrat',
web: {
bundler: 'metro',
Expand Down
52 changes: 34 additions & 18 deletions apps/expo/features/guides/screens/GuidesListScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -133,29 +133,45 @@ export const GuidesListScreen = () => {
}

return (
<ScrollView contentContainerStyle={{ paddingBottom: 40 }}>
<View className="px-4 pt-2">
{guides.length > 0 && (
<Text className="text-xs text-muted-foreground">
{guides.length} {guides.length === 1 ? t('guides.result') : t('guides.results')}
</Text>
)}
</View>

{guides.map((guide: Guide) => (
<View className="px-4 pt-4" key={guide.id}>
<GuideCard guide={guide} onPress={() => handleGuidePress(guide)} />
<FlatList
data={guides}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<View className="px-4 pt-4">
<GuideCard guide={item} onPress={() => handleGuidePress(item)} />
</View>
))}

{guides.length === 0 && (
)}
ListHeaderComponent={
guides.length > 0 ? (
<View className="px-4 pt-2">
<Text className="text-xs text-muted-foreground">
{guides.length} {guides.length === 1 ? t('guides.result') : t('guides.results')}
</Text>
</View>
) : null
}
ListEmptyComponent={
<View className="flex-1 items-center justify-center p-8">
<Text className="text-center text-gray-500 dark:text-gray-400">
{t('guides.noGuidesFound', { query: searchQuery })}
</Text>
</View>
)}
</ScrollView>
}
ListFooterComponent={
isFetchingNextPageSearch ? (
<View className="py-4">
<ActivityIndicator size="small" color={colors.primary} />
</View>
) : null
}
onEndReached={() => {
if (hasNextPageSearch && !isFetchingNextPageSearch) {
fetchNextPageSearch();
}
}}
onEndReachedThreshold={0.5}
contentContainerStyle={{ paddingBottom: 40, flexGrow: 1 }}
/>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ export function PackTemplateListScreen() {
<PackTemplateCard templateId={item.id} onPress={handleTemplatePress} />
</View>
)}
stickyHeaderIndices={[0]}
stickyHeaderIndices={listHeader() ? [0] : undefined}
stickyHeaderHiddenOnScroll
ListHeaderComponent={listHeader()}
ListEmptyComponent={
Expand Down
2 changes: 1 addition & 1 deletion apps/expo/features/trips/components/TripForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
15 changes: 13 additions & 2 deletions apps/expo/lib/hooks/useKeyboardHideBlur.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -23,5 +34,5 @@ export function useKeyboardHideBlur(textInputRef: React.RefObject<{ blur?: () =>
return () => {
keyboardDidHideSubscription?.remove();
};
}, [textInputRef]);
}, [textInputRef, enabled]);
}
2 changes: 1 addition & 1 deletion apps/expo/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "packrat-expo-app",
"version": "2.0.22",
"version": "2.0.23",
"private": true,
"main": "expo-router/entry",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion apps/guides/package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
2 changes: 1 addition & 1 deletion apps/landing/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "packrat-landing-app",
"version": "2.0.22",
"version": "2.0.23",
"private": true,
"scripts": {
"build": "next build",
Expand Down
42 changes: 21 additions & 21 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 10 additions & 14 deletions docs/android-keyboard-focus-prevention-strategies.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<InputRef, InputProps>((props, ref) => {
const inputRef = useRef<any>(null);
const inputRef = useRef<TextInput | null>(null);

// REQUIRED: Apply keyboard hide blur fix
useKeyboardHideBlur(inputRef);

useImperativeHandle(ref, () => inputRef.current);
return <SomeInputComponent ref={inputRef} {...props} />;
});
Expand All @@ -58,7 +59,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?
Expand All @@ -70,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"
}
}
```
Expand Down Expand Up @@ -133,12 +131,10 @@ interface EnhancedTextInputProps extends TextInputProps {
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 hook unconditionally, use enabled flag to control behavior
useKeyboardHideBlur(inputRef, { enabled: autoKeyboardDismiss });

useImperativeHandle(ref, () => inputRef.current!);
return <RNTextInput ref={inputRef} {...props} />;
}
Expand Down
2 changes: 1 addition & 1 deletion docs/android-keyboard-prevention-implementation-summary.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading
Loading