Вставка HTML в сообщение Snackbar

#reactjs #snackbar #twilio-video

# #reactjs #snackbar #twilio-видео

Вопрос:

Итак, я разветвляю пример приложения для видеочата Twilio (https://github.com/twilio/twilio-video-app-react ). Для функции чата используются сообщения Snackbar. Который работает нормально. Но я хочу разрешить пользователю отправлять сообщение, начинающееся с http, чтобы затем сообщение было отправлено в виде URL-адреса гиперссылки.

Компонент ChatInput отлично работает для отображения этого сообщения в виде URL-адреса гиперссылки для локального пользователя (т.Е. Отправителя). Но обработчик событий DataTrack для удаленных пользователей не отображает сообщение в виде гиперссылки. Просто отображает буквальный текст.

Вот файл ChatInput.tsx, где любое сообщение, начинающееся с http, будет корректно отображаться для локального пользователя.

 import React, { useState } from 'react';
import { Button, FormControl, TextField } from '@material-ui/core';
import { useSnackbar } from 'notistack';
import useVideoContext from '../../../hooks/useVideoContext/useVideoContext';

export default function ChatInput() {
  const [message, setMessage] = useState('');
  const { room } = useVideoContext();
  const { enqueueSnackbar } = useSnackbar();

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => setMessage(e.target.value);

  const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();

    if (message) {
      // Get the LocalDataTrack that we published to the room.
      const [localDataTrackPublication] = [...room.localParticipant.dataTracks.values()];

      // Construct a message to send
      const fullMessage = `${room.localParticipant.identity} says: ${message}`;

      if (message.startsWith('http')) {
        // Send the message
        localDataTrackPublication.track.send(`<a href="${message}">${message}</a>`);

        // Render the message locally so the local participant can see that their message was sent.
        enqueueSnackbar(<a href={message}>{message}</a>);
      } else {
        // Send the full message
        localDataTrackPublication.track.send(fullMessage);

        // Render the full message locally so the local participant can see that their message was sent.
        enqueueSnackbar(fullMessage);
      }
      //Reset the text field
      setMessage('');
    }
  };

  return (
    <form autoComplete="off" style={{ display: 'flex', alignItems: 'center' }} onSubmit={handleSubmit}>
      <FormControl>
        <label htmlFor="chat-snack-input" style={{ color: 'black' }}>
          Say something:
        </label>
        <TextField value={message} autoFocus={true} onChange={handleChange} id="chat-snack-input" size="small" />
      </FormControl>
      <Button type="submit" color="primary" variant="contained" style={{ marginLeft: '0.8em' }}>
        Send
      </Button>
    </form>
  );
} 

И вот файл DataTrack.ts, который отображает только строковый литерал для любого удаленного пользователя.

 import { useEffect } from 'react';
import { DataTrack as IDataTrack } from 'twilio-video';
import { useSnackbar } from 'notistack';

var stringToHTML = function (str) {
    var parser = new DOMParser();
    var doc = parser.parseFromString(str, 'text/html');
    return doc.body;
};

export default function DataTrack({ track }: { track: IDataTrack }) {
  const { enqueueSnackbar } = useSnackbar();

  useEffect(() => {
    const handleMessage = (message: string) => { 
    if (message.startsWith('http')) {
        const newMessage = stringToHTML(message);
        enqueueSnackbar(newMessage);
        }
    else {
    enqueueSnackbar(message); }
    };
    track.on('message', handleMessage);
    return () => {
      track.off('message', handleMessage);
    };
  }, [track, enqueueSnackbar]);

  return null; // This component does not return any HTML, so we will return 'null' instead.
} 

Любые предложения относительно того, как я могу заставить удаленных пользователей получать тот же URL-адрес гиперссылки, который видит отправитель?

Ответ №1:

TL; DR: функция stringToHTML возвращает ссылку на элемент DOM, а не на элемент React при передаче сообщения в NotiStack попробуйте обернуть его элементом React:

 //const newMessage = stringToHTML(message);
enqueueSnackbar(<div dangerouslySetInnerHTML={{__html:message}} />);
 

NL; PR: И / или я не уверен, почему вы передаете значение сообщения в NotiStack по-разному в двух компонентах:

 if (message.startsWith('http')) { 
//local user
 enqueueSnackbar(<a href={message}>{message}</a>); //1)
//vs.  remote user
const newMessage = stringToHTML(message);
enqueueSnackbar(newMessage); // should it be the same as 1)?
 

Ответ №2:

Ценю обратную связь. Я обрабатывал сообщение двумя разными способами в зависимости от того, передавался ли URL-адрес или нет. На самом деле автор проекта фактически предоставил чистое решение. Установка пакета Linkify позволяет форматировать как таковые только те строковые элементы, которые считаются HTML.

Вот переработанное содержимое DataTrack.tsx. Работает как чемпион!

 import React from 'react';
import { useEffect } from 'react';
import { DataTrack as IDataTrack } from 'twilio-video';
import { useSnackbar } from 'notistack';

import Linkify from 'react-linkify';

export default function DataTrack({ track }: { track: IDataTrack }) {
  const { enqueueSnackbar } = useSnackbar();
  useEffect(() => {
    const handleMessage = (message: string) =>
      enqueueSnackbar(
        <Linkify
          componentDecorator={(decoratedHref, decoratedText, key) => (
            <a target="_blank" rel="noopener" href={decoratedHref} key={key}>
              {decoratedText}
            </a>
          )}
        >
          {message}
        </Linkify>
      );
    track.on('message', handleMessage);
    return () => {
      track.off('message', handleMessage);
    };
  }, [track, enqueueSnackbar]);

  return null; // This component does not return any HTML, so we will return 'null' instead.
}