@oxyhq/services
Version:
402 lines (396 loc) • 15.1 kB
JavaScript
"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