Files
f7cloud_mobile/docs/app-map/notifications.md
T

116 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Карта: Уведомления (forbion, мобильная веб-версия)
Снято 2026-07-12. Метод по mail/078: структура берётся ИЗ КОДА
(апстрим `nextcloud/notifications`, компоненты `NotificationsApp.vue` / `NotificationItem.vue` /
`ActionButton.vue` / `IconNotification.vue`; на forbion версия 5.0.0-dev.0 — компоненты стабильны
между версиями), живой мобильный web forbion (вьюпорт 390px, аккаунт `cloude`, строго read-only
по mail/073) — для визуальной сверки размещения и порядка. Живой аккаунт `cloude` на момент съёмки
имел **пустую ленту** (empty-state), поэтому структура элемента уведомления и действий снята с кода,
а не с экрана. Пишущих кнопок («Скрыть», «Скрыть все», action-кнопки) НЕ нажимал — dismiss и
выполнение действий это записи (DELETE / произвольный HTTP-метод), а аккаунт реальный рабочий.
Уведомления — НЕ отдельное приложение (нет пункта в шторке приложений и своего URL-роута), а
глобальный **дропдаун-лента в шапке** (компонент `NotificationsApp`, монтируется в `#notifications`).
## Вход
В мобильной вёрстке forbion иконка-триггер (колокольчик) живёт в **шторке приложений**, а не на
плавающей нижней панели. Путь:
- Нижняя панель шелла → `≡` («три полоски», `#open-header-menu-mobile`) открывает шторку
`app-menu--mobile`.
- В шторке — слот `#mobile-header-notifications` (класс `f7-mobile-menu-control-slot`,
`mobile-bottom-slot--overlay`) с кнопкой-колокольчиком (`.notifications-button`,
`aria-label="Уведомления"`, `aria-expanded`).
- Нижняя плавающая панель (общая, зона лида, для ориентира): `<`/`>` (сайдбар-навигация,
`#mobile-header-left-sidebar-button`) · конверт → Почта (`/apps/mail/`) · «карточки» → Задачи
(`/apps/tasks/`) · камера → Конференции (`/apps/spreed/`) · `≡` → шторка приложений.
## Триггер (колокольчик) — `IconNotification`
**Вид:** иконка «колокол». Индикатор состояния (`showDot`):
- точка-бейдж показывается, когда `notifications.length !== 0` **или** когда браузерные разрешения
ещё не запрошены (`webNotificationsGranted === null`);
- значок-предупреждение (`showWarning`) — когда push-уведомления задросселированы политикой
честного использования (`hasThrottledPushNotifications`).
**Действие:** тап → раскрывает дропдаун-ленту (`aria-expanded` false→true), эмит `@opened → onOpen`
(подгрузка/пометка ленты активной). Повторный тап или клик вне (кроме `.popover`) — закрывает.
## Экран: Лента уведомлений (дропдаун `NcHeaderMenu`, класс `header-menu`)
Контейнер `.notification-container` внутри `.header-menu__content`. Тайтл шапки в мобильной
теме — «Уведомления» (`.tooltip-header--notifications`). Два взаимоисключающих состояния:
### Состояние A — есть уведомления (`notifications.length > 0`)
**Тело:** список `<ul class="notification-wrapper">` из элементов `NotificationItem` (см. ниже),
сверху вниз, новые (`notificationId > lastOpenMaxId`) помечены классом `notification--new`.
Если push задросселирован — первым нештатным элементом идёт баннер «Push notifications might be
unreliable» (fairUsePolicy, `key=-2016`).
**Низ:** блок `.dismiss-all` — кнопка **«Скрыть все уведомления»** (tertiary, во всю ширину,
иконка «×»). ⚠️ Пишущая: `onDismissAll``DELETE /ocs/v2.php/apps/notifications/api/v2/notifications`.
### Состояние B — пусто (empty-state `NcEmptyContent`) — наблюдалось у `cloude`
Три варианта текста (computed `emptyContentMessage`):
- `webNotificationsGranted === null`**«Запрос разрешений для показа уведомлений в браузере»**
(именно это видел у `cloude` — иконка колокола `bell-outline-icon`);
- задросселированы push → заголовок = subject fairUsePolicy + описание + кнопка «Contact
Nextcloud GmbH ↗» (внешняя ссылка), иконка `icon-alert-outline`;
- иначе → **«Нет уведомлений»** (`No notifications`). При переходе списка из >0 в 0 на ~5 сек
показывается анимация inbox-zero (галочка + конфетти), затем обычный колокол.
## Элемент: `NotificationItem` (`<li class="notification">`, из кода)
data-атрибуты: `data-id`, `data-timestamp`, `data-object-type`, `data-app`.
**Шапка элемента (`.notification-heading`):**
- время (`NcDateTime`, формат «время + длинная дата», `.notification-time`) — если есть timestamp;
- кнопка **«Скрыть»** (`.notification-dismiss-button`, tertiary, иконка «×», `aria-label="Dismiss"`).
⚠️ Пишущая: `onDismissNotification``DELETE …/api/v2/notifications/{id}`, затем `@remove` (убрать из ленты).
**Субъект (заголовок), три варианта рендера:**
- если `externalLink``<a target=_blank>` класс `full-subject-link external`, текст «{subject} ↗»;
- иначе если есть `link` (useLink) → `<a>` на этот линк;
- иначе — просто блок `.notification-subject` без ссылки.
Внутри: опциональная иконка (`img.notification-icon`); если есть `subjectRich` — богатый рендер
`NcRichText` с параметрами (см. Parameters ниже), иначе простой `.subject` с текстом subject.
**Сообщение (`.notification-message`, если есть `message`):** богатый (`messageRich` + autolink)
или простой текст; длинное сворачивается (`.message-container.collapsed` + градиент
`.notification-overflow`), тап по сообщению — разворот (`onClickMessage`).
**Действия (`.notification-actions`):**
- если `notification.actions[]` непусто — ряд `ActionButton` по каждому action;
- иначе, если есть `externalLink` — одна primary-кнопка «Contact Nextcloud GmbH ↗».
## Элемент: `ActionButton` (кнопка действия уведомления)
Поля action (из API): `label`, `link`, `type`, `primary`.
- `type === 'WEB'` (isWebLink) или `primary` → кнопка **primary**; для WEB это прямой `href` (ссылка).
- прочие типы (GET/POST/DELETE/PUT) → кнопка **secondary**.
**Клик (`onClickAction`):** эмит `notifications:action:execute` (приложение может отменить,
`cancelAction`); для WEB — просто переход по ссылке; иначе ⚠️ пишущий вызов
`axios({ method: action.type, url: action.link })`, при успехе — `@remove` (уходит из ленты) +
эмит `notifications:action:executed`. Ошибка → тост «Failed to perform action».
Пример типового уведомления с действиями — приглашение в Talk-беседу: subject + действия
«Присоединиться»/«Отклонить» и т.п. (состав задаёт серверное приложение-источник).
## Параметры rich-subject/message (`Components/Parameters/`)
`NcRichText` подставляет типизированные плейсхолдеры:
- `UserParameter` — пользователь (аватар + имя, кликабельно);
- `FileParameter` — файл (иконка + имя, ссылка на файл);
- `DefaultParameter` — прочие (просто текст).
`расхождение:` в нативном клиенте (`NotificationsRepository`/`NotificationsSheet`) сейчас берётся
только плоский `subject`/`message` (API v2), rich-параметры и иконка app не разворачиваются —
это мой бэклог (см. представление mail/070).
## API (из кода, все через OCS `apps/notifications/api/v2/notifications`)
- `GET …/notifications` — лента (в нативе уже есть, `NotificationsRepository.load`, limit=50);
- `DELETE …/notifications/{id}` — скрыть одно (в нативе есть `dismiss`);
- `DELETE …/notifications` — скрыть все (в нативе НЕТ — бэклог «отметить/скрыть все»);
- `{action.type} {action.link}` — выполнить действие уведомления (в нативе действий НЕТ — бэклог).
Обновление ленты — по ETag (`lastETag`) и, если доступен, через notify_push (`@nextcloud/notify_push`).
## Стили (тема forbion)
Отдельного `_mobile-notifications.css` в теме нет: на вебе лента — дроп в шапке
(`css/components/_header.css`), мобильное размещение колокольчика — слот
`#mobile-header-notifications` в `header__mobile-bottom`/шторке (тема
`css/pages/pages-mobile/_mobile-common.css`). Для нативной ленты держим общий карточный стиль
модулей (`_mobile-common.css` + токены designsystem) — согласовано с лидом (mail/070).
## Открытые вопросы / сверить, когда лента будет непустой
- (?) Как именно выглядит `notification--new` в мобильной теме (подсветка/полоса) — на пустом
аккаунте не увидеть; сверить на аккаунте с уведомлениями.
- (?) Точный порядок и подписи action-кнопок для реальных источников (Talk-приглашение, дедлайны
Deck/Календаря) — снять живьём, когда появятся уведомления.
- (?) Сворачивание длинного message в мобильной ширине 390px (высота `.message-container`).