Merge pull request #367 from AdityasahuX07/patch-17

[Updated]Added double tap on search button to open keyboard for ready to search feature.
This commit is contained in:
Nayif 2026-01-07 22:13:23 +05:30 committed by GitHub
commit f779febc32
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 85 additions and 18 deletions

View file

@ -2,7 +2,7 @@ import React, { useEffect, useRef, useMemo, useState } from 'react';
import { NavigationContainer, DefaultTheme as NavigationDefaultTheme, DarkTheme as NavigationDarkTheme, Theme, NavigationProp } from '@react-navigation/native'; import { NavigationContainer, DefaultTheme as NavigationDefaultTheme, DarkTheme as NavigationDarkTheme, Theme, NavigationProp } from '@react-navigation/native';
import { createNativeStackNavigator, NativeStackNavigationOptions, NativeStackNavigationProp } from '@react-navigation/native-stack'; import { createNativeStackNavigator, NativeStackNavigationOptions, NativeStackNavigationProp } from '@react-navigation/native-stack';
import { createBottomTabNavigator, BottomTabNavigationProp } from '@react-navigation/bottom-tabs'; import { createBottomTabNavigator, BottomTabNavigationProp } from '@react-navigation/bottom-tabs';
import { useColorScheme, Platform, Animated, StatusBar, TouchableOpacity, View, Text, AppState, Easing, Dimensions } from 'react-native'; import { useColorScheme, Platform, Animated, StatusBar, TouchableOpacity, View, Text, AppState, Easing, Dimensions, DeviceEventEmitter } from 'react-native';
import { mmkvStorage } from '../services/mmkvStorage'; import { mmkvStorage } from '../services/mmkvStorage';
import { PaperProvider, MD3DarkTheme, MD3LightTheme, adaptNavigationTheme } from 'react-native-paper'; import { PaperProvider, MD3DarkTheme, MD3LightTheme, adaptNavigationTheme } from 'react-native-paper';
import type { MD3Theme } from 'react-native-paper'; import type { MD3Theme } from 'react-native-paper';
@ -473,7 +473,6 @@ const TabIcon = React.memo(({ focused, color, iconName, iconLibrary = 'material'
// Update the TabScreenWrapper component with fixed layout dimensions // Update the TabScreenWrapper component with fixed layout dimensions
const TabScreenWrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => { const TabScreenWrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [dimensions, setDimensions] = useState(Dimensions.get('window'));
useEffect(() => { useEffect(() => {
const subscription = Dimensions.addEventListener('change', ({ window }) => { const subscription = Dimensions.addEventListener('change', ({ window }) => {
@ -555,6 +554,7 @@ const MainTabs = () => {
const { settings: appSettings } = useSettingsHook(); const { settings: appSettings } = useSettingsHook();
const [hasUpdateBadge, setHasUpdateBadge] = React.useState(false); const [hasUpdateBadge, setHasUpdateBadge] = React.useState(false);
const [dimensions, setDimensions] = useState(Dimensions.get('window')); const [dimensions, setDimensions] = useState(Dimensions.get('window'));
const lastTapRef = useRef<Record<string, number>>({});
useEffect(() => { useEffect(() => {
const subscription = Dimensions.addEventListener('change', ({ window }) => { const subscription = Dimensions.addEventListener('change', ({ window }) => {
@ -692,15 +692,31 @@ const MainTabs = () => {
const isFocused = props.state.index === index; const isFocused = props.state.index === index;
const lastTapRef = useRef<Record<string, number>>({}); // Add this ref at the top of MainTabs component
const onPress = () => { const onPress = () => {
const now = Date.now();
const DOUBLE_TAP_DELAY = 300;
const lastTap = lastTapRef.current[route.name] || 0;
const isSearchDoubleTap = route.name === 'Search' && (now - lastTap) < DOUBLE_TAP_DELAY;
// Update last tap time
lastTapRef.current[route.name] = now;
const event = props.navigation.emit({ const event = props.navigation.emit({
type: 'tabPress', type: 'tabPress',
target: route.key, target: route.key,
canPreventDefault: true, canPreventDefault: true,
}); });
if (isFocused) { if (isFocused) {
// Same tab pressed - emit scroll to top // If double tap on Search -> Open Keyboard
emitScrollToTop(route.name); if (isSearchDoubleTap) {
DeviceEventEmitter.emit('FOCUS_SEARCH_INPUT');
} else {
// Single tap on active tab -> Scroll to Top
emitScrollToTop(route.name);
}
} else if (!event.defaultPrevented) { } else if (!event.defaultPrevented) {
props.navigation.navigate(route.name); props.navigation.navigate(route.name);
} }
@ -810,19 +826,29 @@ const MainTabs = () => {
const isFocused = props.state.index === index; const isFocused = props.state.index === index;
const onPress = () => { const onPress = () => {
const event = props.navigation.emit({ const now = Date.now();
type: 'tabPress', const DOUBLE_TAP_DELAY = 300;
target: route.key, const lastTap = lastTapRef.current[route.name] || 0;
canPreventDefault: true,
});
if (isFocused) { // DOUBLE TAP LOGIC: If search is pressed twice quickly
// Same tab pressed - emit scroll to top if (route.name === 'Search' && now - lastTap < DOUBLE_TAP_DELAY) {
emitScrollToTop(route.name); DeviceEventEmitter.emit('FOCUS_SEARCH_INPUT');
} else if (!event.defaultPrevented) { }
props.navigation.navigate(route.name);
} lastTapRef.current[route.name] = now;
};
const event = props.navigation.emit({
type: 'tabPress',
target: route.key,
canPreventDefault: true,
});
if (isFocused) {
emitScrollToTop(route.name);
} else if (!event.defaultPrevented) {
props.navigation.navigate(route.name);
}
};
let iconName: IconNameType = 'home'; let iconName: IconNameType = 'home';
let iconLibrary: 'material' | 'feather' | 'ionicons' = 'material'; let iconLibrary: 'material' | 'feather' | 'ionicons' = 'material';
@ -1080,8 +1106,29 @@ const MainTabs = () => {
options={{ options={{
tabBarLabel: t('navigation.search'), tabBarLabel: t('navigation.search'),
tabBarIcon: ({ color, size }) => ( tabBarIcon: ({ color, size }) => (
<MaterialCommunityIcons name={'magnify'} size={size} color={color} /> <Feather name="search" size={size} color={color} />
), ),
tabBarButton: (props) => {
const lastTap = useRef(0);
return (
<TouchableOpacity
{...props}
activeOpacity={0.7}
onPress={(e) => {
const now = Date.now();
const DOUBLE_TAP_DELAY = 300;
// Check for double tap
if (now - lastTap.current < DOUBLE_TAP_DELAY) {
DeviceEventEmitter.emit('FOCUS_SEARCH_INPUT');
} else {
props.onPress?.(e);
}
lastTap.current = now;
}}
/>
);
},
}} }}
/> />
{appSettings?.enableDownloads !== false && ( {appSettings?.enableDownloads !== false && (
@ -1790,4 +1837,4 @@ const AppNavigator = ({ initialRouteName }: { initialRouteName?: keyof RootStack
</PostHogProvider> </PostHogProvider>
); );
export default AppNavigator; export default AppNavigator;

View file

@ -111,6 +111,26 @@ const SearchScreen = () => {
return () => { isMounted.current = false; }; return () => { isMounted.current = false; };
}, []); }, []);
useEffect(() => {
const focusSubscription = DeviceEventEmitter.addListener('FOCUS_SEARCH_INPUT', () => {
// Optional: Reset search state if user double taps while on search
if (query.length === 0) {
setResults({ byAddon: [], allResults: [] });
setSearched(false);
setShowRecent(true);
}
// Use a small timeout to ensure the UI is ready
setTimeout(() => {
if (inputRef.current) {
inputRef.current.focus();
}
}, 120);
});
return () => focusSubscription.remove();
}, [query]);
// Update isSaved and isWatched when selectedItem changes // Update isSaved and isWatched when selectedItem changes
useEffect(() => { useEffect(() => {
if (!selectedItem) return; if (!selectedItem) return;