Commit Graph
141 Commits
Author SHA1 Message Date
iu 723b19bbe5 docs: описание релиза Galo за 21 октября 2026
Описание изменений с main (84ce4d1) до develop (afe85f7): локальный
режим на SQLite и файловой системе, слой объектов, редактирование фото,
новый интерфейс рабочего пространства, видео, Android-клиент, профиль,
офлайн, живое обновление, исправления и инструкция по обновлению.
0.1-alpha
2026-10-21 21:15:59 +03:00
iu afe85f7be0 docs: отчёт об изменениях — раздел по реализации fix-copy-edit-variant-sharing 2026-10-21 21:01:33 +03:00
iu 70ac12db10 chore(openspec): отметить задачи fix-copy-edit-variant-sharing выполненными
Все 9 задач закрыты; в design.md уточнено, что удалённые привязки
не осиротевают объекты: отредактированная версия живёт вместе с фото.
2026-10-21 21:01:33 +03:00
iu 8dd8cb6f0c fix(photos): копия фото не наследует отредактированную версию
Clone переносил все привязки photo_objects, включая роли edit*, хотя
правка принадлежит фото, чьи правки её породили. Теперь копия делит
только обычные варианты и показывается в исходном виде.

- EditRoles() и IsEditRole() в edit_render.go — единый список ролей;
- фильтр edit-ролей в Store.Clone и SQLiteStore.Clone;
- миграции 000016 (Postgres) и 000008 (SQLite) убирают привязки у
  копий, созданных до фикса;
- контрактная проверка копирования, сервисные и e2e-тесты.
2026-10-21 21:01:33 +03:00
iu 759e78ff79 docs: отчёт об изменениях, сделанных без спецификаций
Разбор 21 коммита диапазона 45a953f..eb2d478: что было описано,
что осталось без спек и почему, найденное расхождение по копированию фото
с правками.
2026-10-21 20:31:39 +03:00
iu b075f4c3a4 chore(openspec): предложить fix-copy-edit-variant-sharing
Копирование фото переносит и отредактированную версию, хотя правка
принадлежит фото, чьи правки её породили. Изменение описывает фильтр
edit-ролей в Clone, разовую чистку копий миграцией и дельту требования
«Совместные объекты».
2026-10-21 20:31:39 +03:00
iu 748143c9d7 docs(openspec): описать правки редактора, сделанные без спецификаций
Дополнены спеки ui-workspace (сворачивание кривой, живая гистограмма,
точки кривой, навигация в зуме, Space к центру), media-objects (роли edit*,
привязка отредактированной версии, отдельная очередь рендера правок),
photos (рендер по оригиналу) и dev-make-workflow (сборка WASM-модуля).
2026-10-21 20:21:59 +03:00
iu eb2d478275 feat(web): блок «Кривая» свёрнут по умолчанию
График тональной кривой с гистограммой занимает много места в узкой панели, поэтому
в инструменте «Свет» он изначально свёрнут и раскрывается по клику на заголовок.
2026-10-21 20:02:34 +03:00
iu 332f35b553 feat(web): сворачивание блока «Кривая» в инструменте «Свет»
Заголовок «Кривая» стал кнопкой со стрелкой: график с гистограммой и точками
сворачивается и разворачивается, кнопки выбора канала остаются видны всегда,
чтобы канал можно было переключить и в свёрнутом виде.
2026-10-21 20:00:30 +03:00
iu 48ed0bb775 fix(web): гистограмма пересчитывается при правках
Гистограмма считалась по базовому превью один раз и не менялась при движении
ползунков и кривой. Теперь по уменьшенной копии фото накапливается сэмпл
пикселей, а сама гистограмма пересчитывается с применением текущих правок, так
что график реагирует на манипуляции сразу.
2026-10-21 01:32:14 +03:00
iu b64574b921 feat(web): гистограммы каналов в редакторе кривой
График тональной кривой теперь показывает гистограмму фото: выбранный канал —
своим цветом, мастер-канал (RGB) — всеми тремя слоями. Гистограмма считается
в браузере по уменьшенной копии базового превью и не влияет на скорость
перерисовки.
2026-10-21 01:29:35 +03:00
iu d904b2fe31 fix(web): линия тональной кривой была не видна
Цвета каналов задавались CSS-переменными темы прямо в SVG-атрибутах stroke и
fill, но presentation-атрибуты не понимают var(), а переменные темы хранят
компоненты HSL, а не готовый цвет. В итоге обводка была пустой, и на тёмном
фоне графика кривую не было видно.

