app-map: карта Уведомлений (лента-дропдаун) — код апстрима + живой мобильный web forbion, read-only
This commit is contained in:
@@ -0,0 +1,115 @@
|
|||||||
|
# Карта: Уведомления (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`).
|
||||||
Reference in New Issue
Block a user