From eed77232307ae6dfbad1ed13d3909807a346f430 Mon Sep 17 00:00:00 2001 From: b-mob-notify Date: Mon, 13 Jul 2026 10:22:13 +0000 Subject: [PATCH] =?UTF-8?q?docs(app-map):=20notifications=20P1=20=D0=97?= =?UTF-8?q?=D0=90=D0=9A=D0=A0=D0=AB=D0=A2=20(=D0=BB=D0=B5=D0=BD=D1=82?= =?UTF-8?q?=D0=B0=20a30627a=20+=20unregister=20cc7ad71/255c962=20+=20retry?= =?UTF-8?q?=20a6bda89),=20Ff=2045.5=E2=86=9263.6%;=20Fd=20=D0=BD=D0=B0=20R?= =?UTF-8?q?oborazzi?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/app-map/STATUS.md | 2 +- docs/app-map/notifications.diff.md | 70 +++++++++++++++++------------- 2 files changed, 40 insertions(+), 32 deletions(-) diff --git a/docs/app-map/STATUS.md b/docs/app-map/STATUS.md index 869bc2f..7e568ba 100644 --- a/docs/app-map/STATUS.md +++ b/docs/app-map/STATUS.md @@ -16,7 +16,7 @@ | talk | b-mob-conf | 1 | 41.3 | предв. | ~29 | ? | нет | P1 меню сообщения ~закрыто (Копировать/ссылка/2×переслать/Напоминание/Ответить-лично, …01198a4); остаток P1 файл-тред+перевод эскалирую (кросс-модуль/провайдеры); далее P2 меню беседы; Fd на устройстве | | tasks | b-mob-tasks | 1 | 24.6 | 52.7 | ~34 | ~8 | нет | P1 закрыт в коде (умные коллекции+3-state видимость+персист, `bd56c4b`); далее P2 управление списками; ждёт девайс-сверки | | notes | b-mob-notes | 1 | 42.8 | 0.0 | 26 | 21 | нет | P1 read-only ядро в коде+UI (`8a8f1cf` данные + `fb120d5` VM/список/редактор/конфликт); compile+7/7 тестов+detekt OK (worktree); Fd ждёт девайс. Пинг лиду на регистрацию экрана в app-шелле | -| notifications | b-mob-notify | 1 | 45.5 | 0.0 | 12 | 9 | нет | diff выложен: лента РАБОТАЕТ в app-shell (список/пусто/скрыть-одно/переход/иконка/время); P1-ленту (Скрыть все + action-кнопки) берёт лид (109); мой push-трек: retry регистрации DONE (`a6bda89`) + unregister клиент DONE (`cc7ad71`, контракт f7push подтверждён mail/110) — осталась 1 строка вызова в logout, патч лиду (mail/112); Fd: Roborazzi автономен (111), но нужен `feature/notifications` — просьба лиду (112) | +| notifications | b-mob-notify | 1 | 63.6 | 0.0 | 8 | 9 | нет | **P1 ЗАКРЫТ:** лента action-кнопки+«Скрыть все» (лид `a30627a`), unregister-push (клиент `cc7ad71`+вызов `255c962`), retry (`a6bda89`). Осталось Ф: rich subject/message (P2), ETag/fairUse/WEB-action/сворачивание/new (P3). **Fd в работе автономно:** Roborazzi-строка в `core/designsystem/src/test` (w360dp) + веб-эталон 360px (112б); `feature/notifications` после гейта | | deck | b-mob-deck | 1 | 25.3 | предв. 2/8 | 47 | ~4 | нет | Ff P1 done (назначить-себя/бейджи/меню колонки/быстрые сроки, 6eeba1b…eff3f1c). Fd: Roborazzi 360dp поднят (58f11c2, native PNG), сверено 2 экрана (Д: бейджи иконки-vs-эмодзи, аватар исполнителя, колонка Участники); далее 6 экранов+меню. Крупный Ff (назначение юзера/Комментарии/Вложения/обзор/CRUD) — жду приоритеты лида | | calendar | b-mob-calendar | 1 | 64.0 | 80.4 | ~35 | ~7 | нет | P1 разворот повторений+BYDAY=1MO ЗАКРЫТ в коде (`CalendarRecurrence`, 15 тестов, compile+detekt green, коммиты e7d0a5a/492af61); Appointments=n-a (101); далее P2 EXDATE/RECURRENCE-ID | | contacts | b-mob-contacts | 1 | 37.4 | предв. | ~32 | ? | нет | P1 edit/delete в main (bc1a497+6b98c5a); P2 шаг1 типы TEL/EMAIL в main (a8741e9: парс+подписи+сохранение, 14 тестов); далее P2 шаг2 селектор типа + группы CATEGORIES; live/device далее | diff --git a/docs/app-map/notifications.diff.md b/docs/app-map/notifications.diff.md index 5845ef0..ad64d5c 100644 --- a/docs/app-map/notifications.diff.md +++ b/docs/app-map/notifications.diff.md @@ -1,6 +1,9 @@ # Diff: Уведомления (Notifications) — итерация 1 (2026-07-13) -Ff_app: **45.5%** (done 10 / open 12, n-a 1) · Fd_app: **0.0%** (ждёт девайс-сверки) · автор `b-mob-notify` +Ff_app: **63.6%** (done 14 / open 8, n-a 1) · Fd_app: **0.0%** (Roborazzi-сверка в работе) · автор `b-mob-notify` + +> **Итерация 1b (2026-07-13):** P1-лента закрыта лидом (`a30627a`) + P1-push unregister закрыт +> (клиент `cc7ad71` + вызов в logout `255c962`). Ff 45.5% → 63.6%. > Сверка веб (`nextcloud/notifications 5.0.0-dev.0` + тема forbion, mail/078) ↔ **натив > (реализован в app-shell)**. Лента УЖЕ работает: `NotificationsSheet` (`app/AppSheets.kt`) + @@ -9,11 +12,10 @@ Ff_app: **45.5%** (done 10 / open 12, n-a 1) · Fd_app: **0.0%** (ждёт де > Карта-источник: [`notifications.md`](notifications.md) (из кода + живая сверка forbion, > Browser 1/`cloude`, read-only; лента `cloude` = 3 уведомления `firstrunwizard`). > -> **NB зона:** лента живёт в `app/AppSheets.kt` (app-shell, зона лида). Два P1-расхождения -> (Скрыть-все, action-кнопки) требуют правок в app/ → **прошу лида завести `feature/notifications` -> и перенести `NotificationsSheet`** (предложено в mail/107), тогда закрываю автономно. -> -> **Приоритеты предлагаю (P1→P3); финальная приоритизация — за лидом.** +> **NB зона:** лента в `app/AppSheets.kt` + `core/*` (зона лида) — P1-функционал он закрыл сам +> (mail/113). Моё: core/push (retry+unregister — done) и **Fd (Roborazzi-строка уведомления в +> `core/designsystem/src/test` + веб-эталон 360 CSS-px)** — по разрешению лида (mail/112б). +> `feature/notifications` — после приёмочного гейта (mail/112б). Метки: `[Ф|Д]` (функционал/дизайн) · `[open|done|n-a]`. @@ -26,14 +28,14 @@ Ff_app: **45.5%** (done 10 / open 12, n-a 1) · Fd_app: **0.0%** (ждёт де - [Ф][n-a] иконка-триггер + раскрытие ленты — шелл (лид). Индикатор непрочитанных (точка `showDot` / warning `hasThrottledPushNotifications`) — если понадобится, отдельным пингом лиду. -## Экран 2. Лента (дропдаун/шит) — Ff 57.1% · Fd 0% -**Ф (8, done 4, open 3, n-a 1):** +## Экран 2. Лента (дропдаун/шит) — Ff 71.4% · Fd 0% +**Ф (8, done 5, open 2, n-a 1):** - [Ф][done] загрузка списка (`NotificationsRepository.load`, OCS API v2, `limit=50`) - [Ф][done] пустое состояние «Нет уведомлений» (иконка `nof-not-icon.svg` + текст) — `items.isEmpty()` - [Ф][done] состояние загрузки (спиннер `CircularProgressIndicator`) - [Ф][done] состояние ошибки (текст `error`) -- [Ф][open] **«Скрыть все уведомления»** (dismiss-all → `DELETE …/api/v2/notifications`) — НЕТ - в нативе; на forbion кнопка видна ВСЕГДА (даже при пустой ленте, см. карту) — **P1** +- [Ф][done] **«Скрыть все уведомления»** (`dismissAll()` → `DELETE …/api/v2/notifications`) — + лид `a30627a` (заголовок ленты + кнопка + оптимистичное скрытие) - [Ф][open] баннер fairUsePolicy при throttled push («Push notifications might be unreliable») — P3 - [Ф][open] авто-обновление ленты: сейчас только push-триггер (`refreshRequest`), ETag/поллинга (`lastETag`/notify_push) НЕТ — P2 @@ -44,16 +46,16 @@ Ff_app: **45.5%** (done 10 / open 12, n-a 1) · Fd_app: **0.0%** (ждёт де [Д][open] пустое состояние (вид иконки/текста) · [Д][open] состояние загрузки/ошибки · [Д][open] анимация inbox-zero (галочка+конфетти при переходе >0→0) — P3 -## Экран 3. Элемент уведомления (`F7NotificationRow` ↔ `NotificationItem`) — Ff 50.0% · Fd 0% -**Ф (12, done 6, open 6):** +## Экран 3. Элемент уведомления (`F7NotificationRow` ↔ `NotificationItem`) — Ff 58.3% · Fd 0% +**Ф (12, done 7, open 5):** - [Ф][done] относительное время (`formatNotificationRelativeTime`) — совпадает с forbion («N минут назад») - [Ф][done] кнопка «Скрыть» (× → `dismissNotification` → `DELETE …/notifications/{id}`) - [Ф][done] иконка app (`resolveNotificationIcon` + `AsyncImage` с auth-хедером) - [Ф][done] subject (плоский текст) - [Ф][done] message (плоский текст, если есть) - [Ф][done] переход по тапу (`onNotificationClick` → `openAppLink(notification.link)`) -- [Ф][open] **action-кнопки уведомления** (`notification.actions[]` → `ActionButton`) — НЕТ в - нативе; ломает сценарии с действиями (Talk-приглашение «Присоединиться/Отклонить», и т.п.) — **P1** +- [Ф][done] **action-кнопки уведомления** (`actions[]` → `F7NotificationActionUi`, FlowRow) — + лид `a30627a` (primary=заливка, прочие=контур); закрывает Talk-приглашения и т.п. - [Ф][open] `subjectRich` (`NcRichText` + User/File-параметры) — только плоский subject — P2 - [Ф][open] `messageRich` (rich + autolink) — только плоский message — P2 - [Ф][open] `externalLink` (вариант «↗», target=_blank + primary-кнопка «Contact…») — не отличается @@ -65,32 +67,38 @@ Ff_app: **45.5%** (done 10 / open 12, n-a 1) · Fd_app: **0.0%** (ждёт де [Д][open] вёрстка по теме (карточный стиль `_mobile-common.css` + токены) · [Д][open] иконки/аватар · [Д][open] типографика subject(SemiBold)/message/время -## Экран 4. Кнопка действия (`ActionButton`) — Ff 0.0% · Fd 0% — ОТСУТСТВУЕТ В НАТИВЕ -Модель `action{label,link,type,primary}`; в нативной `F7Notification` поля `actions` вообще НЕТ -(`NotificationsRepository` их не парсит). -**Ф (3, open 3):** -- [Ф][open] primary/secondary по типу (`WEB`/`primary` → primary; GET/POST/DELETE → secondary) — P1 -- [Ф][open] выполнение действия (`method:action.type url:action.link`) + удаление из ленты — P1 -- [Ф][open] WEB-действие как прямая ссылка (`href`) — P1 +## Экран 4. Кнопка действия (`F7NotificationActionUi` ↔ `ActionButton`) — Ff 66.7% · Fd 0% +Реализовано лидом (`a30627a`): парсинг `actions[]` (`parseActions`, type→uppercase, default GET, +primary), рендер `F7NotificationActionUi`, выполнение `executeAction`. +**Ф (3, done 2, open 1):** +- [Ф][done] primary/контур по флагу `primary` (`F7NotificationActionUi.primary`) — рендер FlowRow. + (нюанс упстрима: там `isWebLink||primary`→primary; в нативе только флаг `primary` — визуально + сойдётся, разберём в Fd, не Ф-дыра) +- [Ф][done] выполнение действия (`executeAction`: POST/PUT/DELETE, иначе GET, по абсолютному + `action.link` + `OCS-APIRequest`) + оптимистичное скрытие уведомления после +- [Ф][open] **WEB-действие как прямая ссылка/открытие в браузере** — `executeAction` для типа + `WEB` уходит в `else→GET` (упстрим: WEB = открыть `link` в браузере, а не HTTP-GET). Пограничный + случай, но семантика иная — P3. (сообщу лиду, если встретится WEB-action живьём) --- ## Вне карты-экрана: core/push (моя зона, отдельный бэклог) Не входит в Ff ленты (нет веб-экрана-аналога), но моя зона и уже зафиксировано в представлении (mail/070). Держу как отдельный трек: -- [Ф][~done] **unregister при logout** — контракт f7push подтверждён (mail/110: `DELETE - …/devices/{deviceId}`, идемпотентно). **Клиент готов:** `F7PushRegistrar.unregisterBlocking` - (коммит `cc7ad71`, compile+detekt green). Осталась 1 строка вызова в `MainViewModel.logout()` - (app/, зона лида) — патч отправлен лиду (mail/112). Как впаяет — [done] (P1-push). +- [Ф][done] **unregister при logout** — ЗАКРЫТО. Контракт f7push (mail/110). Клиент + `F7PushRegistrar.unregisterBlocking` (`cc7ad71`, compile+detekt green) + вызов в + `MainViewModel.logout()` best-effort на IO рядом с ревокацией (лид `255c962`, mail/112а). P1-push. - [Ф][done] retry/бэкофф в `F7PushRegistrar.registerBlocking` (3 попытки, бэкофф 1с/3с, повтор на 0/5xx/408/429, стоп на 2xx/4xx) — коммит `a6bda89`, compile+detekt green (P2-push) - [Ф][n-a] серверные хвосты FCM (чистка 403 SenderId mismatch, старые `f7cloud-apk`) — ячейка A, через integrator-b (mail/064) — вне натива ## Сводка -- **Ff = 45.5%** (done 10 / open 12; n-a 3 не в счёте: web-permission-empty, триггер-шелл, серверный FCM). -- **P1 (гейт-приоритет):** «Скрыть все» + action-кнопки (+парсинг `actions` в repo) + unregister-push. -- **P2:** rich subject/message, авто-обновление ETag, retry регистрации. -- **P3:** fairUsePolicy, external-«↗», сворачивание message, `notification--new`, inbox-zero. -- **Fd=0%** до девайс-сверки (вёрстка по теме forbion; лента у `cloude` наполняется — mail/107, - как засеют `occ notification:generate`, сверю rich/action/список живьём). +- **Ff = 63.6%** (done 14 / open 8; n-a 3 не в счёте: web-permission-empty, триггер-шелл, серверный FCM). +- **P1 — ЗАКРЫТ:** «Скрыть все» + action-кнопки + парсинг `actions` (лид `a30627a`); + unregister-push (клиент `cc7ad71` + вызов `255c962`); retry регистрации (`a6bda89`). +- **P2 (осталось):** rich subject/message, авто-обновление ETag. +- **P3 (осталось):** fairUsePolicy-баннер, external-«↗», WEB-action как ссылка, сворачивание + message, `notification--new`, inbox-zero. +- **Fd=0% → в работе автономно:** Roborazzi-скрин `F7NotificationRow` в `core/designsystem/src/test` + (w360dp) + веб-эталон 360 CSS-px (пустое состояние, без данных `cloude`) — по mail/111/112б.