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.
This commit is contained in:
@@ -110,7 +110,8 @@ Android-клиент для self-hosted galo: настройка подключ
|
|||||||
|
|
||||||
Приложение SHALL открывать альбом устройства с сеткой медиа и SHALL предоставлять
|
Приложение SHALL открывать альбом устройства с сеткой медиа и SHALL предоставлять
|
||||||
настройки синхронизации этого альбома по свайпу вправо. Настройки SHALL содержать
|
настройки синхронизации этого альбома по свайпу вправо. Настройки SHALL содержать
|
||||||
только выбор режима синхронизации.
|
только выбор режима синхронизации. Сетка SHALL включать как локальные элементы, так и
|
||||||
|
скачанные с сервера при двусторонней синхронизации.
|
||||||
|
|
||||||
#### Scenario: Открытие альбома устройства
|
#### Scenario: Открытие альбома устройства
|
||||||
|
|
||||||
@@ -132,6 +133,11 @@ Android-клиент для self-hosted galo: настройка подключ
|
|||||||
- **WHEN** серверной копии разгруженного элемента больше нет в целевом альбоме
|
- **WHEN** серверной копии разгруженного элемента больше нет в целевом альбоме
|
||||||
- **THEN** в сетке на элементе показывается перечёркнутый значок облака
|
- **THEN** в сетке на элементе показывается перечёркнутый значок облака
|
||||||
|
|
||||||
|
#### Scenario: Скачанный элемент в сетке
|
||||||
|
|
||||||
|
- **WHEN** серверное фото материализовано на устройстве
|
||||||
|
- **THEN** элемент показывается в сетке альбома устройства наряду с локальными
|
||||||
|
|
||||||
### Requirement: Доступ к альбомам устройства
|
### Requirement: Доступ к альбомам устройства
|
||||||
|
|
||||||
Приложение SHALL запрашивать доступ к медиа устройства, нужный для перечисления
|
Приложение SHALL запрашивать доступ к медиа устройства, нужный для перечисления
|
||||||
@@ -734,3 +740,24 @@ SHALL показывать значками расположение содер
|
|||||||
|
|
||||||
- **WHEN** восстановление оригиналов завершилось
|
- **WHEN** восстановление оригиналов завершилось
|
||||||
- **THEN** ход больше не показывается, а карточка возвращается к метке режима
|
- **THEN** ход больше не показывается, а карточка возвращается к метке режима
|
||||||
|
|
||||||
|
### Requirement: Серверный состав в сетке альбома устройства
|
||||||
|
|
||||||
|
Сетка альбома устройства SHALL отражать состав целевого серверного альбома: новые
|
||||||
|
серверные фото SHALL появляться, а удалённые SHALL исчезать по мере синхронизации, без
|
||||||
|
ручного обновления.
|
||||||
|
|
||||||
|
#### Scenario: Новое фото с сервера
|
||||||
|
|
||||||
|
- **WHEN** в целевом серверном альбоме появилось новое фото, и синхронизация прошла
|
||||||
|
- **THEN** материализованный элемент появляется в сетке альбома устройства
|
||||||
|
|
||||||
|
#### Scenario: Фото удалено на сервере
|
||||||
|
|
||||||
|
- **WHEN** серверное фото удалено на сервере, и синхронизация прошла
|
||||||
|
- **THEN** соответствующий элемент исчезает из сетки альбома устройства
|
||||||
|
|
||||||
|
#### Scenario: Обновление открытого экрана
|
||||||
|
|
||||||
|
- **WHEN** экран альбома устройства открыт, и синхронизация изменила состав
|
||||||
|
- **THEN** сетка обновляется без ручного действия пользователя
|
||||||
|
|||||||
@@ -108,50 +108,42 @@ API, а изменение содержимого SHALL трактовать к
|
|||||||
- **WHEN** содержимое ассета уже присутствует на сервере (в том числе загружено ранее)
|
- **WHEN** содержимое ассета уже присутствует на сервере (в том числе загружено ранее)
|
||||||
- **THEN** приложение связывает ассет с существующим серверным фото без загрузки
|
- **THEN** приложение связывает ассет с существующим серверным фото без загрузки
|
||||||
|
|
||||||
### Requirement: Удаление только собственных загрузок
|
|
||||||
|
|
||||||
При удалении содержимого на устройстве приложение SHALL удалять на сервере только
|
|
||||||
фото, загруженные этим клиентом, и SHALL сохранять остальные фото целевого альбома,
|
|
||||||
в том числе загруженные из других источников.
|
|
||||||
|
|
||||||
#### Scenario: Собственная загрузка
|
|
||||||
|
|
||||||
- **WHEN** удалено содержимое, которое загрузил этот клиент, и его контрольная сумма отсутствует во всех локальных альбомах, льющихся в этот серверный
|
|
||||||
- **THEN** приложение удаляет соответствующее серверное фото
|
|
||||||
|
|
||||||
#### Scenario: Чужие фото
|
|
||||||
|
|
||||||
- **WHEN** в целевом серверном альбоме есть фото, загруженные из других источников
|
|
||||||
- **THEN** приложение не удаляет их
|
|
||||||
|
|
||||||
### Requirement: Отражение серверных изменений на устройстве
|
### Requirement: Отражение серверных изменений на устройстве
|
||||||
|
|
||||||
Приложение SHALL отражать изменения целевого серверного альбома — новые фото,
|
Приложение SHALL приводить локальное представление целевого серверного альбома в
|
||||||
переименования, теги, подписи и удаления — в локальном представлении, SHALL NOT
|
соответствие с его составом: новое серверное фото SHALL материализоваться на устройстве
|
||||||
изменять или удалять оригиналы на устройстве.
|
(оригинал в режиме копий, превью в режиме переноса), переименования, теги и подписи SHALL
|
||||||
|
отражаться, а серверное удаление SHALL убирать локальный элемент. Удаление снятого на
|
||||||
|
устройстве оригинала SHALL учитывать права сервера и системное согласие.
|
||||||
|
|
||||||
#### Scenario: Новое с другого устройства
|
#### Scenario: Новое с другого устройства
|
||||||
|
|
||||||
- **WHEN** другое устройство добавляет фото в целевой серверный альбом
|
- **WHEN** другое устройство добавляет фото в целевой серверный альбом
|
||||||
- **THEN** приложение показывает его в серверном представлении альбома
|
- **THEN** приложение материализует его в альбоме устройства — оригинал в режиме копий, превью в режиме переноса
|
||||||
|
|
||||||
#### Scenario: Удалено на сервере
|
#### Scenario: Удалено на сервере
|
||||||
|
|
||||||
- **WHEN** серверное фото удалено на сервере
|
- **WHEN** серверное фото удалено на сервере
|
||||||
- **THEN** приложение убирает его из локального представления, не трогая оригинал на устройстве
|
- **THEN** приложение убирает соответствующий локальный элемент, а для снятого на устройстве оригинала запрашивается системное согласие на удаление
|
||||||
|
|
||||||
### Requirement: Разгрузка оригиналов в режиме переноса
|
### Requirement: Разгрузка оригиналов в режиме переноса
|
||||||
|
|
||||||
В режиме C после успешной загрузки приложение SHALL автоматически заменять оригинал
|
В режиме C после того, как оригинал элемента оказывается на сервере (загружен этим
|
||||||
на устройстве превью, без отдельного действия на экране; видео SHALL заменяться
|
клиентом или скачан при двусторонней синхронизации), приложение SHALL автоматически
|
||||||
постером-кадром. При включении режима C приложение SHALL запрашивать широкий доступ
|
заменять оригинал на устройстве превью, без отдельного действия на экране; видео SHALL
|
||||||
к файлам, чтобы замена выполнялась без системного диалога согласия.
|
заменяться постером-кадром. При включении режима C приложение SHALL запрашивать широкий
|
||||||
|
доступ к файлам, чтобы замена выполнялась без системного диалога согласия.
|
||||||
|
|
||||||
#### Scenario: Разгрузка фото
|
#### Scenario: Разгрузка фото
|
||||||
|
|
||||||
- **WHEN** оригинал фото успешно загружен
|
- **WHEN** оригинал фото успешно загружен
|
||||||
- **THEN** файл в галерее автоматически заменяется превью с сохранением даты съёмки и места в альбоме
|
- **THEN** файл в галерее автоматически заменяется превью с сохранением даты съёмки и места в альбоме
|
||||||
|
|
||||||
|
#### Scenario: Разгрузка скачанного оригинала
|
||||||
|
|
||||||
|
- **WHEN** альбом переключён в режим переноса, а оригинал элемента был скачан с сервера
|
||||||
|
- **THEN** оригинал автоматически заменяется превью
|
||||||
|
|
||||||
#### Scenario: Разгрузка видео
|
#### Scenario: Разгрузка видео
|
||||||
|
|
||||||
- **WHEN** оригинал видео успешно загружен
|
- **WHEN** оригинал видео успешно загружен
|
||||||
@@ -164,8 +156,9 @@ API, а изменение содержимого SHALL трактовать к
|
|||||||
|
|
||||||
### Requirement: Провенанс разгруженных превью
|
### Requirement: Провенанс разгруженных превью
|
||||||
|
|
||||||
Разгруженные превью и постеры видео SHALL содержать метаданные провенанса,
|
Разгруженные превью, постеры видео и скачанные с сервера элементы SHALL содержать
|
||||||
достаточные для восстановления связи с серверным альбомом и принадлежности
|
метаданные провенанса, включая признак происхождения (загружено клиентом или скачано с
|
||||||
|
сервера), достаточные для восстановления связи с серверным альбомом и принадлежности
|
||||||
загрузок после переустановки приложения.
|
загрузок после переустановки приложения.
|
||||||
|
|
||||||
#### Scenario: Восстановление после переустановки
|
#### Scenario: Восстановление после переустановки
|
||||||
@@ -173,6 +166,11 @@ API, а изменение содержимого SHALL трактовать к
|
|||||||
- **WHEN** приложение переустановлено или его данные очищены, а альбом устройства содержит разгруженные превью
|
- **WHEN** приложение переустановлено или его данные очищены, а альбом устройства содержит разгруженные превью
|
||||||
- **THEN** приложение восстанавливает связь с серверным альбомом и принадлежность своих загрузок из метаданных превью
|
- **THEN** приложение восстанавливает связь с серверным альбомом и принадлежность своих загрузок из метаданных превью
|
||||||
|
|
||||||
|
#### Scenario: Метка скачанного элемента
|
||||||
|
|
||||||
|
- **WHEN** метка провенанса восстанавливается у элемента, скачанного с сервера
|
||||||
|
- **THEN** элемент восстанавливается как скачанный, а не как собственная загрузка клиента
|
||||||
|
|
||||||
#### Scenario: Метка утрачена
|
#### Scenario: Метка утрачена
|
||||||
|
|
||||||
- **WHEN** метаданные провенанса недоступны (например, файл перезаписан сторонним редактором)
|
- **WHEN** метаданные провенанса недоступны (например, файл перезаписан сторонним редактором)
|
||||||
@@ -182,7 +180,10 @@ API, а изменение содержимого SHALL трактовать к
|
|||||||
|
|
||||||
При выходе из режима C приложение SHALL предложить вернуть оригиналы на устройство
|
При выходе из режима C приложение SHALL предложить вернуть оригиналы на устройство
|
||||||
и SHALL предупредить, если пользователь отказывается. Отвязка альбома и выключение
|
и SHALL предупредить, если пользователь отказывается. Отвязка альбома и выключение
|
||||||
синхронизации SHALL NOT удалять фото с сервера.
|
синхронизации SHALL NOT удалять фото с сервера, а скачанные с сервера файлы SHALL
|
||||||
|
оставаться в галерее как обычные. Выключение синхронизации SHALL быть явным и
|
||||||
|
устойчивым: восстановление происхождения SHALL NOT возобновлять связь, пока
|
||||||
|
пользователь не включит синхронизацию снова.
|
||||||
|
|
||||||
#### Scenario: Возврат оригиналов
|
#### Scenario: Возврат оригиналов
|
||||||
|
|
||||||
@@ -197,29 +198,44 @@ API, а изменение содержимого SHALL трактовать к
|
|||||||
#### Scenario: Отвязка
|
#### Scenario: Отвязка
|
||||||
|
|
||||||
- **WHEN** пользователь отвязывает альбом устройства от серверного
|
- **WHEN** пользователь отвязывает альбом устройства от серверного
|
||||||
- **THEN** фото на сервере сохраняются
|
- **THEN** фото на сервере сохраняются, а скачанные файлы остаются в галерее как обычные
|
||||||
|
|
||||||
#### Scenario: Выключение синхронизации
|
#### Scenario: Выключение синхронизации
|
||||||
|
|
||||||
- **WHEN** пользователь выключает синхронизацию альбома устройства
|
- **WHEN** пользователь выключает синхронизацию альбома устройства
|
||||||
- **THEN** связь с серверным альбомом снимается, а фото на сервере сохраняются
|
- **THEN** связь с серверным альбомом снимается, а фото на сервере сохраняются
|
||||||
|
|
||||||
|
#### Scenario: Выключение не отменяется восстановлением
|
||||||
|
|
||||||
|
- **WHEN** пользователь выключил синхронизацию (или отвязал альбом), и затем выполняется проверка с восстановлением происхождения по меткам превью
|
||||||
|
- **THEN** связь не восстанавливается, а альбом остаётся локальным с выбранным режимом выключено
|
||||||
|
|
||||||
|
#### Scenario: Повторное включение
|
||||||
|
|
||||||
|
- **WHEN** пользователь снова включает синхронизацию после выключения
|
||||||
|
- **THEN** связь создаётся заново, а восстановление происхождения снова действует по обычным правилам
|
||||||
|
|
||||||
### Requirement: Автоматическая фоновая синхронизация
|
### Requirement: Автоматическая фоновая синхронизация
|
||||||
|
|
||||||
Синхронизация SHALL выполняться автоматически: при открытом приложении — по
|
Синхронизация SHALL выполняться автоматически в обе стороны: при открытом приложении —
|
||||||
событиям изменения, в фоне — периодически, с учётом ограничений сети, выбранных
|
по событиям изменения, в фоне — периодически, с учётом ограничений сети, выбранных
|
||||||
пользователем. В режиме C разгрузка оригиналов SHALL выполняться автоматически
|
пользователем. В режиме C разгрузка оригиналов SHALL выполняться автоматически после
|
||||||
после успешной загрузки.
|
того, как копия оказалась на сервере.
|
||||||
|
|
||||||
#### Scenario: Фоновая синхронизация
|
#### Scenario: Фоновая синхронизация
|
||||||
|
|
||||||
- **WHEN** приложение не открыто, а в альбоме устройства появились изменения
|
- **WHEN** приложение не открыто, а в альбоме устройства появились изменения
|
||||||
- **THEN** синхронизация выполняется в фоне при ближайшей возможности
|
- **THEN** синхронизация выполняется в фоне при ближайшей возможности
|
||||||
|
|
||||||
|
#### Scenario: Фоновая загрузка серверных фото
|
||||||
|
|
||||||
|
- **WHEN** приложение не открыто, а в целевом серверном альбоме появилось новое фото
|
||||||
|
- **THEN** приложение материализует его на устройстве при ближайшей возможности
|
||||||
|
|
||||||
#### Scenario: Ограничение сети
|
#### Scenario: Ограничение сети
|
||||||
|
|
||||||
- **WHEN** включено ограничение «только Wi-Fi», а доступна только сотовая сеть
|
- **WHEN** включено ограничение «только Wi-Fi», а доступна только сотовая сеть
|
||||||
- **THEN** синхронизация не начинается до появления Wi-Fi
|
- **THEN** синхронизация, включая скачивание серверных фото, не начинается до появления Wi-Fi
|
||||||
|
|
||||||
#### Scenario: Автоматическая разгрузка
|
#### Scenario: Автоматическая разгрузка
|
||||||
|
|
||||||
@@ -325,3 +341,139 @@ API, а изменение содержимого SHALL трактовать к
|
|||||||
|
|
||||||
- **WHEN** среди элементов альбома нет превью с серверной копией
|
- **WHEN** среди элементов альбома нет превью с серверной копией
|
||||||
- **THEN** операция не запускается и ход не показывается
|
- **THEN** операция не запускается и ход не показывается
|
||||||
|
|
||||||
|
### Requirement: Материализация серверных фото на устройстве
|
||||||
|
|
||||||
|
При появлении фото в целевом серверном альбоме приложение SHALL материализовать его на
|
||||||
|
устройстве, записав файл в галерею (MediaStore) бакета альбома устройства: в режиме копий —
|
||||||
|
оригинал, в режиме переноса — превью, для видео — постер. Файл SHALL содержать метку
|
||||||
|
происхождения и дату съёмки серверного фото.
|
||||||
|
|
||||||
|
#### Scenario: Материализация в режиме копий
|
||||||
|
|
||||||
|
- **WHEN** в целевом серверном альбоме появилось новое фото, а альбом устройства в режиме копий
|
||||||
|
- **THEN** приложение скачивает оригинал и создаёт файл в бакете альбома устройства
|
||||||
|
|
||||||
|
#### Scenario: Материализация в режиме переноса
|
||||||
|
|
||||||
|
- **WHEN** в целевом серверном альбоме появилось новое фото, а альбом устройства в режиме переноса
|
||||||
|
- **THEN** приложение скачивает превью и создаёт файл в бакете альбома устройства
|
||||||
|
|
||||||
|
#### Scenario: Материализация видео
|
||||||
|
|
||||||
|
- **WHEN** в целевом серверном альбоме появилось видео
|
||||||
|
- **THEN** приложение создаёт в бакете постер-кадр, а оригинал остаётся на сервере
|
||||||
|
|
||||||
|
#### Scenario: Дата съёмки
|
||||||
|
|
||||||
|
- **WHEN** приложение материализует серверное фото
|
||||||
|
- **THEN** файл получает дату съёмки из серверных метаданных фото
|
||||||
|
|
||||||
|
### Requirement: Признак происхождения элемента
|
||||||
|
|
||||||
|
Каждый элемент альбома устройства SHALL иметь явный признак происхождения: загружено этим
|
||||||
|
клиентом или скачано с сервера. Политика удалений, разгрузка и восстановление SHALL
|
||||||
|
опираться на признак, а не на наличие серверной ссылки.
|
||||||
|
|
||||||
|
#### Scenario: Скачанный элемент не считается своей загрузкой
|
||||||
|
|
||||||
|
- **WHEN** элемент скачан с сервера при двусторонней синхронизации
|
||||||
|
- **THEN** он помечается как скачанный, а не как загруженный этим клиентом
|
||||||
|
|
||||||
|
### Requirement: Двустороннее удаление элементов
|
||||||
|
|
||||||
|
Удаление элемента в альбоме устройства SHALL удалять соответствующее фото на сервере
|
||||||
|
(мягко, в корзину), а серверное удаление SHALL убирать локальный элемент. Для
|
||||||
|
материализованного (серверного) элемента намерением считается только явное удаление в
|
||||||
|
приложении: пропавшая локальная копия SHALL NOT удалять серверное фото, а SHALL
|
||||||
|
материализоваться заново. Операция SHALL подчиняться правам сервера, а удаление файла,
|
||||||
|
снятого на устройстве, SHALL требовать системного согласия.
|
||||||
|
|
||||||
|
#### Scenario: Удаление на устройстве
|
||||||
|
|
||||||
|
- **WHEN** пользователь удаляет элемент синхронизированного альбома устройства
|
||||||
|
- **THEN** приложение помещает соответствующее серверное фото в корзину
|
||||||
|
|
||||||
|
#### Scenario: Удаление на сервере
|
||||||
|
|
||||||
|
- **WHEN** серверное фото помещено в корзину
|
||||||
|
- **THEN** приложение убирает соответствующий локальный элемент
|
||||||
|
|
||||||
|
#### Scenario: Пропала локальная копия серверного фото
|
||||||
|
|
||||||
|
- **WHEN** локальная копия материализованного серверного фото исчезла (очистка галереи, перемещение файла)
|
||||||
|
- **THEN** приложение материализует фото заново и SHALL NOT удалять его на сервере
|
||||||
|
|
||||||
|
#### Scenario: Явное удаление в приложении
|
||||||
|
|
||||||
|
- **WHEN** пользователь удаляет материализованное серверное фото из приложения
|
||||||
|
- **THEN** приложение помечает намерение и удаляет фото на сервере
|
||||||
|
|
||||||
|
#### Scenario: Нет прав на серверное удаление
|
||||||
|
|
||||||
|
- **WHEN** пользователь удаляет элемент, а роль на сервере не позволяет удалить это фото
|
||||||
|
- **THEN** серверное удаление отклоняется, приложение не считает операцию успешной и сообщает об этом
|
||||||
|
|
||||||
|
### Requirement: Журнал удалений и защита от эха
|
||||||
|
|
||||||
|
Локально удалённый элемент SHALL помечаться в журнале удалений, чтобы приложение SHALL NOT
|
||||||
|
материализовать его повторно до подтверждения серверного удаления. Повторные и эхо-события
|
||||||
|
удаления SHALL быть идемпотентны.
|
||||||
|
|
||||||
|
#### Scenario: Удаление ещё не подтверждено сервером
|
||||||
|
|
||||||
|
- **WHEN** элемент удалён на устройстве, а серверное удаление ещё не выполнено
|
||||||
|
- **THEN** при следующем проходе приложение не материализует этот элемент заново
|
||||||
|
|
||||||
|
#### Scenario: Эхо серверного события
|
||||||
|
|
||||||
|
- **WHEN** серверное событие удаления относится к уже удалённому локальному элементу
|
||||||
|
- **THEN** приложение ничего не меняет повторно
|
||||||
|
|
||||||
|
### Requirement: Восстановление элемента из корзины
|
||||||
|
|
||||||
|
Если серверное фото, удалённое с устройства, восстановлено из корзины сервера, приложение
|
||||||
|
SHALL снова материализовать соответствующий элемент на устройстве.
|
||||||
|
|
||||||
|
#### Scenario: Фото вернулось из корзины
|
||||||
|
|
||||||
|
- **WHEN** пользователь восстанавливает серверное фото из корзины
|
||||||
|
- **THEN** приложение материализует соответствующий элемент в альбоме устройства
|
||||||
|
|
||||||
|
### Requirement: Первый проход и ограничение скачивания
|
||||||
|
|
||||||
|
При включении синхронизации приложение SHALL предложить выбрать: скачать всё содержимое
|
||||||
|
целевого серверного альбома или только новое с этого момента. Скачивание SHALL подчиняться
|
||||||
|
ограничению «только Wi-Fi»; при ожидании подходящей сети материализация не начинается.
|
||||||
|
|
||||||
|
#### Scenario: Только новое
|
||||||
|
|
||||||
|
- **WHEN** пользователь выбирает «только новое» при включении синхронизации
|
||||||
|
- **THEN** уже существующее содержимое серверного альбома не скачивается, а материализуются только последующие появления
|
||||||
|
|
||||||
|
#### Scenario: Скачать всё
|
||||||
|
|
||||||
|
- **WHEN** пользователь выбирает «скачать всё» при включении синхронизации
|
||||||
|
- **THEN** приложение материализует весь текущий состав серверного альбома
|
||||||
|
|
||||||
|
#### Scenario: Ожидание WiFi
|
||||||
|
|
||||||
|
- **WHEN** включено ограничение «только Wi-Fi», а доступна только сотовая сеть
|
||||||
|
- **THEN** скачивание не начинается до появления Wi-Fi
|
||||||
|
|
||||||
|
### Requirement: Материализация при нескольких источниках
|
||||||
|
|
||||||
|
Когда несколько альбомов устройства указывают на один серверный альбом, приложение SHALL
|
||||||
|
материализовать серверное фото ровно в один из них, чтобы не создавать дубликат в галерее.
|
||||||
|
Выбор целевого бакета SHALL сохраняться. Содержимое, уже присутствующее на устройстве,
|
||||||
|
SHALL NOT дублироваться.
|
||||||
|
|
||||||
|
#### Scenario: Несколько источников в одну цель
|
||||||
|
|
||||||
|
- **WHEN** два альбома устройства синхронизируются с одним серверным альбомом, и в нём появляется фото
|
||||||
|
- **THEN** приложение создаёт файл ровно в одном из бакетов
|
||||||
|
|
||||||
|
#### Scenario: Содержимое уже на устройстве
|
||||||
|
|
||||||
|
- **WHEN** появившееся серверное фото по содержимому уже присутствует на устройстве
|
||||||
|
- **THEN** приложение связывает элемент с серверным фото, не создавая второй файл
|
||||||
|
|||||||
Reference in New Issue
Block a user