Новое требование в phone-album-sync о потере/возврате серверной копии у разгруженного превью, требование в android-client о метке потерянного элемента и действии удаления с устройства.
716 lines
50 KiB
Markdown
716 lines
50 KiB
Markdown
# android-client Specification
|
||
|
||
## Purpose
|
||
Android-клиент для self-hosted galo: настройка подключения к серверу, авторизация,
|
||
просмотр альбомов и медиа, а также фоновая загрузка фотографий и видео с телефона
|
||
с офлайн-кэшем.
|
||
|
||
## Requirements
|
||
|
||
### Requirement: Настройка подключения к серверу
|
||
|
||
Приложение SHALL требовать явного указания базового URL инстанса galo при первом
|
||
запуске и SHALL проверять доступность сервера перед переходом к работе.
|
||
|
||
#### Scenario: Успешная настройка
|
||
|
||
- **WHEN** пользователь вводит достижимый базовый URL и подтверждает
|
||
- **THEN** приложение проверяет `GET /api/health` и при успехе переходит к входу
|
||
|
||
#### Scenario: Сервер недоступен
|
||
|
||
- **WHEN** сервер недоступен или health вернул ошибку
|
||
- **THEN** приложение показывает ошибку и остаётся на экране настройки, сохраняя введённый URL
|
||
|
||
### Requirement: Небезопасное соединение по LAN
|
||
|
||
Приложение SHALL по умолчанию запрещать незашифрованный HTTP и SHALL разрешать его
|
||
только после явного включения пользователем режима небезопасного соединения.
|
||
|
||
#### Scenario: HTTP без разрешения
|
||
|
||
- **WHEN** пользователь указывает `http`-URL и режим небезопасного соединения выключен
|
||
- **THEN** приложение блокирует подключение и предлагает включить режим
|
||
|
||
#### Scenario: Режим включён
|
||
|
||
- **WHEN** пользователь включил режим небезопасного соединения
|
||
- **THEN** HTTP-подключение разрешено, а интерфейс явно отмечает небезопасное соединение
|
||
|
||
### Requirement: Разрешение относительных URL объектов
|
||
|
||
Приложение SHALL трактовать относительные URL объектов из REST-ответов и SSE как пути
|
||
на выбранном базовом URL сервера.
|
||
|
||
#### Scenario: Отдача объекта
|
||
|
||
- **WHEN** API возвращает `thumb_url` вида `/api/objects/<key>?...`
|
||
- **THEN** приложение запрашивает объект с того же инстанса, что и базовый URL
|
||
|
||
### Requirement: Вход и хранение сессии
|
||
|
||
Приложение SHALL позволять вход и регистрацию по email и паролю и SHALL хранить токены
|
||
только в защищённом хранилище платформы.
|
||
|
||
#### Scenario: Успешный вход
|
||
|
||
- **WHEN** вход проходит успешно
|
||
- **THEN** токены сохраняются в защищённом хранилище, пользователь видит список альбомов
|
||
|
||
#### Scenario: Перезапуск с сессией
|
||
|
||
- **WHEN** приложение перезапущено с сохранённой сессией
|
||
- **THEN** пользователь остаётся авторизованным без повторного ввода пароля
|
||
|
||
### Requirement: Автоматическое обновление токена
|
||
|
||
Приложение SHALL автоматически обновлять access-токен при ответе 401, пока refresh-токен
|
||
действителен, и SHALL завершать сессию при неудачном обновлении.
|
||
|
||
#### Scenario: Прозрачное обновление
|
||
|
||
- **WHEN** запрос получает 401 и refresh-токен действителен
|
||
- **THEN** приложение обновляет токен и повторяет исходный запрос без участия пользователя
|
||
|
||
#### Scenario: Обновление не удалось
|
||
|
||
- **WHEN** обновление токена завершилось ошибкой
|
||
- **THEN** сессия очищается и пользователь возвращается на экран входа
|
||
|
||
### Requirement: Список альбомов
|
||
|
||
Приложение SHALL показывать доступные пользователю альбомы одним смешанным списком:
|
||
серверные альбомы, получаемые из API, а при отсутствии сети — из локального кэша, и
|
||
альбомы устройства. Элементы SHALL различать источник. Связанная пара «альбом
|
||
устройства → серверный альбом» SHALL отображаться одной карточкой альбома устройства
|
||
с указателем связи и меткой режима; связанный серверный альбом SHALL NOT показываться
|
||
в списке отдельной записью.
|
||
|
||
#### Scenario: Онлайн
|
||
|
||
- **WHEN** авторизованный пользователь открывает приложение
|
||
- **THEN** отображается список альбомов с обложками или плейсхолдером
|
||
|
||
#### Scenario: Офлайн из кэша
|
||
|
||
- **WHEN** сеть недоступна, но кэш существует
|
||
- **THEN** список альбомов отображается из кэша с признаком офлайн-режима
|
||
|
||
#### Scenario: Альбомы устройства
|
||
|
||
- **WHEN** у приложения есть доступ к медиа устройства
|
||
- **THEN** в списке появляются альбомы устройства наряду с серверными и помечены источником
|
||
|
||
#### Scenario: Связанная пара
|
||
|
||
- **WHEN** альбом устройства синхронизируется с серверным альбомом
|
||
- **THEN** в списке показывается одна карточка альбома устройства с указателем связи и меткой режима, а связанный серверный альбом отдельной записью не показывается
|
||
|
||
### Requirement: Экран альбома устройства и настройки синхронизации
|
||
|
||
Приложение SHALL открывать альбом устройства с сеткой медиа и SHALL предоставлять
|
||
настройки синхронизации этого альбома по свайпу вправо. Настройки SHALL содержать
|
||
только выбор режима синхронизации.
|
||
|
||
#### Scenario: Открытие альбома устройства
|
||
|
||
- **WHEN** пользователь выбирает альбом устройства в списке
|
||
- **THEN** открывается сетка медиа этого альбома
|
||
|
||
#### Scenario: Настройки по свайпу
|
||
|
||
- **WHEN** пользователь свайпает вправо в открытом альбоме устройства
|
||
- **THEN** открываются настройки синхронизации с выбором режима и только им
|
||
|
||
#### Scenario: Метка перенесённого элемента
|
||
|
||
- **WHEN** оригинал элемента разгружен на сервер в режиме переноса
|
||
- **THEN** в сетке на элементе показывается значок облака
|
||
|
||
#### Scenario: Метка потерянного элемента
|
||
|
||
- **WHEN** серверной копии разгруженного элемента больше нет в целевом альбоме
|
||
- **THEN** в сетке на элементе показывается перечёркнутый значок облака
|
||
|
||
### Requirement: Доступ к альбомам устройства
|
||
|
||
Приложение SHALL запрашивать доступ к медиа устройства, нужный для перечисления
|
||
альбомов и чтения файлов, и SHALL работать без него, не показывая альбомы
|
||
устройства.
|
||
|
||
#### Scenario: Доступ предоставлен
|
||
|
||
- **WHEN** пользователь предоставляет доступ к медиа устройства
|
||
- **THEN** приложение перечисляет альбомы устройства и их содержимое
|
||
|
||
#### Scenario: Доступ не предоставлен
|
||
|
||
- **WHEN** доступ к медиа устройства не предоставлен
|
||
- **THEN** приложение продолжает работать с серверными альбомами и не показывает альбомы устройства
|
||
|
||
### Requirement: Постраничный просмотр альбома
|
||
|
||
Приложение SHALL показывать сетку медиа альбома с постраничной загрузкой по курсору и
|
||
SHALL подгружать следующую страницу при достижении конца списка.
|
||
|
||
#### Scenario: Догрузка страницы
|
||
|
||
- **WHEN** пользователь прокручивает сетку до конца
|
||
- **THEN** приложение запрашивает следующую страницу по курсору и добавляет её к сетке
|
||
|
||
#### Scenario: Пустой альбом
|
||
|
||
- **WHEN** в альбоме нет медиа
|
||
- **THEN** отображается пустое состояние
|
||
|
||
### Requirement: Просмотр медиа на весь экран
|
||
|
||
Полноэкранный просмотр SHALL поддерживать изображение с зумом пальцами, видео с постером
|
||
и управлением воспроизведением, а также панораму 360; навигация между элементами альбома
|
||
SHALL выполняться свайпом.
|
||
|
||
#### Scenario: Свайп между элементами
|
||
|
||
- **WHEN** пользователь свайпает в просмотре
|
||
- **THEN** показывается следующий или предыдущий элемент альбома без выхода из просмотра
|
||
|
||
#### Scenario: Видео
|
||
|
||
- **WHEN** открыт видеоэлемент
|
||
- **THEN** показывается постер и доступно воспроизведение с перемоткой
|
||
|
||
### Requirement: Обновления в реальном времени
|
||
|
||
Пока экран альбома активен, приложение SHALL отображать изменения по SSE (новые,
|
||
обновлённые и удалённые медиа, социальные события) и SHALL догонять пропущенное при
|
||
переподключении.
|
||
|
||
#### Scenario: Новое фото с другого устройства
|
||
|
||
- **WHEN** другое устройство добавляет фото в открытый альбом
|
||
- **THEN** новое фото появляется в сетке без ручного обновления
|
||
|
||
#### Scenario: Переподключение
|
||
|
||
- **WHEN** SSE-соединение разорвалось и восстановилось
|
||
- **THEN** приложение дозагружает изменения, произошедшие за время разрыва
|
||
|
||
### Requirement: Выбор медиа и альбома для загрузки
|
||
|
||
Приложение SHALL предоставлять выбор одного или нескольких фото и видео из системной
|
||
галереи и SHALL требовать выбора альбома назначения перед постановкой в очередь.
|
||
|
||
#### Scenario: Постановка в очередь
|
||
|
||
- **WHEN** пользователь выбрал файлы и альбом назначения
|
||
- **THEN** задания загрузки ставятся в очередь и пользователь видит их статус
|
||
|
||
#### Scenario: Отказ сервера
|
||
|
||
- **WHEN** сервер отклоняет загрузку (например, из-за прав или размера)
|
||
- **THEN** задание помечается ошибкой с причиной от сервера
|
||
|
||
### Requirement: Фоновая очередь загрузки
|
||
|
||
Загрузка SHALL выполняться по одному файлу за HTTP-запрос и SHALL продолжаться при
|
||
уходе приложения из foreground, показывая прогресс в уведомлении; при ошибке SHALL
|
||
выполняться повтор с нарастающей задержкой.
|
||
|
||
#### Scenario: Уход в фон
|
||
|
||
- **WHEN** приложение уходит в фон во время загрузки
|
||
- **THEN** загрузка продолжается и прогресс отображается в уведомлении
|
||
|
||
#### Scenario: Повтор при ошибке
|
||
|
||
- **WHEN** загрузка файла завершилась сетевой ошибкой
|
||
- **THEN** задание повторяется, а после исчерпания попыток помечается ошибкой
|
||
|
||
### Requirement: Условия сети для загрузки
|
||
|
||
Приложение SHALL позволять ограничить загрузку сетью Wi-Fi; при ожидании подходящей
|
||
сети задание остаётся в очереди и не выполняется.
|
||
|
||
#### Scenario: Только Wi-Fi
|
||
|
||
- **WHEN** ограничение «только Wi-Fi» включено, а доступна только сотовая сеть
|
||
- **THEN** загрузка не начинается до появления Wi-Fi, а задание остаётся в очереди
|
||
|
||
### Requirement: Сохранность очереди между запусками
|
||
|
||
Очередь загрузки SHALL сохраняться на устройстве и SHALL продолжаться после перезапуска
|
||
приложения или устройства.
|
||
|
||
#### Scenario: Возобновление после перезапуска
|
||
|
||
- **WHEN** приложение или устройство перезапускается при незавершённой очереди
|
||
- **THEN** незавершённые задания возобновляются
|
||
|
||
### Requirement: Конвертация HEIC в JPEG
|
||
|
||
Файлы HEIC/HEIF SHALL конвертироваться на устройстве в JPEG перед загрузкой с
|
||
сохранением даты съёмки и геолокации.
|
||
|
||
#### Scenario: Загрузка HEIC
|
||
|
||
- **WHEN** пользователь ставит в очередь HEIC-файл
|
||
- **THEN** загружается JPEG-версия, а дата съёмки и координаты из EXIF сохраняются
|
||
|
||
#### Scenario: Ошибка конвертации
|
||
|
||
- **WHEN** конвертация завершилась ошибкой
|
||
- **THEN** задание помечается ошибкой, повреждённый файл не загружается
|
||
|
||
### Requirement: Кэш медиа по устойчивому ключу
|
||
|
||
Приложение SHALL кэшировать полученные превью и оригиналы по устойчивому идентификатору
|
||
объекта, не зависящему от подписи и срока действия URL.
|
||
|
||
#### Scenario: Смена подписи
|
||
|
||
- **WHEN** URL одного и того же объекта со временем получает новую подпись
|
||
- **THEN** приложение переиспользует закэшированный объект вместо повторной загрузки
|
||
|
||
#### Scenario: Офлайн-отдача из кэша
|
||
|
||
- **WHEN** сеть недоступна, а объект есть в кэше
|
||
- **THEN** объект отображается из кэша
|
||
|
||
### Requirement: Единый формат ошибок API
|
||
|
||
Приложение SHALL распознавать ошибки API формата `{error:{code,message}}` и SHALL
|
||
показывать пользователю понятное сообщение.
|
||
|
||
#### Scenario: Ошибка с кодом и сообщением
|
||
|
||
- **WHEN** сервер возвращает ошибку с `code` и `message`
|
||
- **THEN** приложение показывает сообщение, при необходимости адаптированное под язык интерфейса
|
||
|
||
### Requirement: Зум изображения жестами
|
||
|
||
Полноэкранный просмотр изображения SHALL поддерживать увеличение и уменьшение щипком
|
||
(pinch), а при увеличенном изображении — панорамирование перетаскиванием.
|
||
|
||
#### Scenario: Щипок увеличивает изображение
|
||
|
||
- **WHEN** пользователь разводит пальцы на изображении
|
||
- **THEN** изображение увеличивается относительно вписанного вида
|
||
|
||
#### Scenario: Панорамирование при увеличении
|
||
|
||
- **WHEN** изображение увеличено, и пользователь перетаскивает его пальцем
|
||
- **THEN** видимая область изображения смещается, а границы изображения не отрываются от краёв области просмотра
|
||
|
||
### Requirement: Двойной тап переключает вписанный вид и 100%
|
||
|
||
Двойной тап по изображению SHALL переключать вписанный вид и масштаб 100% натурального
|
||
размера с якорем в точку нажатия.
|
||
|
||
#### Scenario: Вход в 100% из вписанного вида
|
||
|
||
- **WHEN** изображение показано вписанным, и пользователь делает двойной тап
|
||
- **THEN** изображение показывается в масштабе 100% натурального размера с центром в точке нажатия
|
||
|
||
#### Scenario: Возврат во вписанный вид
|
||
|
||
- **WHEN** изображение увеличено, и пользователь делает двойной тап
|
||
- **THEN** изображение возвращается во вписанный вид
|
||
|
||
### Requirement: Предел зума по натуральному размеру
|
||
|
||
Зум изображения SHALL ограничиваться снизу вписанным видом, а сверху — 200% натурального
|
||
размера изображения; при входе в зум SHALL догружаться оригинал изображения.
|
||
|
||
#### Scenario: Верхний предел
|
||
|
||
- **WHEN** пользователь продолжает увеличивать изображение за 200% натурального размера
|
||
- **THEN** масштаб останавливается на 200%, изображение не увеличивается дальше
|
||
|
||
#### Scenario: Оригинал при увеличении
|
||
|
||
- **WHEN** пользователь входит в зум
|
||
- **THEN** догружается оригинал изображения и подменяет превью по факту загрузки
|
||
|
||
### Requirement: Навигация и сброс при зуме
|
||
|
||
Пока изображение увеличено, горизонтальный свайп SHALL NOT листать альбом, а жест SHALL
|
||
отдаваться панорамированию; при переходе на другой элемент просмотра зум SHALL сбрасываться
|
||
во вписанный вид.
|
||
|
||
#### Scenario: Свайп не листает увеличенное изображение
|
||
|
||
- **WHEN** изображение увеличено, и пользователь свайпает по нему
|
||
- **THEN** альбом не листается, а изображение панорамируется
|
||
|
||
#### Scenario: Сброс при смене фото
|
||
|
||
- **WHEN** пользователь переходит на другой элемент и возвращается к увеличенному изображению
|
||
- **THEN** изображение показывается во вписанном виде
|
||
|
||
### Requirement: Индикатор загрузки оригинала
|
||
|
||
Пока оригинал изображения загружается после входа в зум, приложение SHALL показывать индикатор
|
||
загрузки в правом верхнем углу области просмотра.
|
||
|
||
#### Scenario: Оригинал загружается
|
||
|
||
- **WHEN** пользователь входит в зум, и оригинал ещё загружается
|
||
- **THEN** в правом верхнем углу показывается индикатор загрузки
|
||
|
||
#### Scenario: Загрузка завершена
|
||
|
||
- **WHEN** оригинал загружен или недоступен
|
||
- **THEN** индикатор загрузки скрыт
|
||
|
||
### Requirement: Проверка доступа «Все файлы» при запуске
|
||
Приложение SHALL при запуске, когда оно уже настроено и авторизовано, проверять наличие доступа
|
||
«Все файлы» и при его отсутствии SHALL предлагать его включить, открывая системный экран настроек.
|
||
|
||
#### Scenario: Доступ не выдан
|
||
- **WHEN** приложение запущено и доступ «Все файлы» не выдан
|
||
- **THEN** показывается диалог с предложением включить доступ
|
||
|
||
#### Scenario: Включение доступа
|
||
- **WHEN** пользователь подтверждает
|
||
- **THEN** открывается системный экран выдачи доступа «Все файлы»
|
||
|
||
#### Scenario: Доступ уже выдан
|
||
- **WHEN** доступ уже выдан
|
||
- **THEN** диалог не показывается
|
||
|
||
### Requirement: Возврат оригиналов при выходе из переноса
|
||
При переключении альбома из режима переноса в «копии» или «выключено» приложение SHALL
|
||
возвращать оригиналы с сервера (когда пользователь выбирает возврат), а незавершённый возврат
|
||
SHALL довосстанавливаться при следующей проверке (при запуске и при открытии альбома).
|
||
|
||
#### Scenario: Перенос → копии
|
||
- **WHEN** пользователь переключает альбом из переноса в копии и подтверждает возврат оригиналов
|
||
- **THEN** оригиналы скачиваются с сервера и заменяют превью на устройстве
|
||
|
||
#### Scenario: Довосстановление
|
||
- **WHEN** альбом не в режиме переноса, но содержит превью с серверным фото
|
||
- **THEN** при запуске или открытии альбома оригиналы довосстанавливаются
|
||
|
||
#### Scenario: Перенос → выключено
|
||
- **WHEN** пользователь переключает перенос в выключено
|
||
- **THEN** предлагается вернуть оригиналы или оставить превью
|
||
|
||
### Requirement: Единая проверка соответствия режиму
|
||
При запуске и при открытии альбома приложение SHALL приводить локальное содержимое в
|
||
соответствие режиму для всех сочетаний состояния ассета: в «переносе» разгружать уже загруженные
|
||
оригиналы, а в «копиях»/«выключено» восстанавливать превью; отсутствующий локальный индекс SHALL
|
||
восстанавливаться по метке происхождения.
|
||
|
||
#### Scenario: Копии с разгруженными файлами
|
||
- **WHEN** альбом в режиме копий, а часть файлов разгружена (превью)
|
||
- **THEN** проверка восстанавливает оригиналы
|
||
|
||
#### Scenario: Перенос с неразгруженными файлами
|
||
- **WHEN** альбом в режиме переноса, файл есть на сервере, но оригинал ещё на телефоне
|
||
- **THEN** проверка разгружает оригинал
|
||
|
||
#### Scenario: Потерянный индекс
|
||
- **WHEN** у разгруженного медиа нет записи в локальном индексе
|
||
- **THEN** связь и происхождение восстанавливаются по метке в файле
|
||
|
||
#### Scenario: Выключенный режим
|
||
- **WHEN** альбом без синхронизации содержит превью
|
||
- **THEN** оригиналы восстанавливаются, а сервер не затрагивается
|
||
|
||
### Requirement: Отсутствие дублей и гонок при синхронизации
|
||
Проверка синхронизации SHALL выполняться последовательно (без одновременных проходов по одной
|
||
галерее) и SHALL NOT ставить повторную загрузку, пока по тому же элементу уже есть задание в
|
||
очереди. Метка происхождения в разгруженном файле SHALL хранить режим синхронизации, а не
|
||
состояние элемента, чтобы восстановление не меняло режим галереи.
|
||
|
||
#### Scenario: Повторная проверка во время загрузки
|
||
- **WHEN** проверка запускается снова, пока загрузка элемента ещё стоит в очереди
|
||
- **THEN** повторная загрузка не ставится
|
||
|
||
#### Scenario: Метка происхождения хранит режим
|
||
- **WHEN** разгруженный файл восстанавливается после переустановки
|
||
- **THEN** режим галереи берётся из метки как режим (перенос/копии), а не как состояние элемента
|
||
|
||
### Requirement: Подпись карточки альбома
|
||
Карточка альбома SHALL показывать в первой строке имя альбома, а для связанной пары — со
|
||
стрелкой имя связанного серверного альбома, и только когда имена различаются. Вторая строка
|
||
SHALL показывать значками расположение содержимого и число элементов: значок карты памяти для
|
||
локального без синхронизации, значки карты памяти и облака для режима копий, значок облака для
|
||
режима переноса; для серверного альбома — значок облака.
|
||
|
||
#### Scenario: Локальный без синхронизации
|
||
- **WHEN** показан локальный альбом без синхронизации
|
||
- **THEN** вторая строка показывает значок карты памяти и число элементов
|
||
|
||
#### Scenario: Режим копий
|
||
- **WHEN** локальный альбом синхронизируется в режиме копий
|
||
- **THEN** вторая строка показывает значки карты памяти и облака и число элементов
|
||
|
||
#### Scenario: Режим переноса
|
||
- **WHEN** локальный альбом синхронизируется в режиме переноса
|
||
- **THEN** вторая строка показывает значок облака и число элементов
|
||
|
||
#### Scenario: Разные имена связанной пары
|
||
- **WHEN** имя локального альбома не совпадает с именем связанного серверного
|
||
- **THEN** первая строка показывает «имя → имя серверного»
|
||
|
||
#### Scenario: Одинаковые имена
|
||
- **WHEN** имена совпадают
|
||
- **THEN** стрелка и имя серверного не показываются
|
||
|
||
### Requirement: Панель атрибутов и действий альбома
|
||
Приложение SHALL открывать панель «Атрибуты и действия» по свайпу справа налево на экране
|
||
любого альбома — серверного и локального. Панель SHALL повторять структуру web: разделы
|
||
«Обложка», «Информация», «Навигация» и раздел «Действия».
|
||
|
||
#### Scenario: Открытие свайпом
|
||
- **WHEN** пользователь свайпает справа налево в открытом альбоме
|
||
- **THEN** открывается панель «Атрибуты и действия»
|
||
|
||
#### Scenario: Структура панели
|
||
- **WHEN** панель открыта
|
||
- **THEN** показаны разделы «Обложка», «Информация», «Навигация» и «Действия»
|
||
|
||
#### Scenario: Закрытие панели
|
||
- **WHEN** пользователь закрывает панель (свайп в обратную сторону или касание вне панели)
|
||
- **THEN** панель скрывается, сетка медиа снова доступна
|
||
|
||
### Requirement: Источник атрибутов локального альбома
|
||
Для локального альбома приложение SHALL показывать ту же панель «Атрибуты и действия», что и
|
||
для серверного, а метаданные SHALL брать локально (имя, число элементов, локальная обложка,
|
||
режим синхронизации, связанный серверный альбом). Действия локального альбома SHALL
|
||
применяться локально.
|
||
|
||
#### Scenario: Локальные метаданные
|
||
- **WHEN** пользователь открывает панель локального альбома
|
||
- **THEN** раздел «Информация» показывает локальные имя, число элементов и обложку
|
||
|
||
#### Scenario: Неактивная навигация без связи
|
||
- **WHEN** локальный альбом не связан с серверным
|
||
- **THEN** строки «Участники» и «Ссылки для просмотра» показаны, но неактивны
|
||
|
||
#### Scenario: Активная навигация при связи
|
||
- **WHEN** локальный альбом связан с серверным
|
||
- **THEN** строки «Участники» и «Ссылки для просмотра» активны и относятся к связанному серверному альбому
|
||
|
||
### Requirement: Переименование и обложка локального альбома
|
||
Для локального альбома действие «Переименовать» SHALL менять отображаемое имя в приложении, не
|
||
изменяя медиа или bucket на устройстве, а «Выбрать обложку»/«Сбросить» SHALL назначать или
|
||
снимать обложку из медиа этого альбома. Имя и обложка SHALL храниться локально.
|
||
|
||
#### Scenario: Переименование
|
||
- **WHEN** пользователь вводит новое имя локального альбома
|
||
- **THEN** панель, заголовок и карточка в списке альбомов показывают новое имя
|
||
|
||
#### Scenario: Назначение и сброс обложки
|
||
- **WHEN** пользователь выбирает медиа обложкой или сбрасывает её
|
||
- **THEN** панель и карточка альбома отражают выбор
|
||
|
||
### Requirement: Удаление локального альбома с телефона
|
||
Действие «Удалить» локального альбома SHALL после подтверждения удалять все фото и видео
|
||
этого альбома с устройства, запрашивая при необходимости системное согласие.
|
||
|
||
#### Scenario: Подтверждение
|
||
- **WHEN** пользователь выбирает «Удалить» локального альбома
|
||
- **THEN** открывается диалог с предупреждением, что медиа будут удалены с телефона
|
||
|
||
#### Scenario: Удаление
|
||
- **WHEN** пользователь подтверждает удаление
|
||
- **THEN** медиа удаляются с устройства, а альбом исчезает из приложения
|
||
|
||
### Requirement: Атрибуты альбома
|
||
Панель SHALL показывать обложку с выбором и сбросом, информацию (название, описание, число
|
||
фото и участников) и переходы к участникам, настройкам и ссылкам для просмотра. Управление
|
||
обложкой и раздел «Навигация» SHALL быть доступны только владельцу и администратору.
|
||
|
||
#### Scenario: Управление обложкой
|
||
- **WHEN** владелец или администратор выбирает или сбрасывает обложку
|
||
- **THEN** изменение сохраняется на сервере и отражается в списке и панели
|
||
|
||
#### Scenario: Наблюдатель
|
||
- **WHEN** наблюдатель открывает панель
|
||
- **THEN** управление обложкой не показывается, а строки «Настройки» и «Ссылки для просмотра» неактивны
|
||
|
||
#### Scenario: Переход к настройкам
|
||
- **WHEN** владелец или администратор открывает настройки из раздела «Навигация»
|
||
- **THEN** доступны правка названия и описания и управление ссылками для просмотра
|
||
|
||
### Requirement: Действия с альбомом
|
||
Панель SHALL содержать действия «Переименовать» и «Удалить». Удаление SHALL подтверждаться
|
||
диалогом, а удалённый альбом SHALL помещать свои фото в корзину.
|
||
|
||
#### Scenario: Переименование
|
||
- **WHEN** владелец или администратор выбирает «Переименовать» и вводит новое название
|
||
- **THEN** название сохраняется на сервере и отражается в списке и заголовке
|
||
|
||
#### Scenario: Удаление с подтверждением
|
||
- **WHEN** владелец выбирает «Удалить» и подтверждает в диалоге
|
||
- **THEN** альбом удаляется, а его фото попадают в корзину
|
||
|
||
### Requirement: Лист атрибутов и действий фото
|
||
Полноэкранный просмотр SHALL открывать лист «Атрибуты и действия» по свайпу вверх как в
|
||
серверном, так и в device-просмотре. Лист SHALL повторять структуру web: разделы
|
||
«Информация», «Метки» и «Действия». При зуме изображения, панораме или активном управлении
|
||
видео свайп вверх SHALL NOT открывать лист.
|
||
|
||
#### Scenario: Открытие листа
|
||
- **WHEN** пользователь свайпает вверх во вписанном изображении
|
||
- **THEN** открывается лист «Атрибуты и действия» для текущего элемента
|
||
|
||
#### Scenario: Зум или панорама
|
||
- **WHEN** изображение увеличено или открыта панорама, и пользователь свайпает вверх
|
||
- **THEN** лист не открывается, жест обрабатывается изображением
|
||
|
||
#### Scenario: Видео
|
||
- **WHEN** открыт видеоэлемент, и пользователь взаимодействует с плеером
|
||
- **THEN** свайп вверх не мешает управлению воспроизведением
|
||
|
||
### Requirement: Атрибуты фото в листе
|
||
Раздел «Информация» SHALL показывать автора, размер файла, тип, разрешение, EXIF-атрибуты,
|
||
дату съёмки, координаты и дату загрузки. Раздел «Метки» SHALL позволять добавлять и снимать
|
||
метки с поиском по меткам пользователя и созданием новых.
|
||
|
||
#### Scenario: Информация
|
||
- **WHEN** лист открыт
|
||
- **THEN** раздел «Информация» показывает метаданные и EXIF текущего фото
|
||
|
||
#### Scenario: Метки
|
||
- **WHEN** пользователь добавляет или снимает метку в разделе «Метки»
|
||
- **THEN** изменение сразу отражается в просмотре без перезагрузки
|
||
|
||
### Requirement: Настройки списка атрибутов
|
||
Раздел «Информация» SHALL давать доступ к настройкам со списком EXIF-атрибутов, где
|
||
пользователь отмечает отображаемые атрибуты и задаёт их подписи, с действиями «отметить все»
|
||
и «снять все». Настройки SHALL применяться ко всем фото и сохраняться в профиле пользователя.
|
||
|
||
#### Scenario: Открытие настроек
|
||
- **WHEN** пользователь открывает настройки атрибутов
|
||
- **THEN** показан список EXIF-атрибутов с галочками видимости и полями подписей
|
||
|
||
#### Scenario: Сохранение настроек
|
||
- **WHEN** пользователь отмечает атрибуты, задаёт подписи и сохраняет
|
||
- **THEN** настройки сохраняются в профиле и применяются в разделе «Информация»
|
||
|
||
#### Scenario: Сброс настроек
|
||
- **WHEN** пользователь сбрасывает настройки
|
||
- **THEN** применяется базовый набор атрибутов с исходными подписями
|
||
|
||
### Requirement: Действия с фото в листе
|
||
Раздел «Действия» SHALL содержать «Скачать», «Переместить», «Копировать», «Установить как
|
||
обложку альбома» (по правам) и «Удалить»; из сетки также «Открыть (просмотр)». Удаление SHALL
|
||
подтверждаться диалогом. Переименование SHALL выполняться по тапу на имя файла.
|
||
|
||
#### Scenario: Состав действий
|
||
- **WHEN** лист открыт
|
||
- **THEN** показаны действия «Скачать», «Переместить», «Копировать», «Установить как обложку» и «Удалить» по правам
|
||
|
||
#### Scenario: Удаление фото
|
||
- **WHEN** пользователь выбирает «Удалить» и подтверждает
|
||
- **THEN** фото уходит в корзину, а просмотр переходит к соседнему элементу
|
||
|
||
#### Scenario: Переименование фото
|
||
- **WHEN** автор, владелец или администратор меняет имя файла на непустое
|
||
- **THEN** новое имя сохраняется и отражается в просмотре и сетке
|
||
|
||
### Requirement: Источник атрибутов device-медиа
|
||
Для device-медиа приложение SHALL показывать атрибуты и действия сопоставленного серверного
|
||
фото, когда сопоставление есть, и локальные атрибуты и действия (имя, размер, тип,
|
||
разрешение, дата, путь) при его отсутствии.
|
||
|
||
#### Scenario: Сопоставлено серверному фото
|
||
- **WHEN** device-медиа сопоставлено серверному фото
|
||
- **THEN** лист показывает серверные атрибуты и серверные действия этого фото
|
||
|
||
#### Scenario: Не сопоставлено
|
||
- **WHEN** сопоставления с серверным фото нет
|
||
- **THEN** лист показывает локальные атрибуты и локальные действия элемента
|
||
|
||
### Requirement: Живое обновление списка альбомов
|
||
Пока пользователь авторизован, приложение SHALL поддерживать персональный поток и
|
||
применять события жизненного цикла альбома к локальному кэшу: создавать или заменять
|
||
альбом по полученному объекту и удалять его по идентификатору, не выполняя
|
||
дополнительный запрос списка.
|
||
|
||
#### Scenario: Альбом создан или изменён в другом месте
|
||
- **WHEN** в персональный поток приходит событие создания или изменения доступного альбома
|
||
- **THEN** альбом появляется в списке или заменяется без запроса списка
|
||
|
||
#### Scenario: Неизвестное событие
|
||
- **WHEN** в персональный поток приходит событие, не относящееся к жизненному циклу альбома
|
||
- **THEN** приложение не меняет локальный список
|
||
|
||
#### Scenario: Навигация между экранами
|
||
- **WHEN** пользователь переходит с главного экрана на другой экран приложения
|
||
- **THEN** подписка на персональный поток не пересоздаётся
|
||
|
||
#### Scenario: Выход из аккаунта
|
||
- **WHEN** пользователь выходит из аккаунта
|
||
- **THEN** подписка на персональный поток закрывается
|
||
|
||
### Requirement: Догон списка при переподключении персонального потока
|
||
После восстановления персонального потока приложение SHALL приводить локальный список
|
||
альбомов в соответствие с серверным снимком, чтобы отразить изменения, пропущенные за
|
||
время разрыва соединения.
|
||
|
||
#### Scenario: Переподключение
|
||
- **WHEN** персональный поток восстановился после разрыва
|
||
- **THEN** приложение перезапрашивает список альбомов, удаляя из кэша альбомы, которых на сервере больше нет
|
||
|
||
### Requirement: Потеря доступа к альбому
|
||
Приложение SHALL переставать отображать альбом, удалённый владельцем или ставший
|
||
недоступным после исключения участника: альбом SHALL исчезать из локального кэша и
|
||
списка, а экран открытого альбома SHALL возвращаться к списку.
|
||
|
||
#### Scenario: Альбом удалён во время просмотра
|
||
- **WHEN** владелец удаляет альбом, пока участник просматривает экран этого альбома
|
||
- **THEN** экран перестаёт показывать альбом и возвращается к списку
|
||
|
||
#### Scenario: Участник исключён
|
||
- **WHEN** участника исключают из альбома
|
||
- **THEN** альбом исчезает из его списка, а открытый экран возвращается к списку
|
||
|
||
#### Scenario: Экран альбома не открыт
|
||
- **WHEN** альбом удалён, а его экран не открыт
|
||
- **THEN** карточка альбома исчезает из списка
|
||
|
||
#### Scenario: Отсутствие альбома в кэше без сети
|
||
- **WHEN** приложение открывает экран альбома, которого нет в кэше, и сеть недоступна
|
||
- **THEN** экран не считается потерявшим доступ и не закрывается сам
|
||
|
||
### Requirement: Выбор сортировки списка альбомов
|
||
Приложение SHALL предоставлять выбор порядка карточек в списке альбомов: «по времени
|
||
изменения», «по названию» или «по числу фото», а также обратный порядок для выбранного
|
||
варианта. Выбор SHALL сохраняться локально на устройстве и применяться при следующем
|
||
запуске. Порядок SHALL применяться ко всему списку: карточки альбомов устройства и
|
||
серверных альбомов упорядочиваются вместе.
|
||
|
||
#### Scenario: Выбор варианта
|
||
- **WHEN** пользователь выбирает вариант сортировки
|
||
- **THEN** карточки списка перестраиваются в соответствии с выбором
|
||
|
||
#### Scenario: Обратный порядок
|
||
- **WHEN** пользователь включает обратный порядок
|
||
- **THEN** выбранный вариант применяется в обратном направлении
|
||
|
||
#### Scenario: Альбомы устройства
|
||
- **WHEN** выбрана любая сортировка
|
||
- **THEN** карточки альбомов устройства упорядочиваются вместе с серверными, а для варианта «по времени изменения» используется дата самого свежего элемента альбома устройства
|
||
|
||
#### Scenario: Сортировка по умолчанию
|
||
- **WHEN** пользователь ещё не выбирал сортировку
|
||
- **THEN** карточки упорядочены по времени изменения, самые свежие сверху, обратный порядок выключен
|
||
|
||
#### Scenario: Сохранение выбора
|
||
- **WHEN** пользователь перезапускает приложение
|
||
- **THEN** список открывается с сохранёнными вариантом и направлением
|
||
|
||
### Requirement: Действия потерявшего серверную копию превью
|
||
|
||
Шит потерявшего серверную копию элемента SHALL предлагать действие «Удалить с
|
||
устройства» и SHALL NOT показывать серверные атрибуты и действия.
|
||
|
||
#### Scenario: Только удаление с устройства
|
||
|
||
- **WHEN** пользователь открывает атрибуты потерявшего серверную копию элемента
|
||
- **THEN** доступно действие «Удалить с устройства», а серверные действия отсутствуют
|