trunicate file name

This commit is contained in:
chark1es 2025-02-16 17:02:53 -08:00
parent 15660161a7
commit 4f01f67587

View file

@ -1,4 +1,8 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState, useCallback, useMemo } from 'react';
// Cache for file content
const contentCache = new Map<string, { content: string | 'image' | 'video' | 'pdf', fileType: string, timestamp: number }>();
const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes
interface FilePreviewProps { interface FilePreviewProps {
url?: string; url?: string;
@ -13,6 +17,100 @@ const FilePreview: React.FC<FilePreviewProps> = ({ url: initialUrl = '', filenam
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [fileType, setFileType] = useState<string | null>(null); const [fileType, setFileType] = useState<string | null>(null);
const [isVisible, setIsVisible] = useState(false);
// Memoize the truncated filename
const truncatedFilename = useMemo(() => {
if (!filename) return '';
const maxLength = 40;
if (filename.length <= maxLength) return filename;
const extension = filename.split('.').pop();
const name = filename.substring(0, filename.lastIndexOf('.'));
const truncatedName = name.substring(0, maxLength - 3 - (extension?.length || 0));
return `${truncatedName}...${extension ? `.${extension}` : ''}`;
}, [filename]);
// Intersection Observer callback
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
setIsVisible(entry.isIntersecting);
},
{ threshold: 0.1 }
);
const previewElement = document.querySelector('.preview-content');
if (previewElement) {
observer.observe(previewElement);
}
return () => observer.disconnect();
}, []);
const loadContent = useCallback(async () => {
if (!url || !filename) return;
console.log('Loading content for:', { url, filename });
setLoading(true);
setError(null);
// Check cache first
const cacheKey = `${url}_${filename}`;
const cachedData = contentCache.get(cacheKey);
if (cachedData && Date.now() - cachedData.timestamp < CACHE_DURATION) {
console.log('Using cached content');
setContent(cachedData.content);
setFileType(cachedData.fileType);
setLoading(false);
return;
}
try {
console.log('Fetching file...');
const response = await fetch(url);
const contentType = response.headers.get('content-type');
console.log('Received content type:', contentType);
setFileType(contentType);
let contentValue: string | 'image' | 'video' | 'pdf';
if (contentType?.startsWith('image/')) {
contentValue = 'image';
} else if (contentType?.startsWith('video/')) {
contentValue = 'video';
} else if (contentType?.startsWith('application/pdf')) {
contentValue = 'pdf';
} else if (contentType?.startsWith('text/')) {
const text = await response.text();
contentValue = text.length > 100000
? text.substring(0, 100000) + '\n\n... Content truncated. Please download the file to view the complete content.'
: text;
} else if (filename.toLowerCase().endsWith('.mp4')) {
contentValue = 'video';
} else {
throw new Error(`Unsupported file type (${contentType || 'unknown'})`);
}
// Cache the content
contentCache.set(cacheKey, {
content: contentValue,
fileType: contentType || 'unknown',
timestamp: Date.now()
});
setContent(contentValue);
} catch (err) {
console.error('Error loading file:', err);
setError(err instanceof Error ? err.message : 'Failed to load file');
} finally {
setLoading(false);
}
}, [url, filename]);
useEffect(() => {
if (isVisible) {
loadContent();
}
}, [isVisible, loadContent]);
useEffect(() => { useEffect(() => {
console.log('FilePreview component mounted'); console.log('FilePreview component mounted');
@ -24,7 +122,6 @@ const FilePreview: React.FC<FilePreviewProps> = ({ url: initialUrl = '', filenam
setUrl(newUrl); setUrl(newUrl);
setFilename(newFilename); setFilename(newFilename);
// Reset state when url is empty (modal closing)
if (!newUrl) { if (!newUrl) {
setContent(null); setContent(null);
setError(null); setError(null);
@ -33,80 +130,16 @@ const FilePreview: React.FC<FilePreviewProps> = ({ url: initialUrl = '', filenam
} }
}; };
// Add event listener only for modal mode
window.addEventListener('filePreviewStateChange', handleStateChange as EventListener); window.addEventListener('filePreviewStateChange', handleStateChange as EventListener);
// Cleanup
return () => { return () => {
window.removeEventListener('filePreviewStateChange', handleStateChange as EventListener); window.removeEventListener('filePreviewStateChange', handleStateChange as EventListener);
}; };
} else { } else {
// For integrated preview, use props directly
setUrl(initialUrl); setUrl(initialUrl);
setFilename(initialFilename); setFilename(initialFilename);
} }
}, [isModal, initialUrl, initialFilename]); }, [isModal, initialUrl, initialFilename]);
useEffect(() => {
console.log('FilePreview state updated:', { url, filename });
if (!url || !filename) {
console.log('No URL or filename, resetting state');
setContent(null);
setError(null);
setFileType(null);
return;
}
const loadContent = async () => {
console.log('Loading content for:', { url, filename });
setLoading(true);
setError(null);
try {
console.log('Fetching file...');
const response = await fetch(url);
const contentType = response.headers.get('content-type');
console.log('Received content type:', contentType);
setFileType(contentType);
if (contentType?.startsWith('image/')) {
console.log('Setting content type as image');
setContent('image');
} else if (contentType?.startsWith('video/')) {
console.log('Setting content type as video');
setContent('video');
} else if (contentType?.startsWith('application/pdf')) {
console.log('Setting content type as pdf');
setContent('pdf');
} else if (contentType?.startsWith('text/')) {
console.log('Loading text content');
const text = await response.text();
if (text.length > 100000) {
console.log('Text content truncated due to length');
setContent(text.substring(0, 100000) + '\n\n... Content truncated. Please download the file to view the complete content.');
} else {
setContent(text);
}
} else if (filename.toLowerCase().endsWith('.mp4')) {
console.log('Fallback to video for .mp4 file');
setContent('video');
} else {
console.log('Unsupported file type');
setError(`This file type (${contentType || 'unknown'}) is not supported for preview. Please download the file to view it.`);
}
} catch (err) {
console.error('Error loading file:', err);
setError('Failed to load file');
} finally {
console.log('Finished loading content');
setLoading(false);
}
};
loadContent();
}, [url, filename]);
const handleDownload = async () => { const handleDownload = async () => {
try { try {
const response = await fetch(url); const response = await fetch(url);
@ -125,21 +158,18 @@ const FilePreview: React.FC<FilePreviewProps> = ({ url: initialUrl = '', filenam
} }
}; };
console.log('Rendering FilePreview with:', { content, error, loading, fileType });
return ( return (
<div className="space-y-4"> <div className="space-y-4">
{/* Header with filename and download button */}
<div className="flex justify-between items-center bg-base-200 p-3 rounded-lg"> <div className="flex justify-between items-center bg-base-200 p-3 rounded-lg">
<div className="flex items-center gap-2 flex-1 min-w-0"> <div className="flex items-center gap-2 flex-1 min-w-0">
<span className="truncate font-medium">{filename}</span> <span className="truncate font-medium" title={filename}>{truncatedFilename}</span>
{fileType && ( {fileType && (
<span className="badge badge-sm">{fileType.split('/')[1]}</span> <span className="badge badge-sm whitespace-nowrap">{fileType.split('/')[1]}</span>
)} )}
</div> </div>
<button <button
onClick={handleDownload} onClick={handleDownload}
className="btn btn-sm btn-ghost gap-2" className="btn btn-sm btn-ghost gap-2 whitespace-nowrap"
> >
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" viewBox="0 0 20 20" fill="currentColor"> <svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
<path fillRule="evenodd" d="M3 17a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm3.293-7.707a1 1 0 011.414 0L9 10.586V3a1 1 0 112 0v7.586l1.293-1.293a1 1 0 111.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z" clipRule="evenodd" /> <path fillRule="evenodd" d="M3 17a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm3.293-7.707a1 1 0 011.414 0L9 10.586V3a1 1 0 112 0v7.586l1.293-1.293a1 1 0 111.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z" clipRule="evenodd" />
@ -148,7 +178,6 @@ const FilePreview: React.FC<FilePreviewProps> = ({ url: initialUrl = '', filenam
</button> </button>
</div> </div>
{/* Preview content */}
<div className="preview-content"> <div className="preview-content">
{loading && ( {loading && (
<div className="flex justify-center items-center p-8"> <div className="flex justify-center items-center p-8">
@ -181,7 +210,12 @@ const FilePreview: React.FC<FilePreviewProps> = ({ url: initialUrl = '', filenam
{!loading && !error && content === 'image' && ( {!loading && !error && content === 'image' && (
<div className="flex justify-center"> <div className="flex justify-center">
<img src={url} alt={filename} className="max-w-full h-auto rounded-lg" /> <img
src={url}
alt={filename}
className="max-w-full h-auto rounded-lg"
loading="lazy"
/>
</div> </div>
)} )}
@ -191,6 +225,7 @@ const FilePreview: React.FC<FilePreviewProps> = ({ url: initialUrl = '', filenam
controls controls
className="max-w-full rounded-lg" className="max-w-full rounded-lg"
style={{ maxHeight: '600px' }} style={{ maxHeight: '600px' }}
preload="metadata"
> >
<source src={url} type="video/mp4" /> <source src={url} type="video/mp4" />
Your browser does not support the video tag. Your browser does not support the video tag.
@ -204,6 +239,7 @@ const FilePreview: React.FC<FilePreviewProps> = ({ url: initialUrl = '', filenam
src={url} src={url}
className="w-full h-full rounded-lg" className="w-full h-full rounded-lg"
title={filename} title={filename}
loading="lazy"
></iframe> ></iframe>
</div> </div>
)} )}