From 54bbed660fc5dc99c6bfe335981e6d431275c91e Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Fri, 18 Sep 2026 12:55:15 -0700 Subject: [PATCH] refactor(mobile): share file header actions and search configuration (#12390) --- .../features/files/ThreadFilesRouteScreen.tsx | 327 +++++++----------- 1 file changed, 121 insertions(+), 206 deletions(-) diff --git a/apps/mobile/src/features/files/ThreadFilesRouteScreen.tsx b/apps/mobile/src/features/files/ThreadFilesRouteScreen.tsx index 2cfe50de830a40a1f2d46ece9a23dbc7c7651f84..9e89ea1b24e49602364c447803d7ce2e7c2fe815 100644 --- a/apps/mobile/src/features/files/ThreadFilesRouteScreen.tsx +++ b/apps/mobile/src/features/files/ThreadFilesRouteScreen.tsx @@ -1,6 +1,5 @@ -import { NativeHeaderToolbar, NativeStackScreenOptions } from "../../native/StackHeader"; +import { NativeStackScreenOptions } from "../../native/StackHeader"; import { StackActions, useNavigation, type StaticScreenProps } from "@react-navigation/native"; -import type { MenuAction } from "@react-native-menu/menu"; import { useCallback, useEffect, useId, useMemo, useRef, useState } from "react"; import { Platform, View } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; @@ -14,10 +13,8 @@ import { } from "@t3tools/shared/filePreview"; import { mediaFileReference } from "@t3tools/client-runtime/media-reference"; -import { AndroidHeaderIconButton, AndroidScreenHeader } from "../../components/AndroidScreenHeader"; import { MaterialScreenContent } from "../../components/MaterialScreenContent"; import { AudioFilePreview } from "../../components/AudioFilePreview"; -import { ControlPillMenu } from "../../components/ControlPill"; import { EmptyState } from "../../components/EmptyState"; import { FilePreviewModal, type FilePreviewSource } from "../../components/FilePreviewModal"; import { LoadingScreen } from "../../components/LoadingScreen"; @@ -38,14 +35,6 @@ import { useAdaptiveWorkspacePaneRole, useRegisterWorkspaceInspector, } from "../layout/AdaptiveWorkspaceLayout"; -import { - createNativeMailSearchToolbarItem, - NATIVE_MAIL_SEARCH_TOOLBAR_SUPPORTED, -} from "../layout/native-mail-search-toolbar"; -import { - AndroidWorkspaceSidebarButton, - WorkspaceSidebarToolbar, -} from "../layout/workspace-sidebar-toolbar"; import { useAppearancePreferences } from "../settings/appearance/AppearancePreferencesProvider"; import { ThreadRouteScreen } from "../threads/ThreadRouteScreen"; import { FilePreviewLoading, FilePreviewNotice } from "./FilePreviewFeedback"; @@ -55,7 +44,7 @@ import { useFileTreeEntries } from "./useFileTreeEntries"; import { preloadWorkspaceFileContents } from "./preload-workspace-file"; import { SourceFileSurface } from "./SourceFileSurface"; import { ThreadFileNavigatorPane } from "./thread-file-navigator-pane"; -import { MaterialFilesHeader } from "./MaterialFilesHeader"; +import { ScreenHeader } from "../../components/ScreenHeader"; import { WorkspaceFileImagePreview } from "./WorkspaceFileImagePreview"; import { WorkspaceFilePreviewError } from "./WorkspaceFilePreviewError"; import { WorkspaceFileVideoPreview } from "./WorkspaceFileVideoPreview"; @@ -70,6 +59,107 @@ import { } from "./filePath"; import { useWorkspaceFileAssetUrlState } from "./workspaceFileAssetUrl"; +function FilesBrowserHeader(props: { + readonly projectName: string; + readonly searchQuery: string; + readonly onSearchQueryChange: (query: string) => void; + readonly onRefresh: () => void; + readonly onBack: () => void; +}) { + return ( + + ); +} + +function FileHeader(props: { + readonly title: string; + readonly subtitle: string; + readonly iconColor: string; + readonly activeMode: string; + readonly fileInspectorSupported: boolean; + readonly onBack: () => void; + readonly onReturnToThread: () => void; + readonly actions: ReadonlyArray<{ + readonly id: string; + readonly title: string; + readonly icon: string; + readonly inline: boolean; + readonly onPress: () => unknown; + }>; +}) { + const { panes, toggleAuxiliaryPane } = useAdaptiveWorkspaceLayout(); + const modes = props.actions.filter(({ inline }) => inline); + return ( + 0 + ? [ + { + id: "modes", + inline: true, + items: modes.map((action) => ({ + ...action, + selected: action.id === props.activeMode, + })), + }, + ] + : []), + ...props.actions.filter(({ inline }) => !inline), + ], + }, + ]} + /> + ); +} + type FileViewMode = "preview" | "source"; function firstRouteParam(value: string | string[] | undefined): string | null { @@ -315,14 +405,10 @@ function FilesToolbarBottomFade() { export function ThreadFilesTreeScreen(props: ThreadFilesRouteScreenProps) { useAdaptiveWorkspacePaneRole("inspector"); const navigation = useNavigation(); - const { fileInspector, layout, panes, showAuxiliaryPane, togglePrimarySidebar } = - useAdaptiveWorkspaceLayout(); + const { fileInspector, layout, showAuxiliaryPane } = useAdaptiveWorkspaceLayout(); const [searchQuery, setSearchQuery] = useState(""); - const isAndroid = Platform.OS === "android"; const { themeAppearance: highlightTheme } = useAppearancePreferences(); - const theme = useUniwindTheme(); - const headerColor = theme["--color-header"]; - const sheetSurfaceColor = theme["--color-sheet-solid"]; + const headerColor = useUniwindTheme()["--color-header"]; const { cwd, environmentId, projectName, selectedThread, threadId } = useThreadFilesWorkspace( props.route.params, ); @@ -430,84 +516,15 @@ export function ThreadFilesTreeScreen(props: ThreadFilesRouteScreenProps) { ); } - const usesCompactMailToolbar = - Platform.OS === "ios" && !layout.usesSplitView && NATIVE_MAIL_SEARCH_TOOLBAR_SUPPORTED; - const content = ( <> - {/* Static header config (glass preset and title) lives in Stack.tsx. The - live sheet color stays dynamic here so the FlatList can remain the - direct scene child for native scroll-edge sampling. */} - 0 ? projectName : undefined, - // No refresh button: the list already supports pull-to-refresh. - unstable_headerToolbarItems: usesCompactMailToolbar - ? () => [ - createNativeMailSearchToolbarItem({ - onSearchTextChange: setSearchQuery, - placeholder: "Search files", - searchTextChangeId: "files-search-text", - }), - ] - : undefined, - headerSearchBarOptions: usesCompactMailToolbar - ? undefined - : { - allowToolbarIntegration: true, - autoCapitalize: "none", - hideNavigationBar: false, - placeholder: "Search files", - onChangeText: (event) => { - setSearchQuery(event.nativeEvent.text); - }, - onCancelButtonPress: () => { - setSearchQuery(""); - }, - }, - }} + - {isAndroid ? ( - <> - { - } - searchQuery={searchQuery} - onSearchQueryChange={setSearchQuery} - onRefresh={entriesQuery.refresh} - onBack={handleReturnToThread} - /> - } - - ) : ( - <> - {layout.usesSplitView ? ( - - - - ) : null} - {usesCompactMailToolbar ? null : ( - - - - )} - - )} ( - () => - fileMenuActions.map((action) => ({ - id: action.id, - title: action.title, - image: action.icon, - state: action.id === resolvedActiveMode ? "on" : undefined, - })), - [fileMenuActions, resolvedActiveMode], - ); - const handleAndroidFileMenuAction = useCallback( - (event: { nativeEvent: { event: string } }) => { - const action = fileMenuActions.find(({ id }) => id === event.nativeEvent.event); - void action?.onPress(); - }, - [fileMenuActions], - ); const handleReturnToThread = useCallback(() => { if (environmentId !== null && threadId !== null) { navigation.dispatch( @@ -878,100 +877,16 @@ export function ThreadFileScreen(props: ThreadFileRouteScreenProps) { return ( - 0 ? headerSubtitle : undefined, - }} + - {isAndroid ? ( - } - hideBottomBorder - onBack={handleBack} - trailing={ - <> - {fileInspector.supported ? ( - - ) : null} - - - - - } - /> - ) : null} - - {fileInspector.supported ? ( - - ) : null} - - - {fileInspector.supported ? ( - - ) : null} - - {fileMenuActions.some(({ inline }) => inline) ? ( - - {fileMenuActions - .filter(({ inline }) => inline) - .map((action) => ( - - {action.title} - - ))} - - ) : null} - {fileMenuActions - .filter(({ inline }) => !inline) - .map((action) => ( - - {action.title} - - ))} - -