UNPKG

@oxyhq/services

Version:

OxyHQ Expo/React Native SDK — UI components, screens, and native features

402 lines (396 loc) 15.1 kB
"use strict"; import React, { useMemo, useState, useEffect } from 'react'; import { View, Text, TouchableOpacity, ScrollView, ActivityIndicator, Image } from 'react-native'; import { Image as ExpoImage } from 'expo-image'; // @ts-ignore - MaterialCommunityIcons is available at runtime import { MaterialCommunityIcons } from '@expo/vector-icons'; import { formatFileSize } from "../../utils/fileManagement.js"; import { fileManagementStyles } from "./styles.js"; import { GroupedSection } from "../GroupedSection.js"; import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime"; export const FileViewer = ({ file, fileContent, loadingFileContent, showFileDetailsInViewer, onToggleDetails, onClose, onDownload, onDelete, themeStyles, isOwner }) => { const isImage = file.contentType.startsWith('image/'); const isText = file.contentType.startsWith('text/') || file.contentType.includes('json') || file.contentType.includes('xml') || file.contentType.includes('javascript') || file.contentType.includes('typescript'); const isPDF = file.contentType.includes('pdf'); const isVideo = file.contentType.startsWith('video/'); const isAudio = file.contentType.startsWith('audio/'); const backgroundColor = isImage && fileContent ? 'transparent' : themeStyles.backgroundColor; const borderColor = themeStyles.borderColor; const [containerWidth, setContainerWidth] = useState(0); const [imageDimensions, setImageDimensions] = useState(null); // Load image dimensions when image content is available useEffect(() => { if (isImage && fileContent) { Image.getSize(fileContent, (width, height) => { setImageDimensions({ width, height }); }, () => { // Fallback if dimensions can't be loaded setImageDimensions({ width: 400, height: 400 }); }); } else { setImageDimensions(null); } }, [isImage, fileContent]); // Calculate display size based on natural dimensions and max constraints // Use natural size when smaller, scale down when larger const imageDisplaySize = useMemo(() => { if (!imageDimensions || !containerWidth) { // Return default size while loading return { width: 400, height: 400 }; } const maxWidth = containerWidth - 24; // Account for padding const maxHeight = 500; const aspectRatio = imageDimensions.width / imageDimensions.height; // Start with natural dimensions let displayWidth = imageDimensions.width; let displayHeight = imageDimensions.height; // Only scale down if exceeds max width if (displayWidth > maxWidth) { displayWidth = maxWidth; displayHeight = displayWidth / aspectRatio; } // Only scale down if exceeds max height if (displayHeight > maxHeight) { displayHeight = maxHeight; displayWidth = displayHeight * aspectRatio; } return { width: displayWidth, height: displayHeight }; }, [imageDimensions, containerWidth]); const fileDetailItems = useMemo(() => { const items = [{ id: 'filename', icon: 'file-document', iconColor: themeStyles.colors.iconSecurity, title: 'File Name', subtitle: file.filename }, { id: 'size', icon: 'server', iconColor: themeStyles.colors.iconStorage, title: 'Size', subtitle: formatFileSize(file.length) }, { id: 'type', icon: 'code-tags', iconColor: themeStyles.colors.iconData, title: 'Type', subtitle: file.contentType }, { id: 'uploaded', icon: 'clock', iconColor: themeStyles.colors.iconPersonalInfo, title: 'Uploaded', subtitle: new Date(file.uploadDate).toLocaleString() }]; if (file.metadata?.description) { items.push({ id: 'description', icon: 'text', iconColor: themeStyles.colors.iconData, title: 'Description', subtitle: file.metadata.description }); } items.push({ id: 'fileId', icon: 'key', iconColor: themeStyles.colors.iconSecurity, title: 'File ID', subtitle: file.id }); return items; }, [file, themeStyles.colors]); return /*#__PURE__*/_jsxs(View, { style: [fileManagementStyles.fileViewerContainer, { backgroundColor }], children: [isImage && fileContent && /*#__PURE__*/_jsxs(_Fragment, { children: [/*#__PURE__*/_jsx(ExpoImage, { source: { uri: fileContent }, style: fileManagementStyles.backgroundImage, contentFit: "cover", blurRadius: 50, transition: 120, cachePolicy: "memory-disk" }), /*#__PURE__*/_jsx(View, { style: [fileManagementStyles.backgroundOverlay, { backgroundColor: themeStyles.isDarkTheme ? 'rgba(0, 0, 0, 0.6)' : 'rgba(255, 255, 255, 0.85)' }] })] }), /*#__PURE__*/_jsx(TouchableOpacity, { style: [fileManagementStyles.floatingBackButton, { backgroundColor: themeStyles.colors.card }], onPress: onClose, children: /*#__PURE__*/_jsx(MaterialCommunityIcons, { name: "arrow-left", size: 20, color: themeStyles.textColor }) }), /*#__PURE__*/_jsx(TouchableOpacity, { style: [fileManagementStyles.floatingDownloadButton, { backgroundColor: themeStyles.colors.card }], onPress: () => onDownload(file.id, file.filename), children: /*#__PURE__*/_jsx(MaterialCommunityIcons, { name: "download", size: 20, color: themeStyles.primaryColor }) }), /*#__PURE__*/_jsx(ScrollView, { style: fileManagementStyles.fileViewerContent, contentContainerStyle: fileManagementStyles.fileViewerContentContainer, children: loadingFileContent ? /*#__PURE__*/_jsxs(View, { style: fileManagementStyles.fileViewerLoading, children: [/*#__PURE__*/_jsx(ActivityIndicator, { size: "large", color: themeStyles.primaryColor }), /*#__PURE__*/_jsx(Text, { style: [fileManagementStyles.fileViewerLoadingText, { color: themeStyles.textColor }], children: "Loading file content..." })] }) : isImage && fileContent ? /*#__PURE__*/_jsx(View, { style: fileManagementStyles.imageContainer, onLayout: e => { const width = e.nativeEvent.layout.width; if (width > 0) { setContainerWidth(width); } }, children: /*#__PURE__*/_jsx(View, { style: [fileManagementStyles.imageWrapper, { width: imageDisplaySize.width, height: imageDisplaySize.height }], children: /*#__PURE__*/_jsx(ExpoImage, { source: { uri: fileContent }, style: { width: imageDisplaySize.width, height: imageDisplaySize.height }, contentFit: "contain", transition: 120, cachePolicy: "memory-disk", onError: () => { // Image failed to load }, accessibilityLabel: file.filename }) }) }) : isText && fileContent ? /*#__PURE__*/_jsx(View, { style: [fileManagementStyles.textContainer, { backgroundColor: themeStyles.colors.card }], children: /*#__PURE__*/_jsx(ScrollView, { style: { flex: 1 }, nestedScrollEnabled: true, children: /*#__PURE__*/_jsx(Text, { style: [fileManagementStyles.textContent, { color: themeStyles.textColor }], children: fileContent }) }) }) : isPDF && fileContent ? /*#__PURE__*/_jsxs(View, { style: fileManagementStyles.unsupportedFileContainer, children: [/*#__PURE__*/_jsx(MaterialCommunityIcons, { name: "file-pdf-box", size: 64, color: themeStyles.colors.secondaryText }), /*#__PURE__*/_jsx(Text, { style: [fileManagementStyles.unsupportedFileTitle, { color: themeStyles.textColor }], children: "PDF Preview Not Available" }), /*#__PURE__*/_jsxs(Text, { style: [fileManagementStyles.unsupportedFileDescription, { color: themeStyles.colors.secondaryText }], children: ["PDF files cannot be previewed in this viewer.", '\n', "Download the file to view its contents."] }), /*#__PURE__*/_jsxs(TouchableOpacity, { style: [fileManagementStyles.downloadButtonLarge, { backgroundColor: themeStyles.primaryColor }], onPress: () => onDownload(file.id, file.filename), children: [/*#__PURE__*/_jsx(MaterialCommunityIcons, { name: "download", size: 18, color: "#FFFFFF" }), /*#__PURE__*/_jsx(Text, { style: fileManagementStyles.downloadButtonText, children: "Download PDF" })] })] }) : isVideo && fileContent ? /*#__PURE__*/_jsxs(View, { style: fileManagementStyles.unsupportedFileContainer, children: [/*#__PURE__*/_jsx(MaterialCommunityIcons, { name: "video-outline", size: 64, color: themeStyles.colors.secondaryText }), /*#__PURE__*/_jsx(Text, { style: [fileManagementStyles.unsupportedFileTitle, { color: themeStyles.textColor }], children: "Video Playback Not Available" }), /*#__PURE__*/_jsxs(Text, { style: [fileManagementStyles.unsupportedFileDescription, { color: themeStyles.colors.secondaryText }], children: ["Video playback is not supported in this viewer.", '\n', "Download the file to view it."] }), /*#__PURE__*/_jsxs(TouchableOpacity, { style: [fileManagementStyles.downloadButtonLarge, { backgroundColor: themeStyles.primaryColor }], onPress: () => onDownload(file.id, file.filename), children: [/*#__PURE__*/_jsx(MaterialCommunityIcons, { name: "download", size: 18, color: "#FFFFFF" }), /*#__PURE__*/_jsx(Text, { style: fileManagementStyles.downloadButtonText, children: "Download Video" })] })] }) : isAudio && fileContent ? /*#__PURE__*/_jsxs(View, { style: fileManagementStyles.unsupportedFileContainer, children: [/*#__PURE__*/_jsx(MaterialCommunityIcons, { name: "music-note-outline", size: 64, color: themeStyles.colors.secondaryText }), /*#__PURE__*/_jsx(Text, { style: [fileManagementStyles.unsupportedFileTitle, { color: themeStyles.textColor }], children: "Audio Playback Not Available" }), /*#__PURE__*/_jsxs(Text, { style: [fileManagementStyles.unsupportedFileDescription, { color: themeStyles.colors.secondaryText }], children: ["Audio playback is not supported in this viewer.", '\n', "Download the file to listen to it."] }), /*#__PURE__*/_jsxs(TouchableOpacity, { style: [fileManagementStyles.downloadButtonLarge, { backgroundColor: themeStyles.primaryColor }], onPress: () => onDownload(file.id, file.filename), children: [/*#__PURE__*/_jsx(MaterialCommunityIcons, { name: "download", size: 18, color: "#FFFFFF" }), /*#__PURE__*/_jsx(Text, { style: fileManagementStyles.downloadButtonText, children: "Download Audio" })] })] }) : /*#__PURE__*/_jsxs(View, { style: fileManagementStyles.unsupportedFileContainer, children: [/*#__PURE__*/_jsx(MaterialCommunityIcons, { name: "file-outline", size: 64, color: themeStyles.colors.secondaryText }), /*#__PURE__*/_jsx(Text, { style: [fileManagementStyles.unsupportedFileTitle, { color: themeStyles.textColor }], children: "Preview Not Available" }), /*#__PURE__*/_jsxs(Text, { style: [fileManagementStyles.unsupportedFileDescription, { color: themeStyles.colors.secondaryText }], children: ["This file type cannot be previewed.", '\n', "Download the file to view its contents."] }), /*#__PURE__*/_jsxs(TouchableOpacity, { style: [fileManagementStyles.downloadButtonLarge, { backgroundColor: themeStyles.primaryColor }], onPress: () => onDownload(file.id, file.filename), children: [/*#__PURE__*/_jsx(MaterialCommunityIcons, { name: "download", size: 18, color: "#FFFFFF" }), /*#__PURE__*/_jsx(Text, { style: fileManagementStyles.downloadButtonText, children: "Download File" })] })] }) }), /*#__PURE__*/_jsxs(View, { style: [fileManagementStyles.fileDetailsSection, { backgroundColor: themeStyles.colors.card }], children: [/*#__PURE__*/_jsxs(View, { style: fileManagementStyles.fileDetailsSectionHeader, children: [/*#__PURE__*/_jsx(Text, { style: [fileManagementStyles.fileDetailsSectionTitle, { color: themeStyles.textColor }], children: "File Details" }), /*#__PURE__*/_jsx(TouchableOpacity, { style: fileManagementStyles.fileDetailsSectionToggle, onPress: onToggleDetails, children: /*#__PURE__*/_jsx(MaterialCommunityIcons, { name: showFileDetailsInViewer ? "chevron-up" : "chevron-down", size: 20, color: themeStyles.colors.secondaryText }) })] }), showFileDetailsInViewer && /*#__PURE__*/_jsxs(_Fragment, { children: [/*#__PURE__*/_jsx(View, { style: fileManagementStyles.fileDetailsSectionContent, children: /*#__PURE__*/_jsx(GroupedSection, { items: fileDetailItems }) }), isOwner && /*#__PURE__*/_jsx(View, { style: fileManagementStyles.fileDetailsActions, children: /*#__PURE__*/_jsxs(TouchableOpacity, { style: [fileManagementStyles.fileDetailsActionButton, { backgroundColor: themeStyles.dangerColor }], onPress: () => { onClose(); onDelete(file.id, file.filename); }, children: [/*#__PURE__*/_jsx(MaterialCommunityIcons, { name: "delete", size: 16, color: "#FFFFFF" }), /*#__PURE__*/_jsx(Text, { style: fileManagementStyles.fileDetailsActionText, children: "Delete" })] }) })] })] })] }); }; //# sourceMappingURL=FileViewer.js.map