Reload the rail trash count after every operation that changes the trash contents, not just navigation and empty-trash: moving photos to trash (grid, panel, viewer), restoring, permanent delete and deleting an album. Previously the counter stayed stale until the next route change.
4.7 KiB
Design
Context
См. proposal.md — Why. Существенное:
TrashStore(frontend/src/lib/stores/trash.svelte.ts) держитcountи умеетload()(запросGET /api/trash/count) иreset().AlbumRail.svelteпоказываетtrash.countи вызываетtrash.load()только в$effectпри смене$page.url.pathnameи при аутентификации.trash/+page.svelteвызываетtrash.load()только вemptyAll;restore,confirmRestore,remove(окончательное удаление) обновляют лишь свой список.- Удаление фото разбросано:
deleteBatchв сетке и панели, прямойapi(...DELETE)в просмотре; удаление альбома — вAlbumActionsDialogsиsettings/+page.svelte. - Серверный
TrashCountсчитает мягко удалённые фото, доступные пользователю, и корректен.
Goals / Non-Goals
Goals:
- Счётчик корзины в рейле отражает актуальное состояние после любой операции, меняющей содержимое корзины, без перезагрузки.
- Единообразный, предсказуемый способ обновления во всех точках.
Non-Goals:
- Синхронизация счётчика между сессиями/устройствами (SSE-событий корзины нет).
- Изменения backend и Android.
- Оптимистичные локальные инкременты без сверки с сервером.
Decisions
1. Обновлять счётчик из сервера (trash.load()), а не инкрементировать локально
После каждой успешной мутации вызываем trash.load(). Это даёт точное значение для всех
случаев, включая пакетное удаление и удаление альбома (где число фото заранее неизвестно
точно), и повторяет уже принятый в emptyAll паттерн. Альтернатива с оптимистичным
+n/-1 требует знать точное число и расходится при частичных ошибках; альтернатива с
общим сигналом (как photo-updates) добавляет лишний слой ради одного значения.
2. Вызывать обновление в каждой точке мутации, после успешного await
Обновление идёт после успешного ответа (в try, не в finally), чтобы при ошибке
счётчик не менялся. Точки: пакетное удаление (сетка), удаление из панели/просмотра,
удаление в просмотре, восстановление и окончательное удаление в корзине, удаление
альбома (диалог и настройки). Это устраняет исходный дефект и родственные рассинхроны
той же природы.
3. Оставить асинхронность неблокирующей
trash.load() уже глотает ошибки и не мешает основному действию; вызов не await-им
блокирующе там, где это меняет UX (в остальных местах допустимо await для
детерминизма тестов). Достаточно, чтобы счётчик обновился к моменту отрисовки.
Risks / Trade-offs
- [Лишний
GET /api/trash/countна каждую мутацию] → незначительная нагрузка, сопоставимо с текущим поведением при навигации и очистке корзины. - [Гонка при параллельных мутациях] → последний ответ сервера побеждает; значение остаётся корректным, т.к. берётся из источника истины.
- [Разброс точек вызова — риск пропустить новую в будущем] → спека фиксирует инвариант; при добавлении новых операций с корзиной требование распространяется на них.
Migration Plan
Не требуется: изменения только во фронтенде, без изменения API и данных.