Модальное окно предпросмотра темы

:information_source: Сводка Модальное окно предпросмотра тем – открывайте и взаимодействуйте с темами, не покидая список тем
:eyeglasses: Предпросмотр Theme Creator
:hammer_and_wrench: Репозиторий GitHub - VaperinaDEV/discourse-topic-preview-modal: Open a topic directly from the topic list in a native Discourse modal, read and interact with the topic, and then continue browsing the list without navigating away from it. · GitHub
:heart: Пригодилось? > ./support --coffee
:question: Гид по установке Как установить тему или компонент темы
:open_book: Новичок в темах Discourse? Гид для начинающих по использованию тем Discourse

Установить этот компонент темы

Topic Preview Modal – открывайте и взаимодействуйте с темами, не покидая список тем

Я создал новый компонент темы Discourse под названием Topic Preview Modal.

Идея довольно проста:

Открывайте тему напрямую из списка тем в нативном модальном окне Discourse, читайте и взаимодействуйте с темой, а затем продолжайте просмотр списка, не покидая его.

Все началось с Facebook-style Topic Modal - Is it better? , но в итоге потребовалась значительная интеграция с системами тем, потока сообщений, композера, модальных окон, закладок, маршрутизации, присутствия, отслеживания прочтения и предварительной загрузки в Discourse.


Зачем?

Обычный поток работы в Discourse выглядит так:

  1. Вы просматриваете список тем.
  2. Вы кликаете по теме.
  3. Discourse переходит по адресу /t/....
  4. Вы читаете/отвечаете/взаимодействуете с темой.
  5. Вы возвращаетесь в список тем.

Для многих сценариев этого вполне достаточно.

Однако, просматривая загруженный список тем, иногда я хочу быстро просмотреть тему, прочитать несколько сообщений, проверить последние ответы, отреагировать на что-то или ответить на быстрый вопрос.

В таком случае покидать список тем кажется неоправданно затратным.

Целью этого компонента было сделать так, чтобы список тем работал больше как входящие:

список тем → предпросмотр → взаимодействие → закрытие → продолжение ровно с того места, где вы остановились.


Что он делает

Предпросмотр – это не просто статичный фрагмент.

Он рендерит фактические компоненты сообщений Discourse внутри нативного DModal.

Это означает, что пользователи могут:

  • читать сообщения
  • прокручивать тему
  • загружать более ранние сообщения
  • загружать новые сообщения ниже
  • реагировать на сообщения
  • добавлять сообщения в закладки
  • цитировать текст
  • отвечать на тему
  • отвечать на отдельные сообщения
  • редактировать сообщения, если это разрешено
  • удалять/восстанавливать сообщения, если это разрешено
  • жаловаться на сообщения
  • просматривать историю сообщений
  • выполнять различные стандартные действия с сообщениями
  • видеть присутствие в теме
  • переходить по ссылкам на другие сообщения в рамках той же темы
  • переходить непосредственно к нужному сообщению
  • открывать полную тему, если это необходимо

Намерение состоит в том, чтобы предпросмотр ощущался как можно ближе к реальному открытию темы.


Два режима запуска

Есть два способа открыть предпросмотр.

1. Вся строка списка тем

Это значение по умолчанию.

Вся строка списка тем становится кликабельной, при этом общие интерактивные элементы, такие как:

  • карточки пользователей
  • участники
  • ссылки на категории
  • теги
  • ссылки на статус темы
  • массовый выбор

исключаются из триггера модального окна.

Это делает опыт очень быстрым при просмотре списка тем.

2. Явная кнопка развертывания

В качестве альтернативы, компонент может рендерить маленькую иконку развертывания через плагинный отсек (plugin outlet) Discourse. Кастомные темы могут просто создать новый <PluginOutlet />, чтобы показать триггер.

В этом режиме стандартное поведение списка тем остается полностью нетронутым.

Пользователь кликает по иконке развертывания, чтобы открыть предпросмотр, в то время как клик по заголовку темы по-прежнему выполняет стандартную навигацию Discourse.

