Files

11 KiB
Raw Permalink Blame History

Diff: Уведомления (Notifications) — итерация 1 (2026-07-13)

Ff_app: 63.6% (done 14 / open 8, n-a 1) · Fd_app: 20.0% (Д done 2 / open 8; Roborazzi-эталон снят, веб-сверка ленты pending) · автор 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) + F7NotificationRow (core/designsystem) + NotificationsRepository (core/network, API v2). [done] = есть в коде натива; Fd=0% — вёрстка на устройстве/визуально ещё не сверялась. Карта-источник: notifications.md (из кода + живая сверка forbion, Browser 1/cloude, read-only; лента cloude = 3 уведомления firstrunwizard).

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].


Экран 1. Триггер (колокольчик) — Ff n-a (зона лида)

Колокольчик-вход живёт в шелле (нижняя шторка, слот #mobile-header-notifications) — зона b-dev-mobile (shell.md), не считаю в свой Ff. Ф (1, n-a 1):

  • [Ф][n-a] иконка-триггер + раскрытие ленты — шелл (лид). Индикатор непрочитанных (точка showDot / warning hasThrottledPushNotifications) — если понадобится, отдельным пингом лиду.

Экран 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)
  • [Ф][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
  • [Ф][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. Элемент уведомления (F7NotificationRowNotificationItem) — Ff 58.3% · Fd 0%

Ф (12, done 7, open 5):

  • [Ф][done] относительное время (formatNotificationRelativeTime) — совпадает с forbion («N минут назад»)
  • [Ф][done] кнопка «Скрыть» (× → dismissNotificationDELETE …/notifications/{id})
  • [Ф][done] иконка app (resolveNotificationIcon + AsyncImage с auth-хедером)
  • [Ф][done] subject (плоский текст)
  • [Ф][done] message (плоский текст, если есть)
  • [Ф][done] переход по тапу (onNotificationClickopenAppLink(notification.link))
  • [Ф][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…») — не отличается от обычной ссылки — P3
  • [Ф][open] сворачивание длинного message (.collapsed + разворот по тапу) — нет — P3
  • [Ф][open] подсветка нового notification--new — нет — P3

Д (5, done 2, open 3): нативный Roborazzi-эталон снят (core/designsystem/src/testnotification-row-states.png, w360dp, перезаписан 8072539 после фикса лида).

Веб-эталон 360 CSS-px — техническое ограничение: лента уведомлений НЕ рендерится в same-origin iframe (приложение notifications координирует вкладки — setCurrentTabAsActive, фетчит только «активная» вкладка; iframe как вторая вкладка остаётся пустым, хотя OCS API отдаёт 3 уведомления firstrunwizard). Populated-строки живьём для попиксельной сверки пока недоступны через iframe. Обходы на изучении; часть Д закрыта по Roborazzi-эталону + карте.

  • [Д][done] перенос текста primary-кнопки посреди слова — было «Присоединитьс/я» на w360dp; лид bfa870c (подпись действия 1.5× вместо 2×, maxLines=2 по словам, паддинги 16/9). Верифицировано перезаписью Roborazzi: «Присоединиться» в одну строку. Находка Roborazzi.
  • [Д][done] зазор subject↔message (сливались на 360dp) — лид bfa870c (6→10dp), подтверждено на PNG.
  • [Д][open] компоновка строки (время+× сверху, иконка+subject/message) — сверить с веб-эталоном.
  • [Д][open] вёрстка по теме (карточный стиль _mobile-common.css + токены) — vs веб-эталон.
  • [Д][open] типографика: натив рендерит удвоенным кеглем (.doubled()) — сверить с мобильной темой.

Экран 4. Кнопка действия (F7NotificationActionUiActionButton) — 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). Клиент 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 = 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б.