feat(contacts): P2 шаг1 — типы TEL/EMAIL (парсинг из raw, подписи в карточке, сохранение при правке) +4 теста

This commit is contained in:
b-mob-contacts
2026-07-13 08:57:34 +00:00
parent 559a35aeb3
commit a8741e982c
6 changed files with 151 additions and 26 deletions
+1
View File
@@ -2,6 +2,7 @@
Формат: `ГГГГ-ММ-ДД | версия | изменение | контракты | риск`
- 2026-07-13 | (без бампа — версия/релиз за лидом) | **Контакты — типы TEL/EMAIL (P2, шаг 1).** `VCardEditor.parseTypedValues` (+публичный делегат `CardDavClient.parseTypedValues`) извлекает значения EMAIL/TEL с токеном TYPE (WORK/HOME/CELL), игнорируя технические INTERNET/VOICE/PREF; разбор из уже сохранённого `raw` vCard — **без новой Room-миграции**. `ContactItem.emailEntries`/`phoneEntries` (фолбэк на значения без типа). Карточка: подписи полей по типу («Рабочий»/«Личный»/«Мобильный»/«Домашний»/…, `ContactUi.emailTypeLabel`/`phoneTypeLabel`) вместо «Email 2»/«Телефон 2». Форма правки: типы сохраняются по значению (`emailTypeByValue`/`phoneTypeByValue`) — **исправлен P1-каветат** нормализации типов к INTERNET/CELL при правке. +4 юнит-теста (`VCardEditorTest`, всего 14): парсинг типов, игнор технических токенов, пустой тип, round-trip сохраняет типы. Гейты зелёные (`:app:compileReleaseKotlin`, `:feature:contacts:detekt`+compile). Осталось по P2: селектор типа в форме + группы CATEGORIES. | контракты: core `CardDavClient` +1 публичный метод (обратно совместимо); feature-слой изолирован | низкий: чтение из raw + подписи, миграций/схемы нет; на устройстве не гонялось (юниты + компил-гейт)
- 2026-07-13 | (без бампа — версия/релиз за лидом) | **Контакты — правка/удаление в UI (P1, contacts.diff, шаг 2/2).** Room: `ContactEntity` +`href`/`etag`/`raw`, миграция `MIGRATION_4_5` (v4→5, аддитивные ALTER ADD COLUMN), `ContactsDao.deleteByUid`. `ContactsRepository`: маппинг href/etag/raw, `updateContact` (round-trip vCard через `VCardEditor` + PUT If-Match) и `deleteContact` (DELETE If-Match + вычистка Room). `ContactItem` +3 поля. `ContactsViewModel`: `openEditSheet`/`closeEditSheet`/`saveEdit`/`deleteContact` + состояние (savingEdit/editError/deletingContact). UI: в карточке кнопки «Править» (форма имя/организация/должность/email/телефон/адрес/сайт/ДР) и «Удалить» (с подтверждением) — только для синхронизированных (есть href/raw). Блок действий вынесен в `ContactDetailActions`. Гейт `:app:compileReleaseKotlin` зелёный; `:feature:contacts:detekt`+compile зелёные (baseline `ContactDetailContent`/`parseContacts` пере-привязан под сдвинутые сигнатуры). NB: P1-edit нормализует TYPE у EMAIL/TEL к INTERNET/CELL (сохранение/правка типов — P2). На устройстве/сервере не гонялось (компил-гейт + юниты ядра); live-проверка на `bmob-*` (mail/085) — следующим шагом. | контракты: core `ContactEntity`+3 поля (Room v4→5 миграция), `CardDavClient` уже расширен в bc1a497; feature-слой изолирован | средний: миграция Room (общий core:database) — проверить апгрейд БД на устройстве; правка/удаление на живом CardDAV не гонялись
- 2026-07-13 | (без бампа — версия/релиз за лидом) | **Контакты — ядро правки/удаления (P1, contacts.diff, шаг 1/2).** `CardDavClient`: `updateContact` (PUT If-Match по ETag, round-trip неизвестных свойств vCard — PHOTO/UID/X-* сохраняются) и `deleteContact` (DELETE If-Match, 404=уже удалён, 412=конфликт ETag). `DavContact` +`href`/`etag`/`raw` (заполняются чтением книги; сигнатуры list/create НЕ менялись). `parseContacts` теперь ловит href/getetag на каждый `<response>`; общий `davContactFromVCard`. Чистый движок `VCardEditor.applyEdits` (замена управляемых FN/N/EMAIL/TEL/ORG/TITLE/ADR/URL/BDAY/NOTE, фолдинг 75, CRLF, экранирование) + модели `VCardFields`/`VCardTypedValue`. **10 юнит-тестов** (`VCardEditorTest`) зелёные: round-trip неизвестных полей, FN с запятыми, PHOTO base64, folded ADR, CRLF-фолдинг. Гейт `:app:compileReleaseKotlin` зелёный. Шаг 2/2 (Room href/etag/raw + миграция, форма правки в карточке, delete в UI) — следующим коммитом. | контракты: core `CardDavClient` +2 метода, `DavContact` +3 опц. поля (обратно совместимо, list/create не тронуты); одобрено лидом mail/072 | низкий: только добавление методов + поля с дефолтами, синк не задет; на устройстве/сервере ещё не гонялось (юниты + компил-гейт)
- 2026-07-13 | (без бампа — версия/релиз за лидом) | **Задачи — умные коллекции в UI (P1, tasks.diff).** Умные коллекции (Важное/Сегодня/Неделя/Все/Текущие/Завершённые) выведены в `TasksListNavView` над реальными списками: агрегат задач всех списков (`loadAllTasks`), счётчики-бейджи, тап открывает отфильтрованный вид; создание в коллекции идёт в список по умолчанию. 3-state видимость (Скрыта/Видима/Авто) в шите настроек с персистом в SharedPreferences (`TasksPrefsStore`, VM через `Factory(context)`). Движок `TasksSmartLists` расширен (`SmartListMode`/`visibleKeys`/`counts`), 25 юнит-тестов зелёные. Ff_app 16.7→24.6%, Fd_app 41.5→52.7% (`tasks.diff.md`). | контракты не менялись | низкий: UI изолирован в модуле, на устройстве не гонялось