Это полезно, если сайт хочет сохранить стандартную модель взаимодействия со списком тем.

Настройка:

trigger_style:
  row

или:

trigger_style:
  button

При использовании режима кнопки отсек (outlet) также настраивается.


Предпросмотр начинается с позиции непрочитанного пользователя

Одна из важных деталей заключается в том, что модальное окно не просто загружает первое сообщение.

Если тема уже была частично прочитана, предпросмотр вычисляет:

last_read_post_number + 1

и открывается вокруг этого сообщения.

Таким образом, если в теме 200 сообщений, а пользователь прочитал до #165, открытие предпросмотра начнется примерно с #166.

Это делает предпросмотр гораздо более полезным для реального просмотра.

Это также означает, что компонент должен иметь дело с обеими сторонами потока сообщений:

  • загрузка более ранних сообщений при необходимости
  • загрузка новых сообщений ниже

Кнопка Ранние сообщения отображается, если есть сообщения выше текущего загруженного диапазона, в то время как сентинел IntersectionObserver автоматически загружает новые сообщения, когда пользователь достигает конца.


Предварительная загрузка (Prefetching)

Одна из самых больших частей компонента – это система предварительной загрузки.

Проблема с модальным окном такого типа в том, что пользователь ожидает мгновенного отклика.

Если мы начнем загрузку темы только после клика пользователя, модальное окно все равно может потратить заметное время на ожидание сети.

Вместо этого компонент может проактивно предварительно загружать темы, пока пользователь просматривает список.

Когда строка темы приближается к области просмотра, IntersectionObserver может запланировать предварительную загрузку.

Существует несколько механизмов защиты, чтобы это не превратилось в неконтролируемый фоновый трафик.

Дебаунсинг (Debouncing)

Тема не запускает запрос немедленно просто потому, что она на мгновение появилась в области просмотра.

Компонент ждет в течение настроенного периода дебаунсинга.

По умолчанию:

400 мс

Это особенно полезно при быстром прокручивании длинного списка тем.

Поле корня (Root margin)

Предварительная загрузка может начаться немного раньше, чем тема фактически войдет в область просмотра.

По умолчанию:

50 px

Это дает запросу небольшое преимущество в старте.

Ограничение одновременных запросов

Количество одновременных предварительных загрузок ограничено.

По умолчанию:

2

Настройка позволяет от 1 до 6 одновременных предварительных загрузок.

Бюджет на минуту

Также существует второй механизм защиты:

max_prefetches_per_minute

По умолчанию:

15

Таким образом, даже если пользователь продолжает прокручивать сотни тем, компонент не будет непрерывно генерировать спекулятивные запросы.

0 отключает лимит.

Предварительная загрузка может быть полностью отключена

Если сайт не хочет никакого спекулятивного сетевого трафика:

enable_prefetch = false

Компонент продолжает работать нормально. Темы просто загружаются, когда открывается предпросмотр.


Данные предварительной загрузки хранятся отдельно от обычной навигации по темам

Здесь есть важная деталь реализации.

Ответ предварительной загрузки не записывается немедленно в обычный ключ предзагрузки topic_<id> Discourse.

Вместо этого компонент использует собственное пространство имен:

topic-preview-modal:prefetch:<topicId>

Только когда пользователь фактически открывает предпросмотр, промис предварительной загрузки повышается до основного ключа предзагрузки темы.

Это сделано намеренно.

Предпросмотр может загружать тему, начиная с last_read_post_number + 1, и я не хочу, чтобы этот специфичный для предпросмотра ответ «протекал» в обычную навигацию по маршруту темы.

Таким образом, жизненный цикл выглядит примерно так:

тема входит в область просмотра
        ↓
предварительная загрузка
        ↓
приватное хранилище предзагрузки
        ↓
пользователь открывает предпросмотр
        ↓
повышение предзагрузки
        ↓
Topic.find()/PostStream используют тот же промис

Это также означает, что модальное окно не должно ждать завершения запроса предварительной загрузки перед открытием.

