import React, { createRef, useCallback, useMemo, useState } from "react"; import { TouchableOpacity, View } from "react-native"; import { useStyles } from "./FormatFile.style"; import { MediaView } from "./components/MediaView"; import type { BottomSheetOptionsSuffix } from "./components/FormatFileBottomSheet"; import { FormatFileBottomSheet } from "./components/FormatFileBottomSheet"; import { FileView } from "./components/FileView"; import type { CreateThumbnail, File, FileUpload, FormattedFile } from "./types"; import { StatusCode } from "./types"; import { AtlantisFormatFileContext } from "./context/FormatFileContext"; import { createUseCreateThumbnail } from "./utils/createUseCreateThumbnail"; import { parseFile } from "./utils/parseFile"; import type { BottomSheetRef } from "../BottomSheet/BottomSheet"; import { useAtlantisI18n } from "../hooks/useAtlantisI18n"; export interface FormatFileProps { /** * File upload details object. Can be a File or a FileUpload */ readonly file: T; /** * Accessibility label */ readonly accessibilityLabel?: string; /** * Accessibility hint */ readonly accessibilityHint?: string; /** * A function which handles the onTap event. */ readonly onTap?: (file: T) => void; /** * A function to be called on "Remove" Bottom Sheet Option press */ readonly onRemove?: () => void; /** * Handler for the "Preview" Bottom Sheet Option press */ readonly onPreviewPress?: (formattedFile: FormattedFile) => void; /** * A file type to show at Bottom Sheet options */ readonly bottomSheetOptionsSuffix?: BottomSheetOptionsSuffix; /** * Uses a grid layout when multi-file upload is supported */ readonly styleInGrid?: boolean; /** * A reference to the element in the rendered output */ readonly testID?: string; /** * Set false to hide all file type iconography, including the video fallback * placeholder icon. */ readonly showFileTypeIndicator?: boolean; readonly createThumbnail?: CreateThumbnail; } type FormatFileInternalProps = Omit< FormatFileProps, "file" | "onTap" > & { readonly file: FormattedFile; readonly onTap: () => void; }; interface FormatFileContentProps { readonly accessibilityLabel?: string; readonly file: FormattedFile; readonly showOverlay: boolean; readonly styleInGrid: boolean; readonly onUploadComplete: () => void; readonly isMedia: boolean; /** * @internal * When true, the component skips its container wrapper entirely * (no border, background, or dimension styles). Use this when the * parent component handles container styling directly, such as in * FormatFileThumbnail. */ readonly skipContainerStyles?: boolean; /** * @internal * A function to be called when the media has loaded. * This is only used in FormatFileThumbnail. */ readonly onMediaLoadEnd?: () => void; /** * Optional override for the tile's surface colour, forwarded to the * underlying `FileView` or `MediaView`. Only meaningful when * `skipContainerStyles` is true and the consumer (e.g. * `FormatFileThumbnail`) is wrapping this content directly. */ readonly surfaceColor?: string; } export function FormatFileContent({ accessibilityLabel, file, showOverlay, styleInGrid, onUploadComplete, isMedia, skipContainerStyles = false, onMediaLoadEnd, surfaceColor, }: FormatFileContentProps) { const styles = useStyles(); const content = isMedia ? ( ) : ( ); if (skipContainerStyles) { return content; } return ( {content} ); } const FormatFileInternalMemoized = React.memo(FormatFileInternal); export function FormatFile({ file, accessibilityLabel, accessibilityHint, onTap, onRemove, bottomSheetOptionsSuffix, styleInGrid = false, testID, showFileTypeIndicator = true, createThumbnail, onPreviewPress, }: FormatFileProps) { const onTapModified = onTap ? () => onTap(file) : () => undefined; const formattedFile = useMemo( () => parseFile(file, showFileTypeIndicator), [file, showFileTypeIndicator], ); return ( ); } function FormatFileInternal({ file, accessibilityLabel, accessibilityHint, onTap, onRemove, bottomSheetOptionsSuffix, styleInGrid = false, onPreviewPress, testID, createThumbnail: createThumbnailProp, }: FormatFileInternalProps) { const [showOverlay, setShowOverlay] = useState( file.status !== StatusCode.Completed, ); const { t } = useAtlantisI18n(); const bottomSheetRef = createRef(); const handlePreviewPress = useCallback(() => { onPreviewPress?.(file); }, [file, onPreviewPress]); const createThumbnail = createThumbnailProp ? createThumbnailProp : async () => ({ error: false, thumbnail: "" }); const { useCreateThumbnail } = createUseCreateThumbnail(createThumbnail); return ( setShowOverlay(false)} isMedia={!!file.isMedia} styleInGrid={styleInGrid} showOverlay={showOverlay} /> ); function handleOnPress() { if (showOverlay || !onRemove) { onTap(); return; } bottomSheetRef.current?.open(); } }