feat: photo rename and viewer delete with confirmation

Backend:
- add PATCH /api/photos/:id to rename original_filename (uploader/owner/admin)
- emit photo.updated SSE event

Frontend:
- inline filename editor in the right panel
- viewer: back button (arrow_back) on the left, delete on the right
- delete confirmed via a modal dialog; filmstrip updates after deletion
This commit is contained in:
iu
2026-10-10 18:50:16 +03:00
parent 1f0e909ffd
commit aa396bdb85
9 changed files with 296 additions and 13 deletions
+31 -5
View File
@@ -7,12 +7,23 @@
photos: Photo[];
index: number;
canEdit?: boolean;
canDelete?: boolean;
onClose: () => void;
onIndexChange: (index: number) => void;
onMakeCover?: (photo: Photo) => void;
onDelete?: (photo: Photo) => void;
}
let { photos, index, canEdit = false, onClose, onIndexChange, onMakeCover }: Props = $props();
let {
photos,
index,
canEdit = false,
canDelete = false,
onClose,
onIndexChange,
onMakeCover,
onDelete
}: Props = $props();
let fresh = $state<Photo | null>(null);
let touchX = 0;
@@ -52,13 +63,20 @@
const btn =
'border-border text-text hover:bg-bg-muted inline-flex items-center gap-1.5 rounded-lg border px-3 py-1.5 text-sm';
const dangerBtn =
'border-border text-danger hover:bg-bg-muted inline-flex items-center gap-1.5 rounded-lg border px-3 py-1.5 text-sm';
</script>
<svelte:window onkeydown={onKey} />
<div class="flex h-full flex-col">
<div class="border-border flex shrink-0 items-center justify-between gap-2 border-b px-3 py-2">
<span class="text-text-muted text-sm">{index + 1} / {photos.length}</span>
<div class="flex items-center gap-2">
<button type="button" class={btn} aria-label="Назад" title="Назад" onclick={onClose}>
<Icon name="arrow_back" />
</button>
<span class="text-text-muted text-sm">{index + 1} / {photos.length}</span>
</div>
<div class="flex items-center gap-2">
{#if canEdit}
<button
@@ -77,9 +95,17 @@
<span class="hidden sm:inline">Скачать</span>
</a>
{/if}
<button type="button" class={btn} aria-label="Закрыть" title="Закрыть" onclick={onClose}>
<Icon name="close" />
</button>
{#if canDelete}
<button
type="button"
class={dangerBtn}
aria-label="Удалить фото"
title="Удалить фото"
onclick={() => current && onDelete?.(current)}
>
<Icon name="delete" class="size-4" />
</button>
{/if}
</div>
</div>