Теперь цвет канала идёт через style в формате hsl(var(--text)), а заливка
точек — hsl(var(--bg)).
2026-10-21 01:24:40 +03:00
iu 2668d87347 fix(web): искажение цветов в предпросмотре правок
Таблицы тональных кривых в WASM-модуле лежали в статическом массиве, который
линковщик помещал в сегмент .data — такой сегмент в wasm доступен только для
чтения, поэтому запись в него не срабатывала, и модуль считал по мусорным
данным (отсюда «каша» вместо плавного градиента).

Теперь таблицы считает и передаёт JS: модуль не хранит состояния и только
применяет правки к пикселям. Дополнительно клиент сверяет модуль с эталонной
JS-реализацией на маленьком образце и, если они расходятся, доверяет JS.

Service worker больше не кэширует .wasm: старый модуль в кэше ломал
предпросмотр даже после обновления сборки.
2026-10-21 01:19:45 +03:00
iu f8ee93777d fix(web): зум в просмотре — контейнер прокрутки и центрирование
Прокручиваемый контейнер просмотра не имел min-w-0, поэтому в зуме он
раздувался до ширины картинки вместо того, чтобы прокручиваться: фото
нельзя было двигать влево-вправо. Та же правка применена к обёрткам просмотра.

Пробел теперь зумит к центру фото: 100% от оригинала с центрированием
изображения в просмотре.
2026-10-21 00:41:08 +03:00
iu b2a509fc38 fix(web): в зуме стрелки панорамируют фото
Стрелки влево/вправо переключают фото только без зума; в зуме они двигают
изображение, как и перетаскивание мышью.
2026-10-21 00:34:15 +03:00
iu 500ec10d7f fix(web): починить зум в просмотре
Механика зума переписана:

- Пробел зумит фото до 100% от оригинала (и обратно к вписанному виду).
- Колесо мыши зумит к курсору: точка под указателем остаётся на месте при
  любом изменении масштаба.
- Стрелки влево/вправо переключают фото и в зуме — панорамирование осталось
  за мышью.
- Масштаб и позиция считаются от размеров оригинала, а не от текущей рамки,
  поэтому зум не «уезжает» и картинка корректно центрируется.
2026-10-21 00:17:15 +03:00
iu 2adcb1374b fix(web): предпросмотр правок работает и без зума
Клиентский предпросмотр был включён только в зуме, поэтому в обычном виде
просмотра фото оставалось без правок. Теперь холст рисует ту часть картинки,
что попала во вьюпорт, в любом режиме, а под ним лежит неотредактированная
версия фото — так правки не применяются дважды.
2026-10-21 00:11:17 +03:00
iu 520886a11d feat(web): правки применяются на фронте, фильтры в WASM
Новый алгоритм редактирования: обработка идёт только на клиенте.

- Фильтры «Свет» считает WASM-модуль на Rust (frontend/wasm/light, 1.5 КБ):
  он строит тональные кривые и обрабатывает пиксели. При недоступности модуля
  используется JS-реализация с тем же результатом — расхождение проверяется
  тестом.
- Просмотр рисует видимую область фото с правками на canvas поверх
  исходной версии, поэтому изменения видны сразу, без ожидания рендера.
- Правки уходят на сервер не сразу: через десять секунд без изменений или при
  переключении фото. Сервер сохраняет параметры и готовит варианты сетки и
  полноразмерную версию в фоне, о готовности сообщает через SSE — клиент
  подтягивает готовую версию.
2026-10-21 00:06:46 +03:00
iu 4d2069d079 feat(photos): рендер только видимой области при зуме правок
Вместо полноразмерной отредактированной версии просмотр запрашивает у сервера
только видимую область кадра: координаты относительные, поэтому запрос не
зависит от размера оригинала. Рендер куска занимает около 0.15 с против 0.8 с
для всей картинки, а полноразмерная версия больше не готовится вовсе.

Зум больше не сбрасывается, когда приходит отрендеренная версия правок:
состояние просмотра сбрасывается только при смене самого фото.
2026-10-20 23:34:45 +03:00
iu 6d3d7cbdd4 perf(photos): ускорить рендер правок в 9 раз
Расчёт правок считал math.Pow для каждого канала каждого пикселя, из-за чего
большое фото обрабатывалось больше секунды. Факторы правок теперь считаются
один раз на рендер, квадраты считаются умножением, а строки обрабатываются
параллельно.