Модальное окно может открыться немедленно со своим скелетным интерфейсом, пока тот же промис продолжает разрешаться.


Поддержка мобильных устройств

На самом деле, это была одна из причин, по которой я потратил значительно больше времени на реализацию.

Первоначальная идея работала приемлемо на десктопе, но мобильные устройства выявили несколько проблем, связанных с:

  • сенсорным взаимодействием
  • прокруткой модального окна
  • фокусом
  • вложенными меню
  • композером
  • видимостью сообщений
  • загрузкой изображений
  • производительностью

Поэтому в итоговой реализации модальное окно не рассматривается как полностью отдельный миниатюрный форум.

Вместо этого оно переиспользует как можно больше существующей инфраструктуры Discourse.


Реальные компоненты сообщений Discourse

Модальное окно не пересоздает сообщения, используя упрощенный кастомный шаблон.

Оно рендерит фактические компоненты:

Post
PostSmallAction

Discourse.

Это важно, потому что иначе предпросмотр быстро превратился бы во вторую реализацию пользовательского интерфейса сообщений.

Компонент передает соответствующие действия в обычные компоненты сообщений, включая такие вещи, как:

  • ответ
  • редактирование
  • удаление
  • восстановление
  • жалоба
  • история
  • закладка
  • вики
  • блокировка/разблокировка
  • тип сообщения
  • изменения владения
  • значки
  • скрытые сообщения
  • цитирование
  • и т.д.

В результате предпросмотр может вести себя гораздо больше как обычная тема, чем как традиционный компонент «предпросмотра».


Ответы и композер

Композер – одна из более сложных частей.

Предпросмотр может открыть обычный композер Discourse для:

Ответа на тему

Композер темы открывается с моделью темы и правильной информацией о черновике.

Ответа на конкретное сообщение

Сообщение передается в композер, чтобы ответ вел себя как обычный ответ на сообщение.

Цитирования выделенного текста

Компонент также интегрируется с PostTextSelection.

Это означает, что пользователи могут выделять текст внутри предпросмотра и использовать стандартный поток цитирования/ответа Discourse.


Вложенные модальные окна

Еще одной сложной частью была система модальных окон Discourse.

Сообщения могут открывать другие модальные окна и диалоги:

  • жалобы
  • история
  • диалоги, связанные со значками
  • изменения владения
  • подтверждения удаления
  • и т.д.

Если бы им позволили взаимодействовать с глобальным сервисом модальных окон как обычно, открытие одного из них могло бы закрыть весь предпросмотр темы.

Чтобы избежать этого, компонент создает локальный механизм под-модальных окон.

Концептуально:

Модальное окно предпросмотра темы
        │
        ├── Модальное окно жалобы
        ├── Модальное окно истории
        ├── Подтверждение удаления
        ├── Модальное окно значка
        └── другое модальное окно, связанное с сообщением

Предпросмотр остается смонтированным под ним.

Компонент временно патчит соответствующие методы сервиса модальных окон, пока он активен, и восстанавливает их при уничтожении.


Маршрутизация внутри модального окна

Еще одна важная деталь – ссылки на сообщения в рамках той же темы.

Например, если сообщение содержит ссылку на:

/t/my-topic/123

предпросмотру не нужно закрываться и уходить.

Вместо этого компонент перехватывает навигацию в рамках той же темы и переходит к запрошенному сообщению внутри модального окна.

То же самое относится к ссылкам, указывающим на тему без конкретного номера сообщения.

Это удерживает пользователя внутри предпросмотра.

Если ссылка указывает на действительно другую тему, компонент сначала восстанавливает свои временные патчи сервисов и закрывается, прежде чем разрешить стандартный переход маршрута Discourse.

Эта очистка важна, потому что иначе подписки предпросмотра и трекер времени могли бы остаться активными, пока инициализируется реальный маршрут темы.


Отслеживание прочтения и отслеживание времени

Я также хотел, чтобы предпросмотр правильно работал с точки зрения Discourse.

Открытие предпросмотра не должно означать, что отслеживание прочтения полностью обходится.

