Карточка альбома устройства получает третью строку: метку режима («Копии»/ «Перенос») и ход загрузки. Процент считается как (uploaded + running) / total для обоих синхронизируемых режимов, пульсирует только при активной загрузке, показывает очередь, ожидание Wi-Fi и ошибку. В режиме переноса карточка показывает и карту памяти, и облако — режимы различает метка, а не значки. Разгрузка оригиналов теперь требует подтверждённой серверной копии в текущем целевом альбоме: устаревший serverPhotoId перепроверяется по альбому, смена цели переносит уже загруженные фото и отклоняется, если перенести нельзя. Потеря серверной копии видна на карточке и на экране альбома, автозатирания превью нет. Изменения OpenSpec: android-album-card-sync-status, safe-album-offload.
62 lines
5.6 KiB
Markdown
62 lines
5.6 KiB
Markdown
# Proposal
|
|
|
|
## Why
|
|
|
|
Реальный инцидент: альбом устройства U2 синхронизировался в режиме переноса в серверный
|
|
альбом A (фото загружены, локальные оригиналы заменены превью), затем альбом A удалили на
|
|
сервере, а альбом устройства перепривязали к новому серверному альбому U2. В U2 уехало всего
|
|
1 фото: остальные на телефоне уже были превью, и загрузить их заново было нечем. Оригиналы
|
|
остались только в корзине сервера, а приложение молча оставило телефон с превью.
|
|
|
|
Три причины:
|
|
- решение о разгрузке принимается по `serverPhotoId` в локальном индексе, без проверки, что
|
|
это фото вообще есть в **текущем** целевом альбоме;
|
|
- смена целевого альбома только перезаписывает связь: уже загруженные фото никто не переносит
|
|
на сервере, а из превью повторно не загрузить;
|
|
- потеря серверной копии — тихий тупик: `ORPHANED` виден, но нигде не сказано, что оригинал
|
|
утрачен, и нет явного предупреждения.
|
|
|
|
## What Changes
|
|
|
|
- **Доверие только к живой копии.** Записанный `serverPhotoId` считается действительным,
|
|
только если это фото присутствует в целевом альбоме. Если копии нет: при локальном
|
|
оригинале ассет загружается заново, при одном превью элемент помечается потерявшим копию.
|
|
- **Разгрузка только под защитой.** Замена оригинала превью выполняется, только если копия
|
|
подтверждена в целевом альбоме; разгрузка «по памяти индекса» запрещена. Автоматическая
|
|
разгрузка в режиме переноса сохраняется.
|
|
- **Смена цели переносит копии.** При смене целевого альбома приложение сначала переносит на
|
|
сервере уже загруженные этим альбомом устройства фото в новый альбом и переключает связь
|
|
только если перенос удался; иначе связь не меняется и пользователь получает отказ.
|
|
- **Потеря копии видна.** Экран альбома устройства и карточка альбома в списке показывают,
|
|
сколько фото осталось без серверной копии; никакого автоматического удаления превью.
|
|
- **BREAKING**: нет. Меняются только защитные условия; успешные сценарии (загрузка,
|
|
разгрузка, повторная синхронизация) сохраняют поведение.
|
|
|
|
## Capabilities
|
|
|
|
### New Capabilities
|
|
|
|
_Нет — поведение относится к существующим способностям._
|
|
|
|
### Modified Capabilities
|
|
|
|
- `phone-album-sync`: разгрузка требует живой копии в текущем целевом альбоме; записанный
|
|
`serverPhotoId` перепроверяется по целевому альбому; смена целевого альбома переносит уже
|
|
загруженные фото и отклоняется, если перенести нельзя.
|
|
- `android-client`: потеря серверной копии показывается на экране альбома устройства и на
|
|
карточке в списке; автоматического удаления превью нет.
|
|
|
|
## Impact
|
|
|
|
- **Код (Android):** `data/sync/SyncPlan` (доверие к живой копии), `data/sync/SyncCoordinator`
|
|
(защищённая разгрузка, перенос при смене цели), `data/sync/OffloadManager` (условие разгрузки),
|
|
`data/upload/UploadWorker` (передача живой копии после загрузки), `data/db/dao/SyncDao` и
|
|
`data/repo/SyncRepository` (счёт потерянных копий), `ui/devicealbum/DeviceAlbumViewModel`
|
|
(смена цели, разгрузка, предупреждение), `ui/devicealbum/DeviceAlbumScreen` (баннер),
|
|
`domain/sync/AlbumSyncProgress` и `ui/albums/*` (сигнал на карточке).
|
|
- **Схема Room:** без изменений; добавляются только запросы.
|
|
- **API и бэкенд:** не затрагиваются (используется существующий `/api/photo-batch/move`).
|
|
- **Веб-клиент:** не затрагивается.
|
|
- **Не затрагивается:** ручные загрузки в серверный альбом; восстановление уже потерянных
|
|
копий из корзины сервера (по решению пользователя это отдельная задача).
|