Fix file rename bugs and hide file extensions
All checks were successful
Automated Container Build / build-and-push (push) Successful in 34s
All checks were successful
Automated Container Build / build-and-push (push) Successful in 34s
This commit is contained in:
parent
5200654d0f
commit
99e0cd5f0c
2 changed files with 51 additions and 17 deletions
|
|
@ -46,6 +46,20 @@ interface UploadProgress {
|
|||
error?: string;
|
||||
}
|
||||
|
||||
function getFileDisplayName(name: string, isDir: boolean) {
|
||||
if (isDir) return name;
|
||||
const dotIndex = name.lastIndexOf('.');
|
||||
if (dotIndex > 0) return name.substring(0, dotIndex);
|
||||
return name;
|
||||
}
|
||||
|
||||
function getFileExtension(name: string, isDir: boolean) {
|
||||
if (isDir) return '';
|
||||
const dotIndex = name.lastIndexOf('.');
|
||||
if (dotIndex > 0) return name.substring(dotIndex);
|
||||
return '';
|
||||
}
|
||||
|
||||
export default function FileExplorer({ onLogout }: FileExplorerProps) {
|
||||
const [currentPath, setCurrentPath] = useState(() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
|
|
@ -324,6 +338,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
|
|||
}
|
||||
|
||||
function handleFileClick(file: FileItem, e: React.MouseEvent) {
|
||||
if (renaming === file.path) return;
|
||||
if (e.shiftKey) {
|
||||
// Multi-select with shift
|
||||
setSelectedFiles(prev => {
|
||||
|
|
@ -406,9 +421,20 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
|
|||
}
|
||||
}
|
||||
|
||||
async function handleRename(path: string) {
|
||||
if (!newName.trim()) return;
|
||||
await api.rename(path, newName);
|
||||
async function handleRename(path: string, originalName: string, isDir: boolean) {
|
||||
if (!newName.trim()) {
|
||||
setRenaming(null);
|
||||
setNewName('');
|
||||
return;
|
||||
}
|
||||
const ext = getFileExtension(originalName, isDir);
|
||||
const finalName = newName.trim() + ext;
|
||||
if (finalName === originalName) {
|
||||
setRenaming(null);
|
||||
setNewName('');
|
||||
return;
|
||||
}
|
||||
await api.rename(path, finalName);
|
||||
setRenaming(null);
|
||||
setNewName('');
|
||||
loadFiles();
|
||||
|
|
@ -785,7 +811,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
|
|||
<div className="w-4 h-4 flex items-center justify-center flex-shrink-0 [&>svg]:w-4 [&>svg]:h-4">
|
||||
{getFileIcon(file)}
|
||||
</div>
|
||||
<span className="truncate flex-1">{file.name}</span>
|
||||
<span className="truncate flex-1" title={getFileDisplayName(file.name, file.is_dir)}>{getFileDisplayName(file.name, file.is_dir)}</span>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
|
|
@ -1141,10 +1167,11 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
|
|||
type="text"
|
||||
value={newName}
|
||||
onChange={(e) => setNewName(e.target.value)}
|
||||
onKeyDown={(e) => { if (e.key === 'Enter') handleRename(file.path); if (e.key === 'Escape') setRenaming(null); }}
|
||||
onBlur={() => handleRename(file.path)}
|
||||
onKeyDown={(e) => { if (e.key === 'Enter') handleRename(file.path, file.name, file.is_dir); if (e.key === 'Escape') setRenaming(null); }}
|
||||
onBlur={() => handleRename(file.path, file.name, file.is_dir)}
|
||||
autoFocus
|
||||
onFocus={(e) => e.target.select()}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
className="w-full text-xs text-center bg-transparent outline-none"
|
||||
style={{
|
||||
color: 'var(--color-text-primary)',
|
||||
|
|
@ -1155,9 +1182,9 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
|
|||
<span
|
||||
className="text-xs font-medium text-center w-full truncate"
|
||||
style={{ color: 'var(--color-text-primary)' }}
|
||||
title={file.name}
|
||||
title={getFileDisplayName(file.name, file.is_dir)}
|
||||
>
|
||||
{file.name}
|
||||
{getFileDisplayName(file.name, file.is_dir)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -1221,15 +1248,16 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
|
|||
type="text"
|
||||
value={newName}
|
||||
onChange={(e) => setNewName(e.target.value)}
|
||||
onKeyDown={(e) => { if (e.key === 'Enter') handleRename(file.path); if (e.key === 'Escape') setRenaming(null); }}
|
||||
onBlur={() => handleRename(file.path)}
|
||||
onKeyDown={(e) => { if (e.key === 'Enter') handleRename(file.path, file.name, file.is_dir); if (e.key === 'Escape') setRenaming(null); }}
|
||||
onBlur={() => handleRename(file.path, file.name, file.is_dir)}
|
||||
autoFocus
|
||||
onFocus={(e) => e.target.select()}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
className="flex-1 text-sm bg-transparent outline-none"
|
||||
style={{ color: 'var(--color-text-primary)', borderBottom: '1px solid var(--color-accent)' }}
|
||||
/>
|
||||
) : (
|
||||
<span className="text-sm truncate" style={{ color: 'var(--color-text-primary)' }}>{file.name}</span>
|
||||
<span className="text-sm truncate" style={{ color: 'var(--color-text-primary)' }} title={getFileDisplayName(file.name, file.is_dir)}>{getFileDisplayName(file.name, file.is_dir)}</span>
|
||||
)}
|
||||
{file.is_pinned && <Pin className="w-3 h-3 flex-shrink-0" style={{ color: 'var(--color-warning)' }} />}
|
||||
</div>
|
||||
|
|
@ -1376,7 +1404,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
|
|||
label="Rename"
|
||||
onClick={() => {
|
||||
setRenaming(contextMenu.file.path);
|
||||
setNewName(contextMenu.file.name);
|
||||
setNewName(getFileDisplayName(contextMenu.file.name, contextMenu.file.is_dir));
|
||||
setContextMenu(null);
|
||||
}}
|
||||
/>
|
||||
|
|
@ -1490,9 +1518,9 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
|
|||
<span
|
||||
className="text-xs font-medium truncate max-w-[200px]"
|
||||
style={{ color: 'var(--color-text-primary)' }}
|
||||
title={upload.name}
|
||||
title={getFileDisplayName(upload.name, false)}
|
||||
>
|
||||
{upload.name}
|
||||
{getFileDisplayName(upload.name, false)}
|
||||
</span>
|
||||
<span className="text-xs" style={{ color: 'var(--color-text-tertiary)' }}>
|
||||
{upload.status === 'complete' ? '✓' : upload.status === 'error' ? '✗' : `${Math.round(upload.progress)}%`}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,12 @@ interface FilePreviewProps {
|
|||
onClose: () => void;
|
||||
}
|
||||
|
||||
function getFileDisplayName(name: string) {
|
||||
const dotIndex = name.lastIndexOf('.');
|
||||
if (dotIndex > 0) return name.substring(0, dotIndex);
|
||||
return name;
|
||||
}
|
||||
|
||||
export default function FilePreview({ file, onClose }: FilePreviewProps) {
|
||||
const [content, setContent] = useState<string | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
|
@ -87,8 +93,8 @@ export default function FilePreview({ file, onClose }: FilePreviewProps) {
|
|||
<div className="flex items-center justify-between px-4 py-3 border-b shrink-0" style={{ borderColor: 'var(--color-border-subtle)', backgroundColor: 'var(--color-bg-secondary)' }}>
|
||||
<div className="flex items-center gap-3 min-w-0">
|
||||
{getIcon(previewType)}
|
||||
<span className="font-medium truncate" style={{ color: 'var(--color-text-primary)' }} title={file.name}>
|
||||
{file.name}
|
||||
<span className="font-medium truncate" style={{ color: 'var(--color-text-primary)' }} title={getFileDisplayName(file.name)}>
|
||||
{getFileDisplayName(file.name)}
|
||||
</span>
|
||||
<span className="text-xs px-2 py-1 rounded-md" style={{ backgroundColor: 'var(--color-bg-tertiary)', color: 'var(--color-text-tertiary)' }}>
|
||||
{formatSize(file.size)}
|
||||
|
|
@ -139,7 +145,7 @@ export default function FilePreview({ file, onClose }: FilePreviewProps) {
|
|||
{previewType === 'audio' && (
|
||||
<div className="w-full max-w-md p-6 rounded-xl text-center" style={{ backgroundColor: 'var(--color-bg-elevated)', border: '1px solid var(--color-border)' }}>
|
||||
<Music className="w-16 h-16 mx-auto mb-6" style={{ color: 'var(--color-accent)' }} />
|
||||
<h3 className="text-lg font-medium mb-4 truncate" style={{ color: 'var(--color-text-primary)' }}>{file.name}</h3>
|
||||
<h3 className="text-lg font-medium mb-4 truncate" style={{ color: 'var(--color-text-primary)' }}>{getFileDisplayName(file.name)}</h3>
|
||||
<audio src={downloadUrl} controls autoPlay className="w-full outline-none" />
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
Reference in a new issue