Поэтому компонент обрабатывает:

  • отслеживание посещений темы
  • отслеживание видимых сообщений
  • тайминг темы
  • обновления последнего прочитанного сообщения

Трекер времени использует IntersectionObserver, чтобы определить, какие сообщения фактически видны.

Каждые 5 секунд тайминг видимых сообщений сбрасывается на:

/topics/timings

Когда модальное окно закрывается, выполняется одна последняя сброска, чтобы последние несколько секунд не были потеряны.

Реализация также ограничивает один интервал тайминга 60 секундами.


Синхронизация состояния непрочитанного в списке тем

Здесь была еще одна тонкая проблема.

Обновления состояния отслеживания тем в Discourse недостаточно для обновления значка непрочитанных, отображаемого непосредственно на строке списка тем.

Поэтому компонент обновляет фактический объект темы, связанный со строкой, после того, как информация о тайминге была сброшена.

Он обновляет значения, такие как:

last_read_post_number
unread_posts
unread
new_posts

при необходимости.

Это означает, что после прочтения темы внутри модального окна список тем может немедленно отразить новое состояние прочтения, не требуя полной перезагрузки страницы.


Видимость сообщений

Предпросмотр использует общий IntersectionObserver, чтобы определить, когда отдельные сообщения становятся видимыми.

Также есть синхронная проверка видимости при подключении наблюдателя.

Это обрабатывает крайний случай, когда сообщение уже видно при его монтировании, но асинхронный первый обратный вызов IntersectionObserver еще не сработал.

Это особенно актуально для очень коротких тем, где вся тема может уже быть видна, когда модальное окно открывается.


Соображения производительности

Одной из основных целей было избежать превращения модального окна в производительностно-тяжелую миниатюрную страницу темы.

Для этого делается несколько вещей.

Прогрессивный рендеринг

Первоначальная загрузка не рендерит каждое сообщение немедленно.

Компонент сначала рендерит достаточно сообщений, чтобы достичь целевой позиции.

Остальные сообщения затем рендерятся прогрессивно с использованием:

requestIdleCallback

когда доступно, с фолбэком на setTimeout.

Это особенно полезно при открытии длинной темы вокруг сообщения, далеко внизу потока.

Содержимое CSS (CSS Containment)

Сообщения используют:

contain: layout;
content-visibility: auto;
contain-intrinsic-size: 1px 180px;

Это позволяет браузеру избегать ненужной работы по рендерингу для сообщений, которые в данный момент не видны.

Ленивая загрузка изображений

Изображениям, которые еще не указали режим загрузки, автоматически присваиваются:

loading="lazy"
decoding="async"

Это предотвращает немедленную загрузку всего содержимого в длинной теме с множеством изображений.


Состояние загрузки

Модальное окно не просто показывает пустую белую/пустую область, пока выполняется запрос.

У него есть скелетный интерфейс с:

  • плейсхолдерами аватаров
  • плейсхолдерами имен пользователей
  • плейсхолдерами тела сообщений
  • анимацией мерцания (shimmer)

Мерцание уважает:

prefers-reduced-motion

так что анимация отключается для пользователей, запросивших уменьшенное движение.


Стабилизация позиции прокрутки

Есть несколько мест, где компоненту нужно вручную манипулировать позицией прокрутки.

Например, при загрузке более ранних сообщений newly inserted content увеличивает высоту прокрутки.

Просто добавление сообщений в начало заставило бы текущую позицию пользователя подпрыгнуть.

Поэтому компонент записывает предыдущую высоту прокрутки и компенсирует разницу после вставки сообщений.

Это удерживает текущее видимое содержимое примерно в том же месте.

То же самое относится к переходу к определенному сообщению.

Компонент выполняет этап позиционирования после рендеринга и повторно проверяет позицию на последующих кадрах, чтобы учесть содержимое, которое может еще устаканиваться.


Присутствие в теме

Когда соответствующие данные темы доступны, предпросмотр также может отображать информацию о присутствии в теме Discourse в нижней части модального окна.

