Внедрение каналов данных для текстового чата

В предыдущей статье мы реализовали первую из трех функций приложения Magny; видео чат. Мы узнали следующее:

  • Создание предложения и ответ
  • Сигнализация с помощью Firestore
  • Отслеживание состояния соединения
  • Закрытие соединения

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

Пользовательский интерфейс для текстового чата

Это простой код для добавления в окно чата. Поскольку .container div имеет гибкий дисплей, это окно чата будет отображаться в правой части страницы. Вы можете спросить, почему в коде есть пустой #chat-box div. Это наш шаблон, чтобы добавить наши сообщения для отображения. Мы будем добавлять их из JavaScript, манипулируя DOM.

Вот таблица стилей для наших элементов текстового чата. Наиболее важными особенностями здесь являются то, что мы отключили отображение полосы прокрутки и дали overflow-y функцию прокрутки. Эти настройки сделали окно чата встроенным в наше приложение. Вы увидите, что это окно само по себе можно прокручивать. .talk-bubble проявится, когда мы реализуем манипуляции с DOM. Вы проверяете наш прогресс и смотрите на пользовательский интерфейс из ветки ниже:



Настройка канала данных

Как только мы создадим соединение, мы можем открыть канал данных. Этот канал данных можно использовать для передачи различных данных, таких как строки, целые числа, объекты (в формате JSON) или даже файлы (в формате буфера). В этом случае мы будем отправлять и получать простые строки. Поскольку это новая функция, давайте создадим новый файл TypeScript с именем text.ts. Прежде всего, импортируйте peerConnection из connect.ts

import { peerConnection } from './connect'

Мы будем открывать канал данных на обоих одноранговых узлах, как мы это делали для открытия соединения. Однако большая часть кода одинакова для обоих одноранговых узлов. Поэтому создадим для них общую функцию.

  • Эта функция принимает RTCDataChannel в качестве аргумента.
  • Затем он разрешает ввод сообщений.
  • Ввод сообщений имеет прослушиватель событий для каждой нажатой клавиши. Он используется для проверки, является ли поле ввода пустым или нет. Если он не пуст, мы включаем кнопку отправки. Это делается для предотвращения отправки пустых сообщений.
  • Наконец, он добавляет прослушиватель событий к событию отправки формы, который берет значение из ввода сообщения и отправляет его удаленному узлу. Он выполняет функцию createBubble() и очищает ввод. Так как отправка сообщения не входит в keyup событие поля ввода, отключаем кнопку.

Давайте посмотрим, что делает функция createBubble:

  • Он принимает сообщение и логическое значение в качестве параметров. Логическое значение определяет, является ли отправитель нами или нет.
  • Как вы помните, мы написали несколько таблиц стилей для пузырей. Мы создаем div с классом talk-bubble и добавляем еще один класс в соответствии с логическим значением параметра. Если это послание от нас, мы помещаем его в нужное место; если это сообщение, пришедшее от удаленного узла, мы помещаем его слева от окна чата.
  • Последнее, что нам нужно сделать, это добавить этот новый пузырь в окно чата. Мы также прокручиваем окно вниз, чтобы пользователи могли видеть новое сообщение без необходимости прокрутки.

Общие функции готовы. Давайте создадим функции, необходимые для установления каналов данных.

  • Тот, кто создаст канал данных, будет инициатором звонка. Мы даем ему имя, так как мы будем использовать другой канал для обмена файлами.
  • Отслеживаем открытость канала данных. Когда он открыт, мы выполняем функцию openTextChannel для подготовки окна чата.
  • Наконец, он ждет сообщений. Когда через textChannel приходит сообщение, мы создаем из него пузырь.

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

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

Все готово для установления обмена текстовыми сообщениями. Однако это всего лишь функции, мы их не выполняем. Давайте выполним их в соответствующих местах в main.ts.

Мы создаем каналы данных, прежде чем делать предложение и отвечать, потому что кандидаты ICE и объекты SDP создаются в соответствии с данными, которые мы будем передавать. Вот почему мы помещаем видео- и аудиопотоки в peerConnection еще до его установки.

Давайте посмотрим на результат:

Как видите, мы установили не только видеочат, но и текстовое общение. Вы можете проверить прогресс в ветке ниже:



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

Говоря о сбросе, нам нужно добавить больше функциональности нашей кнопке отбоя, так как мы добавили новую функцию.

Сбросить соединение

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

Добавьте эту функцию в конец файла connect.ts. Чтобы сбросить соединение, он просто закрывает соединение и создает новое. Причина, по которой мы создали новую функцию в отдельном файле, заключается в том, что мы не можем присвоить значение импортируемой переменной. Обратите внимание, что мы изменили peerConnection на переменную let вместо const, так как мы назначаем новую RTCPeerConnection. Вы можете заметить, что есть функция с именем trackConnection. Давайте рассмотрим это сейчас.

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

Мы добавили эту функцию в функцию resetConnection. Кроме того, добавьте его сразу после того, как мы объявим peerConnection в начале файла connect.ts.

Осталось сделать все заново. Давайте перенастроим нашу функцию отбоя.

Во-первых, мы выполняем функцию resetConnection, чтобы повторно объявить peerConnection. После этого сбрасываем и очищаем следующие части:

  • Ввод в чат и кнопка отправки
  • Сам чат
  • Предложение ввода
  • Кнопки вызова и ответа
  • Кнопка камеры
  • Видео элементы
  • Кнопка отбоя

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



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

В следующей статье мы добавим функцию обмена файлами. Оставайтесь с нами, чтобы узнать больше!

До скорого…