Обработка кадра 4000×3000 сократилась с 1.23 с до 0.14 с. Задержка от
сохранения правок до появления отредактированной версии в сетке и просмотре:
около 0.15 с для небольшого фото и 0.6 с для большого (полноразмерная
версия догружается следом, через 1.4 с).
2026-10-20 23:19:03 +03:00
iu cd3d17c0f0 fix(web): правки применяются сразу и не сбрасывают вкладку
Вкладка «Редактирование» больше не переключается на «Сведения» при
обновлении того же фото: временное состояние панели сбрасывается только при
смене фото.

Правки теперь рендерятся без ожидания тика воркера: сервис будит воркер сразу
после постановки задачи, а уведомление о готовых вариантах приходит до
кодирования полноразмерной версии. Задержка между сохранением правок и
появлением отредактированной версии — около 0.25 с вместо нескольких секунд.
2026-10-20 23:11:57 +03:00
iu 0548b1ab5e feat(web): вкладка «Редактирование» с инструментом «Свет»
В правой панели фото появилась вкладка «Редактирование» с инструментом «Свет»:
ползунки экспозиции, контраста, светов, теней, белых и чёрных и точечная кривая
с выбором канала. Правки сохраняются автоматически, переключатель «Сброс»
удаляет параметры правок и отредактированную версию фото.

Редактирование неразрушающее: параметры хранятся в метаданных фото, а
отредактированная версия рендерится в фоне отдельным объектом (edit, edit_thumb,
edit_medium, edit_preview) рядом с исходными вариантами. Оригинал не меняется,
скачивание отдаёт исходный файл. Сетка, просмотр и обложки альбомов показывают
отредактированную версию, когда она отрендерена.
2026-10-20 23:06:24 +03:00
iu afaaaab5f0 feat(web): офлайн-режим с кешем эскизов и превью
Добавлен service worker: оболочка приложения, ответы API и варианты
изображений (эскиз, превью, средний) сохраняются в офлайн-кеше браузера.
Ключ кеша картинки — путь объекта без подписи, поэтому обновление подписи
не плодит записи; оригиналы и видео не кешируются. Кеш ограничен по размеру
и вытесняет старые записи, при выходе из аккаунта очищается, а в верхней
панели появляется индикатор «Офлайн».

Добавлен манифест PWA; манифест отдаётся с типом application/manifest+json.
2026-10-20 22:27:24 +03:00
iu 961dff15da feat(web): хоткей Ctrl+F для полноэкранного режима приложения
Кнопке «На весь экран» в верхней панели добавлен хоткей Ctrl+F. Хоткей `f`
полноэкранного просмотра больше не срабатывает на Ctrl+F: модификаторы
проверяются явно, поэтому сочетание свободно для кнопки каркаса.
2026-10-20 22:12:24 +03:00
iu 0a9e5274cf fix(web): лента миниатюр следует за текущим фото в просмотре
При переходе стрелками влево/вправо активная миниатюра нижней ленты уходила
за край и оставалась невидимой. Теперь лента прокручивается к текущему фото,
удерживая его плитку в поле зрения.
2026-10-20 22:06:20 +03:00
iu 35f84399bc feat(web): зум клавишами + и - в просмотре фото
В режиме просмотра фотографии (обычный и полноэкранный) клавиши + и - меняют
масштаб изображения на шаг, сохраняя точку в центре просмотра. Пока открыт
просмотр, эти клавиши больше не меняют размер миниатюр в сетке.
2026-10-20 22:01:41 +03:00
iu 50b9983be4 feat(web): сохранять размер миниатюр при скрытых боковых панелях
В скрытом режиме панели больше не растягивают плитки: сетка пересчитывает
число колонок так, чтобы размер миниатюры остался прежним, а в строке
помещалось больше фото. Каркас публикует ширину, которую занимают панели в
текущей раскладке (рейл на широких экранах, панель на сверхшироких), сетка
использует её для пересчёта, а навигация курсора стрелками учитывает
реальное число колонок.
2026-10-20 21:38:02 +03:00
iu 45a953f8bb feat(web): скрытие боковых панелей в альбоме и хоткеи размера миниатюр
В режиме просмотра альбома добавлена кнопка скрытия левой и правой боковых
панелей (рядом с регулятором размера миниатюр) и хоткей Tab. В скрытом режиме
панели не занимают место в раскладке, а появляются по наведению на левую или
правую кромку окна и прячутся после ухода указателя.

