| ... | @@ -1,6 +1,5 @@ | ... | @@ -1,6 +1,5 @@ |
| 1 | import { NativeHeaderToolbar, NativeStackScreenOptions } from "../../native/StackHeader"; | 1 | import { NativeStackScreenOptions } from "../../native/StackHeader"; |
| 2 | import { StackActions, useNavigation, type StaticScreenProps } from "@react-navigation/native"; | 2 | import { StackActions, useNavigation, type StaticScreenProps } from "@react-navigation/native"; |
| 3 | import type { MenuAction } from "@react-native-menu/menu"; | | |
| 4 | import { useCallback, useEffect, useId, useMemo, useRef, useState } from "react"; | 3 | import { useCallback, useEffect, useId, useMemo, useRef, useState } from "react"; |
| 5 | import { Platform, View } from "react-native"; | 4 | import { Platform, View } from "react-native"; |
| 6 | import { useSafeAreaInsets } from "react-native-safe-area-context"; | 5 | import { useSafeAreaInsets } from "react-native-safe-area-context"; |
| ... | @@ -14,10 +13,8 @@ import { | ... | @@ -14,10 +13,8 @@ import { |
| 14 | } from "@t3tools/shared/filePreview"; | 13 | } from "@t3tools/shared/filePreview"; |
| 15 | import { mediaFileReference } from "@t3tools/client-runtime/media-reference"; | 14 | import { mediaFileReference } from "@t3tools/client-runtime/media-reference"; |
| 16 | | 15 | |
| 17 | import { AndroidHeaderIconButton, AndroidScreenHeader } from "../../components/AndroidScreenHeader"; | | |
| 18 | import { MaterialScreenContent } from "../../components/MaterialScreenContent"; | 16 | import { MaterialScreenContent } from "../../components/MaterialScreenContent"; |
| 19 | import { AudioFilePreview } from "../../components/AudioFilePreview"; | 17 | import { AudioFilePreview } from "../../components/AudioFilePreview"; |
| 20 | import { ControlPillMenu } from "../../components/ControlPill"; | | |
| 21 | import { EmptyState } from "../../components/EmptyState"; | 18 | import { EmptyState } from "../../components/EmptyState"; |
| 22 | import { FilePreviewModal, type FilePreviewSource } from "../../components/FilePreviewModal"; | 19 | import { FilePreviewModal, type FilePreviewSource } from "../../components/FilePreviewModal"; |
| 23 | import { LoadingScreen } from "../../components/LoadingScreen"; | 20 | import { LoadingScreen } from "../../components/LoadingScreen"; |
| ... | @@ -38,14 +35,6 @@ import { | ... | @@ -38,14 +35,6 @@ import { |
| 38 | useAdaptiveWorkspacePaneRole, | 35 | useAdaptiveWorkspacePaneRole, |
| 39 | useRegisterWorkspaceInspector, | 36 | useRegisterWorkspaceInspector, |
| 40 | } from "../layout/AdaptiveWorkspaceLayout"; | 37 | } from "../layout/AdaptiveWorkspaceLayout"; |
| 41 | import { | | |
| 42 | createNativeMailSearchToolbarItem, | | |
| 43 | NATIVE_MAIL_SEARCH_TOOLBAR_SUPPORTED, | | |
| 44 | } from "../layout/native-mail-search-toolbar"; | | |
| 45 | import { | | |
| 46 | AndroidWorkspaceSidebarButton, | | |
| 47 | WorkspaceSidebarToolbar, | | |
| 48 | } from "../layout/workspace-sidebar-toolbar"; | | |
| 49 | import { useAppearancePreferences } from "../settings/appearance/AppearancePreferencesProvider"; | 38 | import { useAppearancePreferences } from "../settings/appearance/AppearancePreferencesProvider"; |
| 50 | import { ThreadRouteScreen } from "../threads/ThreadRouteScreen"; | 39 | import { ThreadRouteScreen } from "../threads/ThreadRouteScreen"; |
| 51 | import { FilePreviewLoading, FilePreviewNotice } from "./FilePreviewFeedback"; | 40 | import { FilePreviewLoading, FilePreviewNotice } from "./FilePreviewFeedback"; |
| ... | @@ -55,7 +44,7 @@ import { useFileTreeEntries } from "./useFileTreeEntries"; | ... | @@ -55,7 +44,7 @@ import { useFileTreeEntries } from "./useFileTreeEntries"; |
| 55 | import { preloadWorkspaceFileContents } from "./preload-workspace-file"; | 44 | import { preloadWorkspaceFileContents } from "./preload-workspace-file"; |
| 56 | import { SourceFileSurface } from "./SourceFileSurface"; | 45 | import { SourceFileSurface } from "./SourceFileSurface"; |
| 57 | import { ThreadFileNavigatorPane } from "./thread-file-navigator-pane"; | 46 | import { ThreadFileNavigatorPane } from "./thread-file-navigator-pane"; |
| 58 | import { MaterialFilesHeader } from "./MaterialFilesHeader"; | 47 | import { ScreenHeader } from "../../components/ScreenHeader"; |
| 59 | import { WorkspaceFileImagePreview } from "./WorkspaceFileImagePreview"; | 48 | import { WorkspaceFileImagePreview } from "./WorkspaceFileImagePreview"; |
| 60 | import { WorkspaceFilePreviewError } from "./WorkspaceFilePreviewError"; | 49 | import { WorkspaceFilePreviewError } from "./WorkspaceFilePreviewError"; |
| 61 | import { WorkspaceFileVideoPreview } from "./WorkspaceFileVideoPreview"; | 50 | import { WorkspaceFileVideoPreview } from "./WorkspaceFileVideoPreview"; |
| ... | @@ -70,6 +59,107 @@ import { | ... | @@ -70,6 +59,107 @@ import { |
| 70 | } from "./filePath"; | 59 | } from "./filePath"; |
| 71 | import { useWorkspaceFileAssetUrlState } from "./workspaceFileAssetUrl"; | 60 | import { useWorkspaceFileAssetUrlState } from "./workspaceFileAssetUrl"; |
| 72 | | 61 | |
| | 62 | function FilesBrowserHeader(props: { |
| | 63 | readonly projectName: string; |
| | 64 | readonly searchQuery: string; |
| | 65 | readonly onSearchQueryChange: (query: string) => void; |
| | 66 | readonly onRefresh: () => void; |
| | 67 | readonly onBack: () => void; |
| | 68 | }) { |
| | 69 | return ( |
| | 70 | <ScreenHeader |
| | 71 | title="Files" |
| | 72 | subtitle={props.projectName} |
| | 73 | onBack={props.onBack} |
| | 74 | hideBottomBorder |
| | 75 | matchSearchSurface |
| | 76 | search={{ |
| | 77 | value: props.searchQuery, |
| | 78 | onChangeText: props.onSearchQueryChange, |
| | 79 | placeholder: "Search files", |
| | 80 | onRefresh: props.onRefresh, |
| | 81 | refreshAccessibilityLabel: "Refresh files", |
| | 82 | closeAccessibilityLabel: "Close file search", |
| | 83 | clearAccessibilityLabel: "Clear file search", |
| | 84 | }} |
| | 85 | /> |
| | 86 | ); |
| | 87 | } |
| | 88 | |
| | 89 | function FileHeader(props: { |
| | 90 | readonly title: string; |
| | 91 | readonly subtitle: string; |
| | 92 | readonly iconColor: string; |
| | 93 | readonly activeMode: string; |
| | 94 | readonly fileInspectorSupported: boolean; |
| | 95 | readonly onBack: () => void; |
| | 96 | readonly onReturnToThread: () => void; |
| | 97 | readonly actions: ReadonlyArray<{ |
| | 98 | readonly id: string; |
| | 99 | readonly title: string; |
| | 100 | readonly icon: string; |
| | 101 | readonly inline: boolean; |
| | 102 | readonly onPress: () => unknown; |
| | 103 | }>; |
| | 104 | }) { |
| | 105 | const { panes, toggleAuxiliaryPane } = useAdaptiveWorkspaceLayout(); |
| | 106 | const modes = props.actions.filter(({ inline }) => inline); |
| | 107 | return ( |
| | 108 | <ScreenHeader |
| | 109 | title={props.title} |
| | 110 | subtitle={props.subtitle} |
| | 111 | onBack={props.onBack} |
| | 112 | hideBottomBorder |
| | 113 | options={{ headerTintColor: props.iconColor, headerTitle: props.title }} |
| | 114 | backInSplitView={ |
| | 115 | props.fileInspectorSupported |
| | 116 | ? { |
| | 117 | accessibilityLabel: "Return to chat", |
| | 118 | icon: "chevron.left", |
| | 119 | onPress: props.onReturnToThread, |
| | 120 | } |
| | 121 | : undefined |
| | 122 | } |
| | 123 | actions={ |
| | 124 | props.fileInspectorSupported |
| | 125 | ? [ |
| | 126 | { |
| | 127 | accessibilityLabel: panes.auxiliaryPaneVisible |
| | 128 | ? "Hide file navigator" |
| | 129 | : "Show file navigator", |
| | 130 | icon: "sidebar.right", |
| | 131 | selected: panes.auxiliaryPaneVisible, |
| | 132 | onPress: toggleAuxiliaryPane, |
| | 133 | }, |
| | 134 | ] |
| | 135 | : undefined |
| | 136 | } |
| | 137 | menus={[ |
| | 138 | { |
| | 139 | title: "File actions", |
| | 140 | icon: "ellipsis", |
| | 141 | separateBackground: false, |
| | 142 | items: [ |
| | 143 | ...(modes.length > 0 |
| | 144 | ? [ |
| | 145 | { |
| | 146 | id: "modes", |
| | 147 | inline: true, |
| | 148 | items: modes.map((action) => ({ |
| | 149 | ...action, |
| | 150 | selected: action.id === props.activeMode, |
| | 151 | })), |
| | 152 | }, |
| | 153 | ] |
| | 154 | : []), |
| | 155 | ...props.actions.filter(({ inline }) => !inline), |
| | 156 | ], |
| | 157 | }, |
| | 158 | ]} |
| | 159 | /> |
| | 160 | ); |
| | 161 | } |
| | 162 | |
| 73 | type FileViewMode = "preview" | "source"; | 163 | type FileViewMode = "preview" | "source"; |
| 74 | | 164 | |
| 75 | function firstRouteParam(value: string | string[] | undefined): string | null { | 165 | function firstRouteParam(value: string | string[] | undefined): string | null { |
| ... | @@ -315,14 +405,10 @@ function FilesToolbarBottomFade() { | ... | @@ -315,14 +405,10 @@ function FilesToolbarBottomFade() { |
| 315 | export function ThreadFilesTreeScreen(props: ThreadFilesRouteScreenProps) { | 405 | export function ThreadFilesTreeScreen(props: ThreadFilesRouteScreenProps) { |
| 316 | useAdaptiveWorkspacePaneRole("inspector"); | 406 | useAdaptiveWorkspacePaneRole("inspector"); |
| 317 | const navigation = useNavigation(); | 407 | const navigation = useNavigation(); |
| 318 | const { fileInspector, layout, panes, showAuxiliaryPane, togglePrimarySidebar } = | 408 | const { fileInspector, layout, showAuxiliaryPane } = useAdaptiveWorkspaceLayout(); |
| 319 | useAdaptiveWorkspaceLayout(); | | |
| 320 | const [searchQuery, setSearchQuery] = useState(""); | 409 | const [searchQuery, setSearchQuery] = useState(""); |
| 321 | const isAndroid = Platform.OS === "android"; | | |
| 322 | const { themeAppearance: highlightTheme } = useAppearancePreferences(); | 410 | const { themeAppearance: highlightTheme } = useAppearancePreferences(); |
| 323 | const theme = useUniwindTheme(); | 411 | const headerColor = useUniwindTheme()["--color-header"]; |
| 324 | const headerColor = theme["--color-header"]; | | |
| 325 | const sheetSurfaceColor = theme["--color-sheet-solid"]; | | |
| 326 | const { cwd, environmentId, projectName, selectedThread, threadId } = useThreadFilesWorkspace( | 412 | const { cwd, environmentId, projectName, selectedThread, threadId } = useThreadFilesWorkspace( |
| 327 | props.route.params, | 413 | props.route.params, |
| 328 | ); | 414 | ); |
| ... | @@ -430,84 +516,15 @@ export function ThreadFilesTreeScreen(props: ThreadFilesRouteScreenProps) { | ... | @@ -430,84 +516,15 @@ export function ThreadFilesTreeScreen(props: ThreadFilesRouteScreenProps) { |
| 430 | ); | 516 | ); |
| 431 | } | 517 | } |
| 432 | | 518 | |
| 433 | const usesCompactMailToolbar = | | |
| 434 | Platform.OS === "ios" && !layout.usesSplitView && NATIVE_MAIL_SEARCH_TOOLBAR_SUPPORTED; | | |
| 435 | | | |
| 436 | const content = ( | 519 | const content = ( |
| 437 | <> | 520 | <> |
| 438 | {/* Static header config (glass preset and title) lives in Stack.tsx. The | 521 | <FilesBrowserHeader |
| 439 | live sheet color stays dynamic here so the FlatList can remain the | 522 | projectName={projectName} |
| 440 | direct scene child for native scroll-edge sampling. */} | 523 | searchQuery={searchQuery} |
| 441 | <NativeStackScreenOptions | 524 | onSearchQueryChange={setSearchQuery} |
| 442 | options={{ | 525 | onRefresh={entriesQuery.refresh} |
| 443 | contentStyle: { | 526 | onBack={handleReturnToThread} |
| 444 | backgroundColor: Platform.OS === "android" ? headerColor : sheetSurfaceColor, | | |
| 445 | }, | | |
| 446 | headerShown: !isAndroid, | | |
| 447 | unstable_headerSubtitle: | | |
| 448 | Platform.OS === "ios" && projectName.length > 0 ? projectName : undefined, | | |
| 449 | // No refresh button: the list already supports pull-to-refresh. | | |
| 450 | unstable_headerToolbarItems: usesCompactMailToolbar | | |
| 451 | ? () => [ | | |
| 452 | createNativeMailSearchToolbarItem({ | | |
| 453 | onSearchTextChange: setSearchQuery, | | |
| 454 | placeholder: "Search files", | | |
| 455 | searchTextChangeId: "files-search-text", | | |
| 456 | }), | | |
| 457 | ] | | |
| 458 | : undefined, | | |
| 459 | headerSearchBarOptions: usesCompactMailToolbar | | |
| 460 | ? undefined | | |
| 461 | : { | | |
| 462 | allowToolbarIntegration: true, | | |
| 463 | autoCapitalize: "none", | | |
| 464 | hideNavigationBar: false, | | |
| 465 | placeholder: "Search files", | | |
| 466 | onChangeText: (event) => { | | |
| 467 | setSearchQuery(event.nativeEvent.text); | | |
| 468 | }, | | |
| 469 | onCancelButtonPress: () => { | | |
| 470 | setSearchQuery(""); | | |
| 471 | }, | | |
| 472 | }, | | |
| 473 | }} | | |
| 474 | /> | 527 | /> |
| 475 | {isAndroid ? ( | | |
| 476 | <> | | |
| 477 | { | | |
| 478 | <MaterialFilesHeader | | |
| 479 | projectName={projectName} | | |
| 480 | leading={<AndroidWorkspaceSidebarButton />} | | |
| 481 | searchQuery={searchQuery} | | |
| 482 | onSearchQueryChange={setSearchQuery} | | |
| 483 | onRefresh={entriesQuery.refresh} | | |
| 484 | onBack={handleReturnToThread} | | |
| 485 | /> | | |
| 486 | } | | |
| 487 | </> | | |
| 488 | ) : ( | | |
| 489 | <> | | |
| 490 | {layout.usesSplitView ? ( | | |
| 491 | <NativeHeaderToolbar placement="left"> | | |
| 492 | <NativeHeaderToolbar.Button | | |
| 493 | accessibilityLabel={panes.primarySidebarVisible ? "Maximize files" : "Show threads"} | | |
| 494 | icon={ | | |
| 495 | panes.primarySidebarVisible | | |
| 496 | ? "arrow.up.left.and.arrow.down.right" | | |
| 497 | : "sidebar.left" | | |
| 498 | } | | |
| 499 | onPress={togglePrimarySidebar} | | |
| 500 | separateBackground | | |
| 501 | /> | | |
| 502 | </NativeHeaderToolbar> | | |
| 503 | ) : null} | | |
| 504 | {usesCompactMailToolbar ? null : ( | | |
| 505 | <NativeHeaderToolbar placement="bottom"> | | |
| 506 | <NativeHeaderToolbar.SearchBarSlot /> | | |
| 507 | </NativeHeaderToolbar> | | |
| 508 | )} | | |
| 509 | </> | | |
| 510 | )} | | |
| 511 | <MaterialScreenContent insetHorizontal={layout.usesSplitView}> | 528 | <MaterialScreenContent insetHorizontal={layout.usesSplitView}> |
| 512 | <FileTreeBrowser | 529 | <FileTreeBrowser |
| 513 | key={JSON.stringify([environmentId, cwd])} | 530 | key={JSON.stringify([environmentId, cwd])} |
| ... | @@ -540,10 +557,9 @@ export function ThreadFilesTreeScreen(props: ThreadFilesRouteScreenProps) { | ... | @@ -540,10 +557,9 @@ export function ThreadFilesTreeScreen(props: ThreadFilesRouteScreenProps) { |
| 540 | export function ThreadFileScreen(props: ThreadFileRouteScreenProps) { | 557 | export function ThreadFileScreen(props: ThreadFileRouteScreenProps) { |
| 541 | useAdaptiveWorkspacePaneRole("inspector"); | 558 | useAdaptiveWorkspacePaneRole("inspector"); |
| 542 | const navigation = useNavigation(); | 559 | const navigation = useNavigation(); |
| 543 | const { fileInspector, panes, toggleAuxiliaryPane } = useAdaptiveWorkspaceLayout(); | 560 | const { fileInspector } = useAdaptiveWorkspaceLayout(); |
| 544 | const { appearance, setCodeWordBreak } = useAppearancePreferences(); | 561 | const { appearance, setCodeWordBreak } = useAppearancePreferences(); |
| 545 | const iconColor = useUniwindTheme()["--color-icon"]; | 562 | const iconColor = useUniwindTheme()["--color-icon"]; |
| 546 | const isAndroid = Platform.OS === "android"; | | |
| 547 | const params = props.route.params; | 563 | const params = props.route.params; |
| 548 | const relativePath = normalizeRoutePath(params.path); | 564 | const relativePath = normalizeRoutePath(params.path); |
| 549 | const targetLine = normalizeRouteLine(firstRouteParam(params.line)); | 565 | const targetLine = normalizeRouteLine(firstRouteParam(params.line)); |
| ... | @@ -819,23 +835,6 @@ export function ThreadFileScreen(props: ThreadFileRouteScreenProps) { | ... | @@ -819,23 +835,6 @@ export function ThreadFileScreen(props: ThreadFileRouteScreenProps) { |
| 819 | fileData?.truncated, | 835 | fileData?.truncated, |
| 820 | ]); | 836 | ]); |
| 821 | | 837 | |
| 822 | const androidFileMenuActions = useMemo<MenuAction[]>( | | |
| 823 | () => | | |
| 824 | fileMenuActions.map((action) => ({ | | |
| 825 | id: action.id, | | |
| 826 | title: action.title, | | |
| 827 | image: action.icon, | | |
| 828 | state: action.id === resolvedActiveMode ? "on" : undefined, | | |
| 829 | })), | | |
| 830 | [fileMenuActions, resolvedActiveMode], | | |
| 831 | ); | | |
| 832 | const handleAndroidFileMenuAction = useCallback( | | |
| 833 | (event: { nativeEvent: { event: string } }) => { | | |
| 834 | const action = fileMenuActions.find(({ id }) => id === event.nativeEvent.event); | | |
| 835 | void action?.onPress(); | | |
| 836 | }, | | |
| 837 | [fileMenuActions], | | |
| 838 | ); | | |
| 839 | const handleReturnToThread = useCallback(() => { | 838 | const handleReturnToThread = useCallback(() => { |
| 840 | if (environmentId !== null && threadId !== null) { | 839 | if (environmentId !== null && threadId !== null) { |
| 841 | navigation.dispatch( | 840 | navigation.dispatch( |
| ... | @@ -878,100 +877,16 @@ export function ThreadFileScreen(props: ThreadFileRouteScreenProps) { | ... | @@ -878,100 +877,16 @@ export function ThreadFileScreen(props: ThreadFileRouteScreenProps) { |
| 878 | | 877 | |
| 879 | return ( | 878 | return ( |
| 880 | <View className="flex-1 bg-sheet"> | 879 | <View className="flex-1 bg-sheet"> |
| 881 | <NativeStackScreenOptions | 880 | <FileHeader |
| 882 | options={{ | 881 | title={basename(relativePath)} |
| 883 | // Static header config lives in Stack.tsx (SOLID_HEADER_OPTIONS: solid | 882 | subtitle={headerSubtitle} |
| 884 | // sheet-colored header — this route's content scrolls internally, so | 883 | iconColor={iconColor} |
| 885 | // there is nothing for glass to sample). Only dynamic values here. | 884 | activeMode={resolvedActiveMode} |
| 886 | headerShown: !isAndroid, | 885 | fileInspectorSupported={fileInspector.supported} |
| 887 | headerTintColor: iconColor, | 886 | onBack={handleBack} |
| 888 | headerTitle: basename(relativePath), | 887 | onReturnToThread={handleReturnToThread} |
| 889 | title: basename(relativePath), | 888 | actions={fileMenuActions} |
| 890 | unstable_headerSubtitle: | | |
| 891 | Platform.OS === "ios" && headerSubtitle.length > 0 ? headerSubtitle : undefined, | | |
| 892 | }} | | |
| 893 | /> | 889 | /> |
| 894 | {isAndroid ? ( | | |
| 895 | <AndroidScreenHeader | | |
| 896 | title={basename(relativePath)} | | |
| 897 | subtitle={headerSubtitle} | | |
| 898 | leading={<AndroidWorkspaceSidebarButton />} | | |
| 899 | hideBottomBorder | | |
| 900 | onBack={handleBack} | | |
| 901 | trailing={ | | |
| 902 | <> | | |
| 903 | {fileInspector.supported ? ( | | |
| 904 | <AndroidHeaderIconButton | | |
| 905 | accessibilityLabel={ | | |
| 906 | panes.auxiliaryPaneVisible ? "Hide file navigator" : "Show file navigator" | | |
| 907 | } | | |
| 908 | icon="sidebar.right" | | |
| 909 | selected={panes.auxiliaryPaneVisible} | | |
| 910 | onPress={toggleAuxiliaryPane} | | |
| 911 | /> | | |
| 912 | ) : null} | | |
| 913 | <ControlPillMenu | | |
| 914 | actions={androidFileMenuActions} | | |
| 915 | isAnchoredToRight | | |
| 916 | title="File actions" | | |
| 917 | onPressAction={handleAndroidFileMenuAction} | | |
| 918 | > | | |
| 919 | <AndroidHeaderIconButton accessibilityLabel="File actions" icon="ellipsis" /> | | |
| 920 | </ControlPillMenu> | | |
| 921 | </> | | |
| 922 | } | | |
| 923 | /> | | |
| 924 | ) : null} | | |
| 925 | <WorkspaceSidebarToolbar> | | |
| 926 | {fileInspector.supported ? ( | | |
| 927 | <NativeHeaderToolbar.Button | | |
| 928 | accessibilityLabel="Return to chat" | | |
| 929 | icon="chevron.left" | | |
| 930 | onPress={handleReturnToThread} | | |
| 931 | /> | | |
| 932 | ) : null} | | |
| 933 | </WorkspaceSidebarToolbar> | | |
| 934 | <NativeHeaderToolbar placement="right"> | | |
| 935 | {fileInspector.supported ? ( | | |
| 936 | <NativeHeaderToolbar.Button | | |
| 937 | accessibilityLabel={ | | |
| 938 | panes.auxiliaryPaneVisible ? "Hide file navigator" : "Show file navigator" | | |
| 939 | } | | |
| 940 | icon="sidebar.right" | | |
| 941 | onPress={toggleAuxiliaryPane} | | |
| 942 | separateBackground | | |
| 943 | /> | | |
| 944 | ) : null} | | |
| 945 | <NativeHeaderToolbar.Menu accessibilityLabel="File actions" icon="ellipsis"> | | |
| 946 | {fileMenuActions.some(({ inline }) => inline) ? ( | | |
| 947 | <NativeHeaderToolbar.Menu inline> | | |
| 948 | {fileMenuActions | | |
| 949 | .filter(({ inline }) => inline) | | |
| 950 | .map((action) => ( | | |
| 951 | <NativeHeaderToolbar.MenuAction | | |
| 952 | key={action.id} | | |
| 953 | icon={action.icon} | | |
| 954 | isOn={action.id === resolvedActiveMode} | | |
| 955 | onPress={action.onPress} | | |
| 956 | > | | |
| 957 | {action.title} | | |
| 958 | </NativeHeaderToolbar.MenuAction> | | |
| 959 | ))} | | |
| 960 | </NativeHeaderToolbar.Menu> | | |
| 961 | ) : null} | | |
| 962 | {fileMenuActions | | |
| 963 | .filter(({ inline }) => !inline) | | |
| 964 | .map((action) => ( | | |
| 965 | <NativeHeaderToolbar.MenuAction | | |
| 966 | key={action.id} | | |
| 967 | icon={action.icon} | | |
| 968 | onPress={action.onPress} | | |
| 969 | > | | |
| 970 | {action.title} | | |
| 971 | </NativeHeaderToolbar.MenuAction> | | |
| 972 | ))} | | |
| 973 | </NativeHeaderToolbar.Menu> | | |
| 974 | </NativeHeaderToolbar> | | |
| 975 | <MaterialScreenContent> | 890 | <MaterialScreenContent> |
| 976 | <FileContent | 891 | <FileContent |
| 977 | key={previewKey} | 892 | key={previewKey} |