# Design ## Context См. `proposal.md` — Why. Что определяет подход: - `RestoreManager.restore(assets)` — цикл «получить байты → записать поверх превью → пометить `LOCAL`», без источника хода; возвращает `RestoreReport`. - Вызывают его двое: `DeviceAlbumViewModel.restoreOriginals()` (явное действие и подтверждение выхода из режима C) и `SyncCheck.checkGallery` (неявная починка при открытии альбома). Оба идут в области вызывающего: у экрана это `viewModelScope`, у фонового воркера — его собственная область. - `RestoreManager.restoreTargets` и `GallerySyncPolicy` уже сходятся в одном наборе: превью (`state == PREVIEW`) с серверной копией. - Карточка альбома строит третью строку из `AlbumSyncProgress`, куда сейчас попадают только загрузки (`SyncUpload`) и счётчики индекса. ## Goals / Non-Goals **Goals:** - Показывать ход восстановления на карточке и на экране альбома. - Не обрывать восстановление при уходе с экрана. - Не менять поведение загрузки, разгрузки и режимов. **Non-Goals:** - Восстановление потерянных копий из корзины сервера. - Возобновление восстановления после смерти процесса (операция идемпотентна и повторится при следующей проверке синхронизации). - Прогресс по байтам: считаем элементы, как и в очереди загрузки. ## Decisions **Единая точка восстановления — `RestoreCoordinator` в области приложения.** Он владеет картой «альбом → ход», запускает операцию в scope контейнера и защищается от повторного запуска по одному альбому. Альтернатива «добавить колбэк прямо в менеджер и запускать из экрана» отклонена: она не решает обрыв при уходе с экрана. **Два входа: `start` и `run`.** Явное действие пользователя использует `start` (область приложения, не ждём). `SyncCheck` использует `run` (ждёт завершения), чтобы фоновый воркер, как и раньше, не отдавал управление до конца восстановления. Оба делят одну защиту и одну карту хода. **Прогресс отдаёт сам менеджер.** `restore(assets, onProgress)` вызывает колбэк после каждого элемента, включая пропуски, чтобы полоса доходила до конца. Ход считает `RestoreCoordinator`, а не менеджер: менеджер остаётся про байты и файлы. **Интерфейс ради тестируемости.** Координатор зависит от `AlbumRestorer` (реализация — `RestoreManager`), чтобы его можно было проверить без MediaStore. **Ход на карточке — новое состояние третьей строки.** `AlbumSyncProgress` получает `restorePercent`; приоритет: потерянные копии → восстановление → загрузка → ошибка → ожидание Wi-Fi → очередь → метка. Пульсация включает и восстановление: это активная работа. **Уборка хода — по завершении.** Карта чистится в `finally`, поэтому и успех, и ошибка, и нехватка места убирают строку; незавершённые превью при следующем проходе попробуют снова. ## Risks / Trade-offs - [Двойной запуск восстановления (экран + фон)] → защита по альбому: второй запуск игнорируется, пока идёт первый. - [Прогресс не переживает смерть процесса] → операция идемпотентна: оставшиеся превью восстанавливаются при следующей проверке; карточка просто теряет строку хода. - [Нехватка места обрывает восстановление раньше времени] → строка хода исчезает, состояние остаётся честным (часть превью осталась); отдельного сообщения о нехватке места не добавляем в этом объёме. - [Лишние эмиссии Room + flows при каждом элементе] → альбомы небольшие, состояние обновляется в памяти; реальной нагрузки нет. ## Migration Plan Схема Room не меняется. Откат — обычная сборка без изменения. ## Open Questions - Нужно ли отдельное сообщение при остановке восстановления из-за нехватки места.