Клавиши + и - меняют размер миниатюр на шаг, как кнопки регулятора; хоткей
работает на всех экранах приложения и не срабатывает при вводе текста, в
открытых диалогах и меню.

Спека ui-workspace дополнена требованиями о скрытии панелей и клавишах
изменения размера миниатюр.
2026-10-20 21:30:06 +03:00
iu 3a44844ff9 chore(openspec): архивировать fix-album-sync-churn-and-degradation и синхронизировать спеки
В спеку phone-album-sync добавлены требования о роли локального файла,
подтверждении новой версии до удаления устаревшей и разгрузке без
незавершённой загрузки; уточнены синхронизация изменений, провенанс и
возврат оригиналов.
2026-10-20 20:35:52 +03:00
iu 2e3728d83d fix(android): не принимать разгруженное превью за новую версию элемента
- роль локального файла определяется меткой провенанса, а не состоянием
  индекса: собственное превью выправляет запись (MarkPreview) вместо
  повторной загрузки, постер видео связывается с самим видео;
- изменённое содержимое заменяет серверную копию только после сверки
  original_checksum (ReplaceUpload): прежняя копия удаляется мягко, а при
  расхождении принятая загрузка отбрасывается и элемент не разгружается;
- файл не разгружается, пока по нему есть незавершённая загрузка;
- восстановленный оригинал помечается UPLOADED, чтобы следующий проход не
  разгрузил его снова;
- миграция 8→9 добавляет upload_jobs.replacesPhotoId.
2026-10-20 20:32:20 +03:00
iu 97f8f4e768 fix(backend): отдавать контрольную сумму оригинала в ответе на загрузку
Ответ на загрузку и публикуемое событие теперь несут хеш сохранённого
объекта, чтобы клиент синхронизации мог сверить принятые байты со своим
файлом и только после этого удалять прежнюю версию элемента.
2026-10-20 20:32:20 +03:00
iu 46d54b4b12 chore(openspec): предложить fix-album-sync-churn-and-degradation и уточнить спеку phone-album-sync
Изменение описывает три дефекта двусторонней синхронизации: повторную
загрузку собственного превью, удаление серверной копии до подтверждения
замены и повторную разгрузку только что восстановленного оригинала.
2026-10-20 20:32:20 +03:00
iu cc85d792ea chore(openspec): архивировать phone-album-two-way-sync и синхронизировать спеки
Дельта-спеки перенесены в основные: phone-album-sync (+8 требований, 6 изменено,
1 удалено — «Удаление только собственных загрузок»), android-client.

Change перемещён в openspec/changes/archive/2026-10-16-phone-album-two-way-sync.
2026-10-16 23:20:04 +03:00
iu d17f62f676 chore(openspec): предложить phone-album-two-way-sync и обновить спеку phone-album-sync
Новое изменение: двусторонний миррор альбома устройства и серверного альбома
(материализация серверных фото, признак происхождения, двусторонние удаления с
явным намерением, устойчивое выключение, первый проход, n:1). Purpose основной
спеки phone-album-sync обновлён под двустороннюю синхронизацию.

Change проходит `openspec validate --strict`.
2026-10-16 23:19:08 +03:00
iu cd326c0ae6 feat(android): двусторонняя синхронизация альбома устройства с сервером
Связанная пара «альбом устройства → серверный альбом» становится миррором:
серверные фото материализуются на устройстве (оригинал в режиме копий, превью/
постер в переносе), состав меняется в обе стороны.

- Признак происхождения (OWN/MIRROR) в индексе и метке провенанса; БД v8.
- Материализация серверных фото (MirrorManager) с датой съёмки и меткой.
- Дедуп, материализация ровно в один бакет при n:1, baseline «только новое»/«всё».
- Двустороннее удаление с учётом прав; для зеркал намерением считается только
  явное удаление в приложении (надгробие), пропавшая локальная копия
  материализуется заново — иначе терялись серверные оригиналы.
- Неудачное чтение серверного списка не считается пустым альбомом.
- Устойчивое выключение синхронизации (syncDisabledAt): восстановление
  происхождения не возвращает связь.
