authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-10-13 21:18:29-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-10-13 21:18:29-07:00
log107f6024ea4786a58a83c921728a113b849de192
tree4ebb9d7a5f132431bc9d09ae13ecb58dfef3677f
parent6002c5c68c637de2cd2557bd6c40e46c153e39b1
signature Signed by PGP key B5690EEEBB952194

fix(mobile): search bar animates in on iOS 26+ instead of popping (#19209)


1 files changed, 26 insertions(+), 14 deletions(-)

apps/mobile/src/features/workspace/WorkspaceToolbar.ios.tsx+26-14
...@@ -4,6 +4,7 @@ import { Platform } from "react-native";...@@ -4,6 +4,7 @@ import { Platform } from "react-native";
4import { useNativeLayoutMetrics } from "../../native/native-layout-metrics";4import { useNativeLayoutMetrics } from "../../native/native-layout-metrics";
5import type { SearchBarCommands } from "react-native-screens";5import type { SearchBarCommands } from "react-native-screens";
66
7import { NATIVE_LIQUID_GLASS_SUPPORTED } from "../../native/native-glass";
7import { NativeHeaderToolbar, NativeStackScreenOptions } from "../../native/StackHeader";8import { NativeHeaderToolbar, NativeStackScreenOptions } from "../../native/StackHeader";
8import { NativeWorkspaceTabActivityContext } from "../../native/v5-workspace-context";9import { NativeWorkspaceTabActivityContext } from "../../native/v5-workspace-context";
9import { useHardwareKeyboardCommand } from "../keyboard/hardwareKeyboardCommands";10import { useHardwareKeyboardCommand } from "../keyboard/hardwareKeyboardCommands";
...@@ -26,8 +27,7 @@ export function WorkspaceToolbar(props: {...@@ -26,8 +27,7 @@ export function WorkspaceToolbar(props: {
26 const searchRef = useRef<SearchBarCommands>(null);27 const searchRef = useRef<SearchBarCommands>(null);
27 const tab = workspace.state.tabs[workspace.state.selectedTab];28 const tab = workspace.state.tabs[workspace.state.selectedTab];
28 const selectedTab = workspace.state.selectedTab;29 const selectedTab = workspace.state.selectedTab;
29 const focusSearch = useCallback(() => {30 const enterSearch = useCallback(() => {
30 if (!workspace.isTabActive(selectedTab)) return false;
31 workspace.dispatch({31 workspace.dispatch({
32 type: "scroll",32 type: "scroll",
33 tab: selectedTab,33 tab: selectedTab,
...@@ -35,9 +35,14 @@ export function WorkspaceToolbar(props: {...@@ -35,9 +35,14 @@ export function WorkspaceToolbar(props: {
35 });35 });
36 workspace.dispatch({ type: "search", tab: selectedTab });36 workspace.dispatch({ type: "search", tab: selectedTab });
37 if (props.sidebar && !panes.primarySidebarVisible) togglePrimarySidebar();37 if (props.sidebar && !panes.primarySidebarVisible) togglePrimarySidebar();
38 }, [workspace, selectedTab, props.sidebar, panes.primarySidebarVisible, togglePrimarySidebar]);
39 const focusSearch = useCallback(() => {
40 if (!workspace.isTabActive(selectedTab)) return false;
41 // A mounted bar enters search from its own onFocus.
42 if (!NATIVE_LIQUID_GLASS_SUPPORTED) enterSearch();
38 searchRef.current?.focus();43 searchRef.current?.focus();
39 return true;44 return true;
40 }, [workspace, selectedTab, props.sidebar, panes.primarySidebarVisible, togglePrimarySidebar]);45 }, [workspace, selectedTab, enterSearch]);
41 useHardwareKeyboardCommand("focusSearch", focusSearch);46 useHardwareKeyboardCommand("focusSearch", focusSearch);
42 const restoreSearch = useCallback(() => {47 const restoreSearch = useCallback(() => {
43 if (isTabActive(selectedTab) && tab.searchMode !== "resting")48 if (isTabActive(selectedTab) && tab.searchMode !== "resting")
...@@ -59,8 +64,10 @@ export function WorkspaceToolbar(props: {...@@ -59,8 +64,10 @@ export function WorkspaceToolbar(props: {
59 <NativeStackScreenOptions64 <NativeStackScreenOptions
60 optionsVersion={[selectedTab, tab.searchMode]}65 optionsVersion={[selectedTab, tab.searchMode]}
61 options={{66 options={{
67 // Liquid Glass keeps search mounted as UIKit's own header button, which
68 // morphs into the field; swapping a stacked bar in and out cannot animate.
62 headerSearchBarOptions:69 headerSearchBarOptions:
63 tab.searchMode !== "resting"70 NATIVE_LIQUID_GLASS_SUPPORTED || tab.searchMode !== "resting"
64 ? {71 ? {
65 ref: searchRef,72 ref: searchRef,
66 autoFocus: tab.searchMode === "active",73 autoFocus: tab.searchMode === "active",
...@@ -68,12 +75,15 @@ export function WorkspaceToolbar(props: {...@@ -68,12 +75,15 @@ export function WorkspaceToolbar(props: {
68 hideNavigationBar: false,75 hideNavigationBar: false,
69 hideWhenScrolling: false,76 hideWhenScrolling: false,
70 obscureBackground: false,77 obscureBackground: false,
71 placement: "stacked",78 placement: NATIVE_LIQUID_GLASS_SUPPORTED ? "integratedButton" : "stacked",
72 allowToolbarIntegration: false,79 allowToolbarIntegration: NATIVE_LIQUID_GLASS_SUPPORTED,
73 placeholder:80 placeholder:
74 selectedTab === "pullRequests" ? "Search pull requests" : "Search threads",81 selectedTab === "pullRequests" ? "Search pull requests" : "Search threads",
75 cancelButtonText: "✕",82 cancelButtonText: "✕",
76 onFocus: () => workspace.dispatch({ type: "search", tab: selectedTab }),83 onFocus: () =>
84 tab.searchMode === "resting"
85 ? enterSearch()
86 : workspace.dispatch({ type: "search", tab: selectedTab }),
77 onBlur: () => workspace.dispatch({ type: "expand", tab: selectedTab }),87 onBlur: () => workspace.dispatch({ type: "expand", tab: selectedTab }),
78 onSearchButtonPress: () => searchRef.current?.blur(),88 onSearchButtonPress: () => searchRef.current?.blur(),
79 onChangeText: ({ nativeEvent }) =>89 onChangeText: ({ nativeEvent }) =>
...@@ -102,13 +112,15 @@ export function WorkspaceToolbar(props: {...@@ -102,13 +112,15 @@ export function WorkspaceToolbar(props: {
102 })112 })
103 }113 }
104 />114 />
105 <NativeHeaderToolbar.Button115 {NATIVE_LIQUID_GLASS_SUPPORTED ? null : (
106 accessibilityLabel={116 <NativeHeaderToolbar.Button
107 selectedTab === "pullRequests" ? "Search pull requests" : "Search threads"117 accessibilityLabel={
108 }118 selectedTab === "pullRequests" ? "Search pull requests" : "Search threads"
109 icon="magnifyingglass"119 }
110 onPress={focusSearch}120 icon="magnifyingglass"
111 />121 onPress={focusSearch}
122 />
123 )}
112 </NativeHeaderToolbar>124 </NativeHeaderToolbar>
113 <NativeHeaderToolbar placement="bottom">125 <NativeHeaderToolbar placement="bottom">
114 {nativeToolbarCompose ? <NativeHeaderToolbar.Spacer flexible /> : null}126 {nativeToolbarCompose ? <NativeHeaderToolbar.Spacer flexible /> : null}