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.
62 lines
4.7 KiB
Markdown
62 lines
4.7 KiB
Markdown
# 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 и данных.
|