feat: photo multiselect, move/copy, trash and compact chrome
- backend: soft delete + trash (photos.deleted_at, migration 000005), GET /trash, restore, permanent delete with storage-key refcount (S3 objects removed only when no other album references them); batch endpoints photo-batch/{delete,move,copy} - metadata only, copies clone rows sharing storage keys
- frontend: Ctrl/Shift multiselect; right-panel 'Действия' as icon text rows (Открыть (просмотр), Переместить, Копировать, Удалить); drag selected photos onto rail album -> TransferDialog; /trash route; trash entry as last item in the left rail; empty right panel hidden on overview
- frontend: compact top bars (app header and viewer), compact theme switcher
- openspec: sync lightroom-workspace-ui artifacts (photos/ui-workspace specs, design #20-24, tasks 9.11-9.17)
This commit is contained in:
@@ -174,6 +174,40 @@ EXIF извлекается при загрузке и хранится в `phot
|
||||
маршрута, а в альбоме — у выбора; выбор сбрасывается при смене альбома. Альтернатива — хранить
|
||||
выбор в URL (query) — избыточно для локального выделения.
|
||||
|
||||
### 20. Корзина: мягкое удаление и подсчёт ссылок
|
||||
Удаление фото — мягкое (`photos.deleted_at`, миграция `000005_photo_trash`): списки и просмотр
|
||||
исключают такие фото, `GET /trash` отдаёт их по альбомам пользователя (владелец или участник).
|
||||
Восстановление снимает `deleted_at`. Окончательное удаление удаляет запись, и только если
|
||||
после этого `storage_key` больше нигде не встречается (копии в других альбомах разделяют
|
||||
объект), удаляются объекты S3 (оригинал, thumb, preview). Альтернатива — удалять S3 сразу —
|
||||
порвала бы копии.
|
||||
|
||||
### 21. Перенос и копирование — только метаданные
|
||||
Перенос меняет `photos.album_id`; копирование создаёт новую запись с тем же `storage_key`,
|
||||
thumb/preview и метаданными (клон). Объекты S3 не дублируются, поэтому «ссылки в любых
|
||||
альбомах» — это строки photos с общим `storage_key`. Права: удаление в источнике (автор или
|
||||
owner/admin) и загрузка в цель (member+).
|
||||
|
||||
### 22. Мультивыбор и трансфер через общие сторы
|
||||
Выбор — стор `selection` (набор фото + anchor); Ctrl/Cmd — toggle, Shift — диапазон по
|
||||
индексам текущей сетки. Действия инициируют диалог: стор `transfer` хранит ids, необязательный
|
||||
целевой альбом и действие; глобальный `TransferDialog` (портал) выбирает альбом и
|
||||
перенос/копию. Перетаскивание с сетки на альбом рейла передаёт ids через `dataTransfer` типа
|
||||
`application/x-galo-photos`; дроп-цель — элементы рейла. Обработчик загрузки показывает оверлей
|
||||
только для типа `Files`, поэтому при переносе фото зона загрузки не появляется.
|
||||
|
||||
### 23. Рейл: вход в корзину и скрытие панели
|
||||
В рейле альбомов последним элементом списка добавлена «Корзина» (иконка корзины), ведущая на
|
||||
маршрут `/trash` со списком удалённых фото и действиями «Вернуть»/«Удалить навсегда». Пустая
|
||||
правая панель на обзоре скрыта: `AppShell` получает `panelVisible`, который layout выключает на
|
||||
корневом маршруте.
|
||||
|
||||
### 24. Компактный верхний хром
|
||||
Верхняя панель приложения (`AppShell`) и верхняя панель режима просмотра (`Viewer`) сведены к
|
||||
минимуму: узкие вертикальные отступы (`py-0.5`/`py-1`), иконочные органы управления без рамок,
|
||||
иконки `size-4`, компактный переключатель темы. Функциональность (меню, панель, темы, полный
|
||||
экран, навигация, скачивание, удаление) сохраняется.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [Много альбомов → много presign в списке] → presign это локальная крипто-операция без сети;
|
||||
|
||||
Reference in New Issue
Block a user