137 lines
14 KiB
Markdown
137 lines
14 KiB
Markdown
# Карта: Уведомления (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`) — СВЕРЕНО ЖИВЬЁМ у `cloude`
|
||
**Тело:** список `<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`.
|
||
|
||
**Живая сверка 2026-07-12 (лента `cloude` = 3 уведомления `firstrunwizard`, скриншот 390px):**
|
||
- `расхождение вер.:` кнопка **«Скрыть все уведомления»** на forbion (5.0.0-dev.0) показывается
|
||
ВСЕГДА, в т.ч. при пустой ленте — в master-коде она под `v-if="notifications.length > 0"`.
|
||
Нативный клиент должен ориентироваться на поведение forbion (кнопка всегда), не на master.
|
||
- `расхождение вер.:` время в элементе отображается **относительно** («45 минут назад»,
|
||
«49 минут назад»), а не в формате «время + длинная дата» из master `NcDateTime`.
|
||
- Порядок в строке подтверждён: верхняя строка = [относительное время] … [× «Скрыть»];
|
||
ниже слева иконка app, справа subject (жирнее) и, если есть, message (серым, второй строкой).
|
||
- Наблюдённые элементы (все `data-app=firstrunwizard`, `objectType` user/app): у всех есть
|
||
время + «Скрыть» + иконка + ссылка (`full-subject-link`, не внешняя), rich-subject НЕТ,
|
||
action-кнопок НЕТ; у двух из трёх есть `message`. Примеры (приветственные, не приватные):
|
||
«Добавьте данные в свой профиль…», «Рекомендованное приложение: Формы», «…: Распознавание».
|
||
- `notification--new` в этой выборке не встретился (уведомления не новее `lastOpenMaxId`) —
|
||
подсветку нового сверить, когда придёт свежее.
|
||
|
||
### Состояние 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).
|
||
|
||
## Открытые вопросы (остаток после живой сверки 2026-07-12)
|
||
Закрыто живьём: размещение строки, относительное время, «Скрыть все» всегда видна, состав
|
||
простого уведомления (firstrunwizard) — см. «Состояние A». Осталось (нужны уведомления
|
||
конкретных типов, которых у `cloude` сейчас нет):
|
||
- (?) `notification--new`: подсветка/полоса для СВЕЖЕГО уведомления (в выборке все были старее
|
||
`lastOpenMaxId`) — сверить, когда придёт новое.
|
||
- (?) Точный порядок и подписи **action-кнопок** для источников с действиями (Talk-приглашение
|
||
«Присоединиться/Отклонить», дедлайны Deck/Календаря) — у firstrunwizard действий нет.
|
||
- (?) Сворачивание длинного `message` на ширине 390px (высота `.message-container.collapsed`,
|
||
градиент `.notification-overflow`, разворот по тапу) — у наблюдённых message короткие.
|
||
- (?) Богатый `subjectRich` (`NcRichText` с User/File-параметрами) — у firstrunwizard subject
|
||
плоский; сверить на уведомлении с упоминанием пользователя/файла.
|