Live Demo - GitHub Repo

Часть 1 - Часть 2 - Часть 3 - Часть 4 - Часть 5 - Часть 6 - Часть 7

В конце части 5 мы остановились на полностью функционирующем чате. Но теперь мы собираемся добавить боковую панель со списком активных пользователей в комнате. Phoenix Presence делает это очень просто.

Начните с запуска генератора присутствия

mix phoenix.gen.presence

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

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

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

Мы просто добавили одну строку в функцию join, send self (),: after_join. Затем новая функция handle_info использует модуль Presence для отслеживания сокета с помощью ключа, равного идентификатору current_user сокета, и мета-объекта. В мета-объект мы можем поместить любую информацию, например, тип устройства или время в сети. Здесь я просто создаю ключ: user со значением, равным json-представлению пользователя, которого мы создали в UserView. Затем функция push отправляет событие «присутствие_state» во внешний интерфейс, которое мы можем перехватить для синхронизации нашего списка пользователей.

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

Сначала мы инициализируем пустой объект присутствия, а затем обновляем его в событии 'availability_state', которое мы добавили в канал комнаты, а также для 'availability_diff' , которое Phoenix генерирует всякий раз, когда пользователь отключается от канала.

Во время каждого события мы будем вызывать нашу функцию syncPresentUsers (). Класс Presence в библиотеке javascript Phoenix имеет функцию list, которая принимает объект присутствия в качестве первого аргумента, и функцию, которая возвращает значение, каждое из которых мы хотим перечислить. представить объект пользователем. У Криса МакКорда есть полезный пост в блоге, который лучше описывает это. В моем случае я пишу встроенную функцию, которая выбирает мета-ключ : user, который мы определили, чтобы мы могли сопоставить каждого текущего пользователя и поместить его в массив существующих пользователей. Затем этот массив отправляется в наш редуктор комнаты.

Теперь мы можем получить список текущих пользователей из хранилища redux и передать его новому компоненту RoomSidebar в нашем контейнере Room. Мы также добавим currentUser в качестве опоры, чтобы мы могли отображать его имя пользователя в RoomSidebar.

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

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

Git commit с работающим Presence

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

Прокрутка страницы

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

Чтобы решить эту проблему, мы реализуем новую конечную точку API. Мы будем извлекать данные оттуда, когда пользователь прокручивает список сообщений до начала, и добавляем сообщения в свой массив сообщений.

Создайте новый message_controller.ex, который будет иметь запрос, аналогичный room_channel.ex:

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

Этому новому действию index также потребуется соответствующее представление index.json в MessageView.

Нам также нужно добавить этот новый маршрут в router.ex, вложив его в маршруты для комнат.

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

Мы отправляем запросы REQUEST, SUCCESS и FAILURE, чтобы мы могли соответствующим образом обновить пользовательский интерфейс.

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

Как и в нашем первоначальном ответе, нам нужно reverse () сообщений, чтобы получить их в правильном порядке для нашего пользовательского интерфейса. И мы просто переключаем значение loadingOlderMessages, чтобы отслеживать состояние загрузки.

Я собираюсь забежать вперед и обновить компонент MessageList, добавив в него функции, необходимые для отслеживания прокрутки, а затем обсудить это:

Прежде всего, в нашем конструкторе мы отключаем handleScroll (),, поэтому он вызывается только после задержки в 200 мс. И мы прикрепляем прослушиватель событий к окну для этой функции, когда компонент монтируется / размонтируется.

Затем handleScroll () вызовет функцию loadMore (), которая будет отправлена ​​в контейнере Room, только если есть другие сообщения, которые нужно загрузить, и если пользователь прокрутил внутри 20 пикселей верха контейнера. Через мгновение мы реализуем свойства moreMessages и loadMore в контейнере Room.

В методе жизненного цикла componentWillReceiveProps мы вызываем новую функцию mightScrollToBottom (), когда длина наших сообщений изменяется - в основном, всякий раз, когда получено новое сообщение.

MaybeScrollToBottom () будет прокручиваться вниз только в том случае, если текущая позиция прокрутки пользователя в списке сообщений находится в пределах 50 пикселей от нижней части. Таким образом, если они прокрутили вверх, чтобы просмотреть старые сообщения, мы не будем опускать их экран обратно при получении нового сообщения. Но если они просматривают нижнюю часть списка сообщений, их представление будет прокручиваться вниз, чтобы отобразить недавно полученные сообщения.

Функция scrollToBottom () заключена в функцию setTimeout (), потому что без нее она будет отключена на высоту 43 пикселей самого последнего сообщения - это небольшая хитрость.

Вернемся к контейнеру Room, чтобы реализовать то, что осталось:

Важным здесь является то, что мы добавили функцию handleLoadMore (), которая улавливает свойство onLoadMore MessageLists. Он берет самое старое сообщение (первый элемент в списке сообщений - this.props.messages [0] .id) и использует его в качестве параметра last_seen_id.

MessageList будет знать, есть ли еще сообщения, которые нужно загрузить, в зависимости от того, больше ли total_pages разбивки на страницы, чем текущее page_number.

В handleMessageCreate () мы нарушаем соглашение React, вызывая функцию scrollToBottom (), которая находится внутри дочернего компонента MessageList. В данном случае я извиняюсь, потому что это краткое решение, гарантирующее, что пользователь, который пишет новое сообщение, будет прокручен в нижнюю часть страницы, если он ранее прокручивался в представлении - в противном случае может быть неочевидно, что их сообщение было успешно отправлено.

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

Git commit с работающей прокруткой страницы

Прочтите часть 7 или просмотрите живую демонстрацию