Fix pinned icons, add bulk download, add media metadata
Some checks failed
Automated Container Build / build-and-push (push) Has been cancelled

This commit is contained in:
Elijah 2026-05-23 08:56:58 -07:00
parent 2b49e90b81
commit ed5cc79c25
6 changed files with 182 additions and 18 deletions

View file

@ -788,7 +788,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
if (ext === 'pdf') {
return (
<div className={`${large ? 'w-20 h-20 rounded-2xl text-2xl' : 'w-6 h-6 rounded text-[8px]'} flex items-center justify-center font-black tracking-tighter shadow-sm text-white bg-[#ea4335]`}>
<div className={`${large ? 'w-20 h-20 rounded-2xl text-2xl' : 'w-5 h-5 rounded text-[6px]'} flex items-center justify-center font-black tracking-tighter shadow-sm text-white bg-[#ea4335]`}>
PDF
</div>
);
@ -797,40 +797,40 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
const fallbackIcon = (() => {
if (isImage) {
return (
<div className={`${large ? 'w-20 h-20 rounded-2xl' : 'w-6 h-6 rounded'} flex items-center justify-center shadow-sm text-white bg-[#3b82f6]`}>
<ImageIcon className={large ? "w-10 h-10 text-white" : "w-3.5 h-3.5 text-white"} />
<div className={`${large ? 'w-20 h-20 rounded-2xl' : 'w-5 h-5 rounded'} flex items-center justify-center shadow-sm text-white bg-[#3b82f6]`}>
<ImageIcon className={large ? "w-10 h-10 text-white" : "w-3 h-3 text-white"} />
</div>
);
}
if (isVideo) {
return (
<div className={`${large ? 'w-20 h-20 rounded-2xl' : 'w-6 h-6 rounded'} flex items-center justify-center shadow-sm text-white bg-[#a855f7]`}>
<FilmIcon className={large ? "w-10 h-10 text-white" : "w-3.5 h-3.5 text-white"} />
<div className={`${large ? 'w-20 h-20 rounded-2xl' : 'w-5 h-5 rounded'} flex items-center justify-center shadow-sm text-white bg-[#a855f7]`}>
<FilmIcon className={large ? "w-10 h-10 text-white" : "w-3 h-3 text-white"} />
</div>
);
}
if (['mp3', 'wav', 'flac', 'ogg', 'm4a'].includes(ext)) {
return (
<div className={`${large ? 'w-20 h-20 rounded-2xl' : 'w-6 h-6 rounded'} flex items-center justify-center shadow-sm text-white bg-[#ec4899]`}>
<MusicIcon className={large ? "w-10 h-10 text-white" : "w-3.5 h-3.5 text-white"} />
<div className={`${large ? 'w-20 h-20 rounded-2xl' : 'w-5 h-5 rounded'} flex items-center justify-center shadow-sm text-white bg-[#ec4899]`}>
<MusicIcon className={large ? "w-10 h-10 text-white" : "w-3 h-3 text-white"} />
</div>
);
}
if (['js', 'ts', 'jsx', 'tsx', 'py', 'go', 'rs', 'java', 'c', 'cpp', 'html', 'css', 'json'].includes(ext)) {
return (
<div className={`${large ? 'w-20 h-20 rounded-2xl' : 'w-6 h-6 rounded'} flex items-center justify-center shadow-sm text-white bg-[#f59e0b]`}>
<CodeIcon className={large ? "w-10 h-10 text-white" : "w-3.5 h-3.5 text-white"} />
<div className={`${large ? 'w-20 h-20 rounded-2xl' : 'w-5 h-5 rounded'} flex items-center justify-center shadow-sm text-white bg-[#f59e0b]`}>
<CodeIcon className={large ? "w-10 h-10 text-white" : "w-3 h-3 text-white"} />
</div>
);
}
if (['zip', 'tar', 'gz', 'rar', '7z'].includes(ext)) {
return (
<div className={`${large ? 'w-20 h-20 rounded-2xl' : 'w-6 h-6 rounded'} flex items-center justify-center shadow-sm text-white bg-[#14b8a6]`}>
<ArchiveIcon className={large ? "w-10 h-10 text-white" : "w-3.5 h-3.5 text-white"} />
<div className={`${large ? 'w-20 h-20 rounded-2xl' : 'w-5 h-5 rounded'} flex items-center justify-center shadow-sm text-white bg-[#14b8a6]`}>
<ArchiveIcon className={large ? "w-10 h-10 text-white" : "w-3 h-3 text-white"} />
</div>
);
}
@ -1545,8 +1545,23 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
className="p-2 hover:bg-white/5 rounded-lg transition-colors flex items-center gap-2 text-sm"
style={{ color: 'var(--color-text-primary)' }}
onClick={() => {
// In a real app, this would trigger a bulk download zip
alert('Bulk download not implemented yet');
const form = document.createElement('form');
form.method = 'POST';
form.action = api.getBulkDownloadUrl();
form.target = '_blank';
Array.from(selectedFiles).forEach(path => {
const input = document.createElement('input');
input.type = 'hidden';
input.name = 'paths';
input.value = path;
form.appendChild(input);
});
document.body.appendChild(form);
form.submit();
document.body.removeChild(form);
setSelectedFiles(new Set());
}}
>
<Download className="w-4 h-4" />

View file

@ -2,7 +2,7 @@
import { useState, useEffect } from 'react';
import { motion } from 'framer-motion';
import { X, File, Folder, HardDrive, Calendar, RefreshCw, FileType } from 'lucide-react';
import { X, File, Folder, HardDrive, Calendar, RefreshCw, FileType, Info } from 'lucide-react';
import api from '@/lib/api';
interface FileInfoModalProps {
@ -139,6 +139,45 @@ export default function FileInfoModal({ filePath, onClose }: FileInfoModalProps)
</div>
)}
</div>
{data.info.metadata && Object.keys(data.info.metadata).length > 0 && (
<div className="space-y-3 bg-black/5 dark:bg-white/5 rounded-xl p-4">
<div className="flex items-center gap-2 mb-2 text-sm font-semibold" style={{ color: 'var(--color-text-primary)' }}>
<Info className="w-4 h-4" /> Media Information
</div>
{data.info.metadata.ImageWidth && data.info.metadata.ImageHeight && (
<div className="flex items-center justify-between text-sm">
<span style={{ color: 'var(--color-text-secondary)' }}>Resolution</span>
<span style={{ color: 'var(--color-text-primary)' }}>{data.info.metadata.ImageWidth} × {data.info.metadata.ImageHeight}</span>
</div>
)}
{data.info.metadata.VideoFrameRate && (
<div className="flex items-center justify-between text-sm">
<span style={{ color: 'var(--color-text-secondary)' }}>Framerate</span>
<span style={{ color: 'var(--color-text-primary)' }}>{data.info.metadata.VideoFrameRate} fps</span>
</div>
)}
{data.info.metadata.CompressorID && (
<div className="flex items-center justify-between text-sm">
<span style={{ color: 'var(--color-text-secondary)' }}>Codec</span>
<span style={{ color: 'var(--color-text-primary)' }}>{data.info.metadata.CompressorID}</span>
</div>
)}
{data.info.metadata.Make && data.info.metadata.Model && (
<div className="flex items-center justify-between text-sm">
<span style={{ color: 'var(--color-text-secondary)' }}>Camera</span>
<span style={{ color: 'var(--color-text-primary)' }}>{data.info.metadata.Make} {data.info.metadata.Model}</span>
</div>
)}
{data.info.metadata.DateTimeOriginal && (
<div className="flex items-center justify-between text-sm">
<span style={{ color: 'var(--color-text-secondary)' }}>Captured</span>
<span style={{ color: 'var(--color-text-primary)' }}>{data.info.metadata.DateTimeOriginal}</span>
</div>
)}
</div>
)}
</div>
) : (
<div className="text-center text-sm py-10" style={{ color: 'var(--color-text-secondary)' }}>

View file

@ -237,6 +237,11 @@ class ApiClient {
return this.accessToken ? `${url}&token=${this.accessToken}` : url;
}
getBulkDownloadUrl() {
const url = `${API_BASE}/api/files/download-bulk`;
return this.accessToken ? `${url}?token=${this.accessToken}` : url;
}
getThumbnailUrl(checksum: string) {
const url = `${API_BASE}/api/files/thumbnail/${encodeURIComponent(checksum)}`;
return this.accessToken ? `${url}?token=${this.accessToken}` : url;