feat: focus search bar on second tab press (#558)

This commit is contained in:
Fredrik Burmester
2025-02-22 13:09:17 +01:00
committed by GitHub
parent 5590c2f784
commit af2bd030e9
3 changed files with 59 additions and 2 deletions

View File

@@ -26,12 +26,14 @@ import React, {
useEffect, useEffect,
useLayoutEffect, useLayoutEffect,
useMemo, useMemo,
useRef,
useState, useState,
} from "react"; } from "react";
import { Platform, ScrollView, TouchableOpacity, View } from "react-native"; import { Platform, ScrollView, TouchableOpacity, View } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context"; import { useSafeAreaInsets } from "react-native-safe-area-context";
import { useDebounce } from "use-debounce"; import { useDebounce } from "use-debounce";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { eventBus } from "@/utils/eventBus";
type SearchType = "Library" | "Discover"; type SearchType = "Library" | "Discover";
@@ -120,21 +122,44 @@ export default function search() {
[api, searchEngine, settings] [api, searchEngine, settings]
); );
type HeaderSearchBarRef = {
focus: () => void;
blur: () => void;
setText: (text: string) => void;
clearText: () => void;
cancelSearch: () => void;
};
const searchBarRef = useRef<HeaderSearchBarRef>(null);
const navigation = useNavigation(); const navigation = useNavigation();
useLayoutEffect(() => { useLayoutEffect(() => {
navigation.setOptions({ navigation.setOptions({
headerSearchBarOptions: { headerSearchBarOptions: {
ref: searchBarRef,
placeholder: t("search.search"), placeholder: t("search.search"),
onChangeText: (e: any) => { onChangeText: (e: any) => {
router.setParams({ q: "" }); router.setParams({ q: "" });
setSearch(e.nativeEvent.text); setSearch(e.nativeEvent.text);
}, },
hideWhenScrolling: false, hideWhenScrolling: false,
autoFocus: true, autoFocus: false,
}, },
}); });
}, [navigation]); }, [navigation]);
useEffect(() => {
const unsubscribe = eventBus.on("searchTabPressed", () => {
// Screen not actuve
if (!searchBarRef.current) return;
// Screen is active, focus search bar
searchBarRef.current?.focus();
});
return () => {
unsubscribe();
};
}, []);
const { data: movies, isFetching: l1 } = useQuery({ const { data: movies, isFetching: l1 } = useQuery({
queryKey: ["search", "movies", debouncedSearch], queryKey: ["search", "movies", debouncedSearch],
queryFn: () => queryFn: () =>

View File

@@ -20,6 +20,7 @@ import type {
TabNavigationState, TabNavigationState,
} from "@react-navigation/native"; } from "@react-navigation/native";
import { SystemBars } from "react-native-edge-to-edge"; import { SystemBars } from "react-native-edge-to-edge";
import { eventBus } from "@/utils/eventBus";
export const NativeTabs = withLayoutContext< export const NativeTabs = withLayoutContext<
BottomTabNavigationOptions, BottomTabNavigationOptions,
@@ -76,6 +77,11 @@ export default function TabLayout() {
}} }}
/> />
<NativeTabs.Screen <NativeTabs.Screen
listeners={({ navigation }) => ({
tabPress: (e) => {
eventBus.emit("searchTabPressed");
},
})}
name="(search)" name="(search)"
options={{ options={{
title: t("tabs.search"), title: t("tabs.search"),

26
utils/eventBus.ts Normal file
View File

@@ -0,0 +1,26 @@
type Listener<T = void> = (data?: T) => void;
class EventBus {
private listeners: Record<string, Listener<any>[]> = {};
on<T = void>(event: string, callback: Listener<T>): () => void {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event].push(callback);
return () => this.off(event, callback);
}
off<T = void>(event: string, callback: Listener<T>): void {
if (!this.listeners[event]) return;
this.listeners[event] = this.listeners[event].filter(
(fn) => fn !== callback
);
}
emit<T = void>(event: string, data?: T): void {
this.listeners[event]?.forEach((callback) => callback(data));
}
}
export const eventBus = new EventBus();