Untitled

mail@pastecode.io avatar
unknown
plain_text
2 years ago
6.4 kB
3
Indexable
import React, { useState, useEffect } from 'react';
import firebase from 'firebase/app';
import 'firebase/firestore';
import 'firebase/storage';
import { faTrash } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import DOMPurify from 'dompurify';

const ChatMessage = (props) => {
  const { text, uid, fileUrl, photoURL } = props.message;
  const messageClass = uid === firebase.auth().currentUser.uid ? 'sent' : 'received';
  const [username, setUsername] = useState(null);
  const [taggedText, setTaggedText] = useState(null);
  const [avatarUrl, setAvatarUrl] = useState(null);
  const handleDeleteClick = async () => {
    const confirmed = window.confirm("Are you sure you want to delete this message?");
    if (confirmed) {
      const db = firebase.firestore();
      await db.collection("messages").doc(props.id).delete();
    }
  };
  useEffect(() => {
    const storageRef = firebase.storage().ref();
    const avatarRef = storageRef.child(`profile-pictures/${uid}/0.png`);

    avatarRef.getDownloadURL()
      .then((url) => {
        setAvatarUrl(url);
      })
      .catch(() => {
        if (firebase.auth().photoURL) {
          setAvatarUrl(firebase.auth().photoURL);
        }
      });
      

    const fetchUsernameAndTaggedText = async () => {
      const usernameSnapshot = await firebase.database().ref(`users/${uid}/username`).once('value');
      const usernameData = usernameSnapshot.val();
      if (usernameData) {
        setUsername(usernameData);
      } else {
        setUsername(firebase.auth().displayName);
      }
  
      const tagRegex = /@([\w\s]+)/g;
      const matches = text ? text.match(tagRegex) : null;
      if (matches) {
        const replacedMatches = await Promise.all(matches.map(async (match) => {
          const username = match.trim().substring(1);
          const userRef = firebase.database().ref(`users`).orderByChild('username').equalTo(username);
          const snapshot = await userRef.once('value');
          const user = snapshot.val();
          if (user) {
            const uid = Object.keys(user)[0];
            return uid;
          } else {
            return null;
          }
        }));
        let currentIndex = 0;
        const newTaggedText = text.replace(tagRegex, (match) => {
          const uid = replacedMatches[currentIndex++];
          if (uid) {
            return `<a href="/profile/${uid}" style="color: blue;">${match}</a>`;
          } else {
            return match;
          }
        });
        const linkRegex = /(?:^|[^"'])(https?:\/\/[^\s"]+)(?=["']|$)/g;
        const newLinkedText = newTaggedText.replace(linkRegex, (match) => {
          return `<a href="${match}" class="link-preview" target="_blank">${match}</a>`;
        });
        setTaggedText(newLinkedText);
      } else {
        const linkRegex = /(?:^|[^"'])(https?:\/\/[^\s"]+)(?=["']|$)/g;
        const newLinkedText = text ? text.replace(linkRegex, (match) => {
          return `<a href="${match}" class="link-preview" target="_blank">${match}</a>`;
        }) : null;
        if (newLinkedText !== text) {
          setTaggedText(newLinkedText);
        } else {
          setTaggedText(text);
        }
      }
    }
  
    fetchUsernameAndTaggedText();
  }, [uid, text, taggedText, fileUrl]);
  

  const usernameClass = messageClass === 'sent' ? 'username-sent' : 'username-received';

  const [showDelete, setShowDelete] = useState(false);
  
  const showDeleteButton = () => {
  if (uid === firebase.auth().currentUser.uid) {
  setShowDelete(true);
  }
  };
  const hideDeleteButton = () => {
  setShowDelete(false);
  };
  
  const [linkPreview, setLinkPreview] = useState(null);
  
  useEffect(() => {
      const linkRegex = /(?:^|[^"'])(https?:\/\/[^\s"]+)(?=["']|$)/g;
      const matches = text ? text.match(linkRegex) : null;
    if (matches) {
      const link = matches[0];
      const url = new URL(link);
      const previewUrl = `https://api.linkpreview.net/?key=${process.env.REACT_APP_LINK_PREVIEW_API_KEY}&q=${url}`;
    
      fetch(previewUrl)
        .then(response => response.json())
        .then(data => {
          if (data.title) {
            setLinkPreview({
              url: url.href,
              title: data.title,
              description: data.description,
              image: data.image
            });
          }
        })
        .catch(error => console.error(error));
    }
  }, [text]);
  
  const sanitizedText = DOMPurify.sanitize(taggedText, { ALLOWED_TAGS: ['a', 'img'], ALLOWED_ATTR: ['href', 'src'] });
  const limitWords = (str, limit) => {
    const words = str.trim().split(/\s+/);
    return words.length <= limit ? str : words.slice(0, limit).join(' ') + '...';
  };
  const linkPreviewHTML = linkPreview ? `
    <a href="${linkPreview.url}" target="_blank" rel="noopener noreferrer" class="link-preview">
      ${linkPreview.image ? `<img src="${linkPreview.image}" alt="${linkPreview.title}" class="link-preview-image" />` : ''}
      <div class="link-preview-details">
        ${linkPreview.title ? `<h4 class="link-preview-title">${linkPreview.title}</h4>` : ''}
        ${linkPreview.description ? `<p class="link-preview-description">${limitWords(linkPreview.description, 20)}</p>` : ''}
      </div>
    </a>` : '';
  const safeHTML = `<span class="${usernameClass}">${username}: </span>${sanitizedText.replace(/<a /g, '<a style="color: blue;" ')}${linkPreviewHTML}`;

  return (
    <div
      className={`message ${messageClass}`}
      onMouseEnter={showDeleteButton}
      onMouseLeave={hideDeleteButton}
    >
      {avatarUrl ? (
        <img src={avatarUrl} alt="Avatar" />
      ) : (
        <img src={photoURL} alt="Avatar" />
      )}
      <div className="message-content">
        {username && taggedText && (
          <p
          className="message-text"
          dangerouslySetInnerHTML={{ __html: safeHTML }}
        />
        )}
        {fileUrl && (
                <a href={fileUrl} target="_blank" rel="noopener noreferrer">
                  <img src={fileUrl} alt="chat attachment" className="message-image-preview" />
                </a>
        )}
        {showDelete && (
          <div className="delete-button" onClick={handleDeleteClick}>
            <FontAwesomeIcon icon={faTrash} />
          </div>
        )}
        </div>
    </div>
  );
};

export default ChatMessage;