- Очередь загрузки — одно дренирующее задание вместо задания на файл
  (JobScheduler переставал запускать остаток на больших альбомах).
- Дебаунс наблюдателя MediaStore и кэш sha256 по (size, dateModified).

Покрыто unit-тестами и instrumented-тестами (режимы A/B/C и переключения,
двусторонний миррор, объём 100 фото + 20 видео на тест).
2026-10-16 23:19:02 +03:00
iu 19309e2ac0 chore(openspec): архивировать android-album-restore-progress и синхронизировать спеки
Дельта перенесена в основные спеки: android-client («Прогресс восстановления
оригиналов») и phone-album-sync («Ход и устойчивость восстановления оригиналов»).
Изменение перемещено в openspec/changes/archive/2026-10-16-android-album-restore-progress.
2026-10-16 20:15:57 +03:00
iu fcc7ac05c1 feat(android): показывать ход восстановления оригиналов
Возврат из переноса в копии скачивает оригиналы с сервера, но ход нигде не
отражался: карточка умела показывать только загрузку, а восстановление шло в
области экрана и обрывалось при уходе с него.

Добавлен RestoreCoordinator: выполняет восстановление в области приложения
(уход с экрана больше не прерывает), ведёт ход «альбом -> восстановлено из
всего» и защищает от одновременного запуска по одному альбому. RestoreManager
сообщает ход по каждому элементу и вынесен за интерфейс AlbumRestorer; SyncCheck
ходит через координатора и, как и раньше, дожидается завершения.

Карточка альбома показывает «Копии — восстановление N%» (пульсирует), экран
альбома устройства — строку «Восстановление оригиналов: X из Y» с полосой.

Изменение OpenSpec: android-album-restore-progress.
2026-10-16 20:15:05 +03:00
iu 1eb1177273 feat(android): статус синхронизации на карточке и безопасная разгрузка
Карточка альбома устройства получает третью строку: метку режима («Копии»/
«Перенос») и ход загрузки. Процент считается как (uploaded + running) / total
для обоих синхронизируемых режимов, пульсирует только при активной загрузке,
показывает очередь, ожидание Wi-Fi и ошибку. В режиме переноса карточка
показывает и карту памяти, и облако — режимы различает метка, а не значки.

Разгрузка оригиналов теперь требует подтверждённой серверной копии в текущем
целевом альбоме: устаревший serverPhotoId перепроверяется по альбому, смена
цели переносит уже загруженные фото и отклоняется, если перенести нельзя.
Потеря серверной копии видна на карточке и на экране альбома, автозатирания
превью нет.

Изменения OpenSpec: android-album-card-sync-status, safe-album-offload.
2026-10-16 18:36:51 +03:00
iu 98aaa3156a chore(openspec): архивировать add-orphaned-preview-handling и синхронизировать спеки
Новое требование в phone-album-sync о потере/возврате серверной копии у
разгруженного превью, требование в android-client о метке потерянного элемента
и действии удаления с устройства.
2026-10-16 14:13:01 +03:00
iu 5cfa045171 test(android): покрыть потерю и возврат серверной копии
Unit-тесты планировщика, координатора, политики и восстановления, Compose-
тесты маркера и шита, instrumented-проверка удаления превью с устройства и
E2E-сценарии потери/возврата серверной копии. E2E не прогонялись: нужен
эмулятор и сервер на 10.0.2.2:8088.
2026-10-16 14:12:58 +03:00
iu 3ff8f80eed feat(android): обработка потерянных серверных копий разгруженных превью
В режиме переноса, если серверное фото исчезает из целевого альбома,
разгруженное превью помечается состоянием ORPHANED: в сетке показывается
перечёркнутое облако, элемент можно удалить с устройства, а вернувшийся
оригинал заливается заново. Детекция — в reconcile по живым фото альбома,
 возврат серверного фото снимает пометку.
