import React from 'react'; import { ActivityIndicator, Image, Pressable, Text, View } from 'react-native'; import { AlertTriangle, File as FileIcon, FileImage, FileSpreadsheet, FileText, FileType as FileTypeGlyph, Presentation, Video, type LucideIcon, } from 'lucide-react-native'; import { FILE_ATTACHMENT_SIZE, fileAttachmentStyles } from './fileAttachmentStyles'; import type { FileType } from './mediaUtils'; // File-type glyph + accent color, mirroring the web DS `FileAttachment` // (`file-icons.tsx` FILE_TYPE_ICON_MAP → `--sem-file-icon-*`). The accent hexes // are brand-fixed and identical in light/dark, exactly like the web tokens, so // they're inlined here rather than routed through the theme mapper; `upload` // stays a neutral muted foreground. const FILE_TYPE_ICON: Record = { pdf: { Icon: FileText, color: '#DF3336' }, image: { Icon: FileImage, color: '#F0ABFC' }, slides: { Icon: Presentation, color: '#DDB440' }, spreadsheet: { Icon: FileSpreadsheet, color: '#347043' }, docs: { Icon: FileTypeGlyph, color: '#3B51E5' }, video: { Icon: Video, color: '#7E73F2' }, upload: { Icon: FileIcon, color: '#A1A1AA' }, }; export type FileAttachmentVariant = 'file' | 'image'; export type FileAttachmentSize = 'medium' | 'small'; export type FileAttachmentState = 'default' | 'loading' | 'error'; export interface FileAttachmentProps { /** Layout treatment: a labelled chip (`file`) or a square image thumbnail (`image`). @default "file" */ variant?: FileAttachmentVariant; /** Size variant. @default "medium" */ attachmentSize?: FileAttachmentSize; /** Attachment state — default, loading (uploading), or error (failed). @default "default" */ state?: FileAttachmentState; /** File type — determines the leading icon in the `file` variant. @default "upload" */ fileType?: FileType; /** File name shown in the chip (and used as the image `alt`/accessibility label). */ name?: string; /** Image source URL for the `image` variant. */ src?: string; /** Whether the remove (×) button is shown. @default false */ removable?: boolean; /** Called when the remove button is pressed. */ onRemove?: () => void; /** Accessibility label for the remove button. @default `Remove ${name}` */ removeAriaLabel?: string; /** Called when the `file` chip itself is pressed (e.g. to open the file). Makes the chip tappable. */ onPress?: () => void; } // A remove (×) badge straddling the top-right corner — identical for every // attachment type (image thumbnail or file chip). function RemoveButton({ label, onRemove }: { label: string; onRemove?: () => void }) { return ( × ); } /** * An uploaded file shown as a labelled chip (`file`) or a square image * thumbnail (`image`) — the native mirror of the web DS `FileAttachment`. * Used in the composer's attachment strip and in sent chat messages. */ export function FileAttachment({ variant = 'file', attachmentSize = 'medium', state = 'default', fileType = 'upload', name, src, removable = false, onRemove, removeAriaLabel, onPress, }: FileAttachmentProps) { const sc = FILE_ATTACHMENT_SIZE[attachmentSize]; const isError = state === 'error'; const isLoading = state === 'loading'; const label = removeAriaLabel ?? (name ? `Remove ${name}` : 'Remove attachment'); const showRemove = removable && Boolean(onRemove); if (variant === 'image') { const imageEl = ( {isLoading ? ( ) : isError ? ( ) : src ? ( ) : null} {showRemove ? : null} ); return showRemove ? {imageEl} : imageEl; } const { Icon, color } = FILE_TYPE_ICON[fileType]; const chipStyle = [fileAttachmentStyles.chip, isError && fileAttachmentStyles.errorBorder, { height: sc.chip }]; const chipContent = ( <> {isLoading ? ( ) : isError ? ( ) : ( )} {name ? ( {name} ) : null} {showRemove ? : null} ); const chipEl = onPress ? ( {chipContent} ) : ( {chipContent} ); return showRemove ? {chipEl} : chipEl; }