Feat: Live thumbnails for office files
All checks were successful
Automated Container Build / build-and-push (push) Successful in 1m26s
All checks were successful
Automated Container Build / build-and-push (push) Successful in 1m26s
This commit is contained in:
parent
b80abdbefb
commit
80d60d540a
10 changed files with 244 additions and 106 deletions
|
|
@ -28,6 +28,7 @@ import FileInfoModal from './FileInfoModal';
|
|||
import SharedFilesPage from './SharedFilesPage';
|
||||
import OfficeHome from './OfficeHome';
|
||||
import OnlyOfficeEditor from './OnlyOfficeEditor';
|
||||
import ThumbnailImage from './ThumbnailImage';
|
||||
|
||||
interface FileItem {
|
||||
name: string;
|
||||
|
|
@ -172,40 +173,6 @@ function Tooltip({ children, text }: { children: React.ReactNode; text: string }
|
|||
);
|
||||
}
|
||||
|
||||
function ThumbnailImage({ checksum, fallbackIcon, downloadToken }: { checksum: string; fallbackIcon: React.ReactNode; downloadToken: string }) {
|
||||
const [errorCount, setErrorCount] = useState(0);
|
||||
const [key, setKey] = useState(0);
|
||||
const [showFallback, setShowFallback] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<img
|
||||
key={key}
|
||||
src={downloadToken ? `${api.getThumbnailUrl(checksum, downloadToken)}${errorCount > 0 ? `&t=${key}` : ''}` : ''}
|
||||
alt=""
|
||||
className="w-full h-full object-cover"
|
||||
style={{ display: showFallback ? 'none' : 'block' }}
|
||||
onError={(e) => {
|
||||
if (errorCount < 5) {
|
||||
e.currentTarget.style.display = 'none';
|
||||
setTimeout(() => {
|
||||
setErrorCount(c => c + 1);
|
||||
setKey(Date.now());
|
||||
}, 2000);
|
||||
} else {
|
||||
setShowFallback(true);
|
||||
}
|
||||
}}
|
||||
onLoad={(e) => {
|
||||
e.currentTarget.style.display = 'block';
|
||||
setShowFallback(false);
|
||||
}}
|
||||
/>
|
||||
{showFallback && fallbackIcon}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default function FileExplorer({ onLogout }: FileExplorerProps) {
|
||||
const isMobile = useIsMobile();
|
||||
const [sidebarOpen, setSidebarOpen] = useState(false);
|
||||
|
|
@ -1159,7 +1126,8 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
|
|||
const ext = file.name.split('.').pop()?.toLowerCase() || '';
|
||||
const isImage = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg'].includes(ext) || file.mime_type?.startsWith('image/');
|
||||
const isVideo = ['mp4', 'mkv', 'avi', 'mov', 'webm', 'ogg'].includes(ext) || file.mime_type?.startsWith('video/');
|
||||
const isMedia = isImage || isVideo;
|
||||
const isOffice = ['docx', 'doc', 'pptx', 'ppt', 'xlsx', 'xls'].includes(ext) || file.mime_type?.includes('officedocument');
|
||||
const isMedia = isImage || isVideo || isOffice;
|
||||
|
||||
if (ext === 'pdf') {
|
||||
return (
|
||||
|
|
@ -1169,31 +1137,31 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
|
|||
);
|
||||
}
|
||||
|
||||
if (ext === 'docx' || ext === 'doc') {
|
||||
return (
|
||||
<div className={`${large ? 'w-20 h-20 rounded-2xl text-2xl' : 'w-5 h-5 rounded text-[6px] flex-shrink-0 aspect-square'} flex items-center justify-center font-black tracking-tighter shadow-sm text-white bg-[#2B579A]`}>
|
||||
<FileText className={large ? "w-10 h-10 text-white" : "w-3 h-3 text-white"} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (ext === 'pptx' || ext === 'ppt') {
|
||||
return (
|
||||
<div className={`${large ? 'w-28 h-20 rounded-2xl text-2xl' : 'w-5 h-5 rounded text-[6px] flex-shrink-0 aspect-square'} flex items-center justify-center font-black tracking-tighter shadow-sm text-white bg-[#D24726]`}>
|
||||
<LayoutTemplate className={large ? "w-10 h-10 text-white" : "w-3 h-3 text-white"} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (ext === 'xlsx' || ext === 'xls') {
|
||||
return (
|
||||
<div className={`${large ? 'w-28 h-20 rounded-2xl text-2xl' : 'w-5 h-5 rounded text-[6px] flex-shrink-0 aspect-square'} flex items-center justify-center font-black tracking-tighter shadow-sm text-white bg-[#107C41]`}>
|
||||
<FileSpreadsheet className={large ? "w-10 h-10 text-white" : "w-3 h-3 text-white"} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const fallbackIcon = (() => {
|
||||
if (ext === 'docx' || ext === 'doc') {
|
||||
return (
|
||||
<div className={`${large ? 'w-20 h-20 rounded-2xl text-2xl' : 'w-5 h-5 rounded text-[6px] flex-shrink-0 aspect-square'} flex items-center justify-center font-black tracking-tighter shadow-sm text-white bg-[#2B579A]`}>
|
||||
<FileText className={large ? "w-10 h-10 text-white" : "w-3 h-3 text-white"} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (ext === 'pptx' || ext === 'ppt') {
|
||||
return (
|
||||
<div className={`${large ? 'w-28 h-20 rounded-2xl text-2xl' : 'w-5 h-5 rounded text-[6px] flex-shrink-0 aspect-square'} flex items-center justify-center font-black tracking-tighter shadow-sm text-white bg-[#D24726]`}>
|
||||
<LayoutTemplate className={large ? "w-10 h-10 text-white" : "w-3 h-3 text-white"} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (ext === 'xlsx' || ext === 'xls') {
|
||||
return (
|
||||
<div className={`${large ? 'w-28 h-20 rounded-2xl text-2xl' : 'w-5 h-5 rounded text-[6px] flex-shrink-0 aspect-square'} flex items-center justify-center font-black tracking-tighter shadow-sm text-white bg-[#107C41]`}>
|
||||
<FileSpreadsheet className={large ? "w-10 h-10 text-white" : "w-3 h-3 text-white"} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (isImage) {
|
||||
return (
|
||||
<div className={`${large ? 'w-20 h-20 rounded-2xl' : 'w-5 h-5 rounded flex-shrink-0 aspect-square'} flex items-center justify-center shadow-sm text-white bg-[#3b82f6]`}>
|
||||
|
|
|
|||
Reference in a new issue