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 и данных.
|