Так пользователи могут видеть, кто еще в данный момент просматривает тему, не выходя из предпросмотра.


Взаимодействие с мобильными меню и фокусом

Мобильные устройства привнесли еще одну категорию проблем.

Некоторые элементы пользовательского интерфейса Discourse используют общие сервисы модальных окон/меню, и эти сервисы не обязательно знают, что предпросмотр темы в данный момент действует как вложенный контекст просмотра.

Поэтому у компонента есть дополнительная обработка вокруг:

  • modal.close()
  • меню Float Kit
  • восстановления фокуса
  • композера
  • клавиатурного управления лайтбоксом
  • блокировки прокрутки body

Например, если меню внутренне пытается вызвать глобальный метод закрытия модального окна, это не должно случайно закрыть весь предпросмотр темы.

Аналогично, когда композер открыт, фокус должен оставаться внутри композера, а не быть возвращен в контекст фокуса предпросмотра.


Конфигурация

В настоящее время компонент предоставляет следующие настройки:

Настройка По умолчанию Описание
trigger_style row Сделать всю строку кликабельной или использовать явную кнопку
plugin_outlet topic-list-after-title Отсек (outlet), используемый триггером кнопки
enable_prefetch true Включить/отключить фоновую предварительную загрузку тем
max_concurrent_prefetches 2 Максимальное количество одновременных запросов предварительной загрузки
prefetch_debounce_ms 400 Задержка перед началом предварительной загрузки
prefetch_root_margin_px 50 Начать предварительную загрузку за такое количество пикселей до входа строки в область просмотра
max_prefetches_per_minute 15 Максимальное количество спекулятивных запросов в минуту

Управление предварительной загрузкой намеренно настраивается, потому что у разных сообществ могут быть очень разные паттерны трафика и характеристики хостинга/сети.


Одна из основных целей дизайна: не ломать обычный Discourse

Я пытался сохранить компонент как можно ближе к существующей архитектуре Discourse.

Он не реализует собственный рендерер сообщений, собственный композер, собственную модель темы или свой полностью отдельный поток сообщений.

Вместо этого он создает временный контекст просмотра вокруг существующих компонентов и сервисов Discourse.

Именно поэтому некоторые части реализации более сложны, чем могут показаться на первый взгляд.

Более интересной задачей было:

Может ли тема вести себя почти как обычная тема Discourse, пока она фактически отображается внутри другого пользовательского интерфейса?

Для этого потребовалось иметь дело с границами между глобальными сервисами Discourse и локальным предпросмотром.

18 лайков

К вашему сведению:

У него есть проблемы с математикой. Но это может быть ещё одним частным случаем.

1 лайк

Абсолютная легенда :slight_smile: теперь нужно разобраться, как заставить это работать в моей конфигурации :slight_smile: @awesomerobot от чего зависит твоя тема для клика по всей строке

api.renderInOutlet("topic-list-before-link", TopicListItemClick);
2 лайка

Для всех, кто использует тему Reddit-ish, вот исправление, которое у меня заработало.

Совместимость с темой Reddit-ish

Примечание для всех, кто использует тему Reddit-ish: сама кнопка модального окна работает, но стандартный триггер строки — нет.

Проблема в том, что Reddit-ish заменяет стандартное поведение строк списка тем и обрабатывает клики по всей карточке темы. Из-за этого обычная обработка клика по строке для модального окна не работает как положено.

Изменение настройки Topic Preview Modal на:

Trigger style: button
Plugin outlet: topic-list-after-title

работает корректно, потому что Reddit-ish уже включает outlet topic-list-after-title.

Чтобы сохранить поведение клика по всей карточке, я оставил Topic Preview Modal в режиме кнопки и изменил существующее действие openTopic() в Reddit-ish так, чтобы оно запускало рабочую кнопку модального окна.

Оригинальное действие Reddit-ish:

