Files
ds-galo/openspec/changes/archive/2026-10-16-android-album-sort/design.md
T
iu e4fc6f0ce9 chore(openspec): архивировать android-album-sort и синхронизировать основные спеки
android-client: +1 требование — выбор порядка карточек и направления, порядок
охватывает весь список вместе с альбомами устройства.
2026-10-16 13:27:30 +03:00

7.9 KiB

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, при которой список снова упорядочен только по времени изменения, а альбомы устройства закреплены сверху.