2026-10-16 14:12:55 +03:00
iu 863038b5a0 chore: добавить AGENTS.md с инструкциями для агентов
Правила работы в репозитории: команды, архитектура, миграции, конфигурация,
фронтенд, OpenSpec и конвенции коммитов. Отдельный раздел про прогоны с
эмулятором: instrumented-тесты и запуск AVD могут зависнуть, прогресс нужно
проверять примерно раз в минуту, а не ждать молча.
2026-10-16 13:28:03 +03:00
iu e4fc6f0ce9 chore(openspec): архивировать android-album-sort и синхронизировать основные спеки
android-client: +1 требование — выбор порядка карточек и направления, порядок
охватывает весь список вместе с альбомами устройства.
2026-10-16 13:27:30 +03:00
iu 8010ff26f9 test(android): instrumented-проверка сохранения сортировки
AlbumSortE2ETest на эмуляторе: два альбома, выбор «по названию», включение
обратного порядка, перезапуск экрана — порядок и отметки сохраняются.
2026-10-16 13:27:23 +03:00
iu 3e8a6b87b7 feat(android): сортировать весь список и добавить обратный порядок
Выбор порядка раньше действовал только на серверные альбомы: карточки устройства
были закреплены сверху и не сортировались, а направления не было вовсе.

- порядок применяется ко всему списку: карточки устройства и серверные
  упорядочиваются вместе единым компаратором
- альбом устройства получает дату последнего изменения (самый свежий элемент,
  как при выборе обложки), поэтому «по времени изменения» осмыслен и для него
- обратный порядок инвертирует выбранный ключ; карточка без даты остаётся
  последней в любом направлении
- пара (ключ, направление) хранится в DataStore за узким портом
  AlbumSortPreference; меню сортировки получило переключатель «Обратный порядок»
- README: раздел Album sorting обновлён
2026-10-16 13:27:20 +03:00
iu 28e3d4676f fix(make): пересобирать изменившийся фронтенд в make run
Цель run зависела только от backend, а статика фронтенда попадает в бинарник
через go:embed из backend/static, который обновляла лишь цель frontend. Поэтому
make run пересобирал Go-код со старым UI.

- run теперь зависит от frontend backend;
- frontend стала инкрементальной: npm ci только при изменении package.json/
  package-lock.json, npm run build и копирование в backend/static — только когда
  исходники или конфиги новее, чем backend/static/index.html (он же stamp);
- проверки вынесены в shell-recipe, т.к. имена SvelteKit-роутов вида (app)
  ломают разбор имён файлов в make.
2026-10-16 13:26:43 +03:00
iu 14db79632a chore(openspec): архивировать android-live-albums и fix-album-cover-readiness
Дельта android-live-albums: android-client (+3 требования: живое обновление
списка, догон при переподключении, потеря доступа) и realtime (порядок списка
теперь правило отображения клиента).

Дельта fix-album-cover-readiness: realtime (+1 требование: album.updated при
готовности вариантов фото-обложки).
2026-10-16 12:48:32 +03:00
iu d0d6362a01 fix(android): прогонять unit-тесты только на debug-варианте
Тестовая активность Compose (androidx.activity.ComponentActivity) объявляется
зависимостью ui-test-manifest, подключённой как debugImplementation, поэтому в
release-манифесте её нет и createComposeRule падает на testReleaseUnitTest.
Источники тестов вариант-агностичны, поэтому прогон один раз на debug даёт то
же покрытие и не тащит тестовую активность в production-манифест.
2026-10-16 12:48:08 +03:00
iu 3178699213 test(android): сквозной сценарий живого обновления на эмуляторе
LiveAlbumsE2ETest против локального сервера и двух аккаунтов: участник
регистрируется на устройстве, владелец создаёт альбом и приглашает его,
альбом появляется в списке без обновления, переименование меняет открытый
экран, исключение убирает альбом и возвращает экран к списку.

- E2E: хелперы invite/accept/rename/remove и получение id пользователя
2026-10-16 12:48:02 +03:00
iu 9061f7fc44 feat(android): обновлять список альбомов в реальном времени
Приложение подписывается на персональный поток GET /api/sse/me и применяет
album.created/updated/deleted к кэшу Room, поэтому список и открытый экран
следуют изменениям с другого устройства или из веба без ручного обновления.

- PersonalSseClient и парсер событий жизненного цикла; подписка живёт вместе с
  сессией (AlbumsViewModel), закрывается на выходе
- AlbumRepository.apply: upsert альбома или удаление его вместе с фото;
  при переподключении список догоняется серверным снимком
- потеря доступа (удаление альбома или исключение участника) убирает альбом из
  списка, а открытый экран возвращается к списку
- README: раздел Live updates и ограничение с истечением токена
2026-10-16 12:47:53 +03:00