@action
openTopic(event) {
  if (
    (event.target.nodeName === "A" && !event.target.closest(".raw-link")) ||
    event.target.closest(".badge-wrapper")
  ) {
    return;
  }

  const { navigateToTopic, topic } = this.args.outletArgs;

  if (wantsNewWindow(event)) {
    window.open(topic.lastUnreadUrl, "_blank");
  } else {
    navigateToTopic(topic, topic.lastUnreadUrl);
  }
}

Я изменил его на:

@action
openTopic(event) {
  if (
    (event.target.nodeName === "A" && !event.target.closest(".raw-link")) ||
    event.target.closest(".badge-wrapper") ||
    event.target.closest(".topic-preview-modal__trigger-wrapper")
  ) {
    return;
  }

  const { navigateToTopic, topic } = this.args.outletArgs;

  if (wantsNewWindow(event)) {
    window.open(topic.lastUnreadUrl, "_blank");
    return;
  }

  const previewButton = event.currentTarget.querySelector(
    ".topic-preview-modal__trigger-wrapper--button"
  );

  if (previewButton) {
    event.preventDefault();
    event.stopPropagation();
    previewButton.click();
    return;
  }

  navigateToTopic(topic, topic.lastUnreadUrl);
}

Триггер кнопки модального окна рендерится как:

<div class="topic-preview-modal__trigger-wrapper">
  <span
    role="button"
    class="topic-preview-modal__trigger-wrapper--button"
  >

Таким образом, это не воссоздает логику модального окна. Это просто заставляет клик по карточке Reddit-ish запускать существующую рабочую кнопку предпросмотра.

Результат:

  • Клик по карточке темы открывает модальное окно предпросмотра.

  • Клик по заголовку темы открывает модальное окно предпросмотра.

  • Кнопка предпросмотра по-прежнему работает.

  • Cmd/Ctrl+клик по-прежнему открывает обычную тему в новой вкладке.

  • Ссылки на категории и другие обычные ссылки продолжают работать нормально.

  • Если кнопка предпросмотра отсутствует, Reddit-ish возвращается к стандартной навигации по темам.

Таким образом, базовое модальное окно отлично работает с Reddit-ish; несовместимость касается исключительно стандартного триггера строки.

Я также скрыл кнопку, используя:

.topic-preview-modal__trigger-wrapper {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
}
3 лайка

Теперь нужно попробовать заставить вложенные ответы работать в модальном окне :slight_smile:

1 лайк

Ты, чувак, просто крут. Это, наверное, самая потрясающая тема компонента, которую я видел за долгое время.

Единственное, чего мне не хватает, — это большей / настраиваемой ширины. Было бы здорово, если бы мы могли сделать её такой же, как ширина постов в теме.

2 лайка

Я установил максимальную ширину в 800px

    .d-modal {
        --modal-max-width: 600px;
        --modal-width: 30em;
        --modal-min-width: 400px;
    }
1 лайк

Это применимо ко всем модальным окнам…
Я сделал это, также чтобы иконка выделялась меньше

.topic-preview-modal__trigger-wrapper--button .d-icon {
    fill: #888;
}

@media (width >= 40rem) {
  .d-modal.topic-preview-modal {
      --modal-max-width: 800px;
  }
}
2 лайка

@Don Мне интересно: почему вы не переиспользовали компонент <PostList> из ядра?

1 лайк

Привет, @Jagster, спасибо за отчет! Вот исправление: FIX: Replace visibility:hidden to allow MathJax rendering · VaperinaDEV/discourse-topic-preview-modal@f9eb70f · GitHub


Спасибо, @RGJ, я добавил для этого настройку.


Отличный вопрос.

PostList/PostListItem предназначены для списков краткого содержания на основе отрывков (черновики, закладки, ленты активности), а не для отображения фактического интерактивного потока сообщений темы.

Они выводят @post.excerpt/expandedExcerpt через DDecoratedHtml, а также заголовок с аватаром и кнопку разворачивания отрывка. Здесь нет действий «лайк», «цитата», «ответ», «редактирование» или «жалоба» — то есть всего интерактивного функционала сообщений, который нужен модальному окну.

