# Diff: Уведомления (Notifications) — итерация 1 (2026-07-13) Ff_app: **45.5%** (done 10 / open 12, n-a 1) · Fd_app: **0.0%** (ждёт девайс-сверки) · автор `b-mob-notify` > Сверка веб (`nextcloud/notifications 5.0.0-dev.0` + тема forbion, mail/078) ↔ **натив > (реализован в app-shell)**. Лента УЖЕ работает: `NotificationsSheet` (`app/AppSheets.kt`) + > `F7NotificationRow` (`core/designsystem`) + `NotificationsRepository` (`core/network`, API v2). > `[done]` = есть в коде натива; `Fd=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); финальная приоритизация — за лидом.** Метки: `[Ф|Д]` (функционал/дизайн) · `[open|done|n-a]`. --- ## Экран 1. Триггер (колокольчик) — Ff n-a (зона лида) Колокольчик-вход живёт в шелле (нижняя шторка, слот `#mobile-header-notifications`) — **зона `b-dev-mobile`** (shell.md), не считаю в свой Ff. **Ф (1, n-a 1):** - [Ф][n-a] иконка-триггер + раскрытие ленты — шелл (лид). Индикатор непрочитанных (точка `showDot` / warning `hasThrottledPushNotifications`) — если понадобится, отдельным пингом лиду. ## Экран 2. Лента (дропдаун/шит) — Ff 57.1% · Fd 0% **Ф (8, done 4, open 3, 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** - [Ф][open] баннер fairUsePolicy при throttled push («Push notifications might be unreliable») — P3 - [Ф][open] авто-обновление ленты: сейчас только push-триггер (`refreshRequest`), ETag/поллинга (`lastETag`/notify_push) НЕТ — P2 - [Ф][n-a] пустой вариант «Запрос разрешений браузера» (`webNotificationsGranted===null`) — n-a: веб-концепт web-push; натив на FCM, разрешение POST_NOTIFICATIONS запрашивается системно **Д (5, open 5):** [Д][open] вёрстка ленты по теме/токенам · [Д][open] высота/скролл шита · [Д][open] пустое состояние (вид иконки/текста) · [Д][open] состояние загрузки/ошибки · [Д][open] анимация inbox-zero (галочка+конфетти при переходе >0→0) — P3 ## Экран 3. Элемент уведомления (`F7NotificationRow` ↔ `NotificationItem`) — Ff 50.0% · Fd 0% **Ф (12, done 6, open 6):** - [Ф][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** - [Ф][open] `subjectRich` (`NcRichText` + User/File-параметры) — только плоский subject — P2 - [Ф][open] `messageRich` (rich + autolink) — только плоский message — P2 - [Ф][open] `externalLink` (вариант «↗», target=_blank + primary-кнопка «Contact…») — не отличается от обычной ссылки — P3 - [Ф][open] сворачивание длинного message (`.collapsed` + разворот по тапу) — нет — P3 - [Ф][open] подсветка нового `notification--new` — нет — P3 **Д (4, open 4):** [Д][open] компоновка строки (время+× сверху, иконка+subject/message) · [Д][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 --- ## Вне карты-экрана: 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] 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/список живьём).