chore(openspec): архивировать android-album-sort и синхронизировать основные спеки
android-client: +1 требование — выбор порядка карточек и направления, порядок охватывает весь список вместе с альбомами устройства.
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
# Design
|
||||
|
||||
## Context
|
||||
|
||||
См. `proposal.md` — мотивация. Ограничения, определяющие подход:
|
||||
|
||||
- Порядок задавался запросом `AlbumDao.observeAll()` (`ORDER BY updatedAt DESC, title ASC`) —
|
||||
из UI его не изменить, а серверный порядок (`created_at DESC`) клиентом не использовался.
|
||||
- Список смешанный: `AlbumListBuilder` собирал два блока — карточки альбомов устройства, а
|
||||
под ними серверные (связанный серверный альбом скрывался, его показывала карточка
|
||||
устройства).
|
||||
- `DeviceAlbum` (MediaStore-бакет) содержит `name`, `itemCount`, `coverUri`, `relativePath`,
|
||||
но **не содержит дат**; дата элемента есть у `DeviceMedia` (`dateTaken`, `dateModified`).
|
||||
- `DeviceMediaMapping.albums` уже вычисляет «самый свежий элемент» бакета, чтобы выбрать
|
||||
обложку, — эту же величину можно взять как дату изменения альбома устройства.
|
||||
- Локальные настройки отображения живут в DataStore (`SettingsRepository`); на вебе аналоги —
|
||||
плотность сетки и тема.
|
||||
- Серверные `users.preferences` заменяются целиком, поэтому настройка отображения туда не
|
||||
идёт (см. решение 1 прошлой редакции).
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- Один порядок для всего списка, включая альбомы устройства, и обратный порядок к нему.
|
||||
- Сохранять выбор на устройстве и не менять поведение по умолчанию.
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- Синхронизация выбора между устройствами.
|
||||
- Сортировка на вебе и на сервере.
|
||||
- Отдельные варианты «по дате создания»: у альбома устройства нет даты создания бакета.
|
||||
|
||||
## Decisions
|
||||
|
||||
**1. Один общий список, а не два блока.**
|
||||
Карточки альбомов устройства и серверных альбомов упорядочиваются вместе по выбранному
|
||||
ключу; источник и раньше был виден по иконкам (карта памяти / облако), поэтому группировка
|
||||
по источнику ничего не добавляла, а сортировка «по названию» в двух блоках выглядела
|
||||
несортированной. Связанная пара остаётся одной карточкой устройства и участвует в порядке
|
||||
как она.
|
||||
Альтернатива — сортировать каждый блок отдельно, оставив устройство сверху: меньше правок,
|
||||
но выбранный порядок не выполнялся бы для половины списка.
|
||||
|
||||
**2. Дата альбома устройства — самый свежий его элемент.**
|
||||
`DeviceMediaMapping` уже выбирает самый свежий элемент для обложки; то же значение
|
||||
становится датой изменения альбома и попадает в `DeviceAlbum`. Дополнительных запросов к
|
||||
MediaStore не нужно — данные уже в курсоре.
|
||||
Альтернативы: `MAX(date_modified)` отдельным запросом (лишний проход по провайдеру);
|
||||
брать только `device_assets` из Room (там лишь файлы, которые приложение уже обрабатывало, —
|
||||
дата была бы почти всегда пустой).
|
||||
|
||||
**3. Направление — отдельный флаг поверх ключа.**
|
||||
Хранится пара «ключ + обратный порядок», а не шесть независимых вариантов: меню остаётся
|
||||
коротким, а переключатель «Обратный порядок» относится к любому ключу. Натуральные
|
||||
направления: время изменения и число фото — по убыванию, название — по возрастанию;
|
||||
обратный порядок инвертирует выбранное.
|
||||
Альтернатива — шесть пунктов меню: нагляднее, но перегружает панель.
|
||||
|
||||
**4. Сравнение — по общим ключам карточки, в сборщике списка.**
|
||||
`AlbumListBuilder` строит единый компаратор по паре «заголовок / число элементов / дата
|
||||
изменения», которую умеет выдать и карточка устройства, и серверная. Серверную дату
|
||||
(`updatedAt`, ISO-8601) приводит к миллисекундам доменная функция; отсутствующая дата уходит
|
||||
в конец. Сортировка остаётся в сборщике, а Room — хранилищем без собственного порядка.
|
||||
Альтернатива — отдельный запрос на каждый режим в `AlbumDao`: размножение запросов и жёсткая
|
||||
связь сортировки с кэшем.
|
||||
|
||||
**5. Хранение — узкий порт `AlbumSortPreference` поверх DataStore.**
|
||||
Пара «ключ + направление» читается и пишется через `AlbumSortPreference`
|
||||
(`SettingsRepository` его реализует). Узость важна не только стилистически: она позволила
|
||||
тестам ViewModel не поднимать DataStore, из-за чего несколько его экземпляров на одном
|
||||
файле в одной JVM встают на файловой блокировке.
|
||||
Альтернатива — читать настройки из общего `SettingsState`: шире зависимость и та же
|
||||
проблема в тестах.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **Смешение источников в одном списке** → карточка устройства и серверная карточка могут
|
||||
стоять рядом; источник различается иконками, а связанная пара по-прежнему одна карточка.
|
||||
- **Альбом устройства без элементов** (пустой бакет) не имеет даты → попадает в конец при
|
||||
сортировке по времени изменения.
|
||||
- **Разбор ISO-даты серверного альбома** может не удаться (неожиданный формат) → такая
|
||||
карточка уходит в конец, а не ломает сравнение.
|
||||
- **Настройка не переносится на другое устройство** → принято: это настройка отображения,
|
||||
а не пользовательские данные.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
Изменение аддитивное: новая пара настроек в DataStore со значением по умолчанию (по времени
|
||||
изменения, без обратного порядка), схема Room и API не меняются. Откат — предыдущая версия
|
||||
APK, при которой список снова упорядочен только по времени изменения, а альбомы устройства
|
||||
закреплены сверху.
|
||||
Reference in New Issue
Block a user