Пагинация в PostList также однонаправлена (fetchMorePosts только добавляет записи вниз), тогда как модальное окно должно открываться на последнем прочитанном сообщении и загружать как более ранние, так и более поздние сообщения вокруг него — для этого требуется двунаправленная подгрузка сервиса postStream, а не простой обратный вызов fetch-more.

Так что повторное использование PostList потребовало бы переписывания большей части интерактивного поведения Post и двунаправленной подгрузки postStream поверх компонента, созданного для другой задачи. Использование реального компонента Post из ядра и сервиса postStream напрямую гарантирует, что модальное окно будет вести себя идентично настоящей странице темы.

3 лайка

У меня это работает с вложенными представлениями, нужно больше тестирования и, вероятно, добавить опцию в админку. Несколько человек уже используют это, Дон, и это заставляет людей чаще запрашивать контент и дольше оставаться на сайте, так как всё очень плавно и легко прокручивается и т.д. Ты проделал потрясающую работу с этим

4 лайка

Сейчас тестирую! Очень впечатляющее улучшение интерфейса

2 лайка

Нам здесь отчаянно нужны усиления, но с таким временем отклика я немного обеспокоен — у вас же есть жизнь? :laughing:

Спасибо :sign_of_the_horns:

3 лайка

Вопрос: почему border-top у .topic-avatar в итоге появляется как разделитель между частью предыдущего сообщения, которая остается видимой при прокрутке, и началом текущего сообщения? Это border-top действительно предназначено для этого, или есть какое-то правило позиционирования/переполнения, которое заставляет его вести себя так?

@media (width >= 40rem) {
    .topic-avatar {
        border-top: 1px solid var(--content-border-color);
        padding-top: var(--space-4);
        width: var(--topic-avatar-width);
        float: left;
        z-index: 2;
        height: 100%;
        overflow-anchor: none;
    }
}
1 лайк

Думаю, это может быть очень полезно для быстрого просмотра сообщений, когда не хочется ждать полной загрузки страницы. Было бы здорово, если бы можно было включить опцию, при которой клик по уведомлению открывал бы модальное окно вместо списка тем.

1 лайк

Привет, @Don! После того как я потратил 24 часа на тестирование этого потрясающего компонента, мне особенно понравилось закрытие свайпом вниз: я обнаружил, что мне не нужно двигать большим пальцем и рукой, и пользоваться сайтом было очень удобно. Однако при просмотре длинных тем возврат наверх свайпом не был идеальным.

Чтобы решить эту проблему, я добавил на мобильных устройствах закрытие свайпом вправо. Это кажется очень логичным, и, на мой взгляд, отлично работает для тех, кто хочет быстро прокручивать контент и т. д. Что вы думаете по этому поводу?

Ссылка для тестирования: https://www.carptalk-online.co.uk/ (очевидно, работает только на мобильных устройствах)

3 лайка

Я попробовал твой форум, Дамиан, и этот свайп-жест действительно идеален. Хорошая работа, как всегда!

Мне интересно, должен ли этот компонент темы работать в Horizon? Я пробовал его в двух релизах, и вверху появляется ошибка для администраторов. Похоже, что он несовместим?

Надеюсь, что всё-таки работает; мне очень нравится эта новая возможность для экспериментов.

2 лайка

Какая у тебя ошибка?

2 лайка

Привет :waving_hand:

Я добавил новый параметр: open_all_topic_links.

При включении любая ссылка на тему в любом месте страницы (содержание поста, уведомления, карточка пользователя, результаты поиска, рекомендуемые/связанные темы, боковая панель и т. д.) открывает модальное окно предпросмотра, а не перенаправляет на страницу темы — и это касается не только строк в списке тем. Ссылка на конкретный пост открывает модальное окно, прокрученное к этому посту. Ссылки внутри списка тем и внутри уже открытого модального окна предпросмотра сохраняют своё существующее поведение.

4 лайка

Вы — лучший в своём роде :slight_smile: Есть ли шанс добавить в ядро возможность закрывать окно свайпом вправо на мобильных устройствах и поддержку вложенных представлений :eyes:

2 лайка