ported some formatting logic from webroot
This commit is contained in:
parent
d692a2bb5c
commit
a947e67968
@ -1,4 +1,6 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { ChatMessage } from '../../../interfaces/chat-message.model';
|
||||
import { formatTimestamp, formatMessageText } from './messageFmt';
|
||||
import s from './ChatUserMessage.module.scss';
|
||||
|
||||
interface Props {
|
||||
@ -6,21 +8,23 @@ interface Props {
|
||||
showModeratorMenu: boolean;
|
||||
}
|
||||
|
||||
export default function ChatUserMessage(props: Props) {
|
||||
const { message, showModeratorMenu } = props;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export default function ChatUserMessage({ message, showModeratorMenu }: Props) {
|
||||
const { body, user, timestamp } = message;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { displayName, displayColor } = user;
|
||||
|
||||
const color = `hsl(${displayColor}, 100%, 70%)`;
|
||||
const formattedTimestamp = `Sent at ${formatTimestamp(timestamp)}`;
|
||||
const [formattedMessage, setFormattedMessage] = useState<string>(body);
|
||||
|
||||
useEffect(() => {
|
||||
setFormattedMessage(formatMessageText(body));
|
||||
}, [message]);
|
||||
|
||||
return (
|
||||
<div className={s.root} style={{ borderColor: color }}>
|
||||
<div className={s.root} style={{ borderColor: color }} title={formattedTimestamp}>
|
||||
<div className={s.user} style={{ color }}>
|
||||
{displayName}
|
||||
</div>
|
||||
<div className={s.message}>{body}</div>
|
||||
<div className={s.message} dangerouslySetInnerHTML={{ __html: formattedMessage }} />
|
||||
{showModeratorMenu && <div>Moderator menu</div>}
|
||||
</div>
|
||||
);
|
||||
|
63
web/components/chat/ChatUserMessage/messageFmt.ts
Normal file
63
web/components/chat/ChatUserMessage/messageFmt.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import { convertToText } from '../chat';
|
||||
import { getDiffInDaysFromNow } from '../../../utils/helpers';
|
||||
|
||||
const stripTags = (str: string) => str && str.replace(/<\/?[^>]+(>|$)/g, '');
|
||||
const convertToMarkup = (str = '') => convertToText(str).replace(/\n/g, '<p></p>');
|
||||
|
||||
function getInstagramEmbedFromURL(url: string) {
|
||||
const urlObject = new URL(url.replace(/\/$/, ''));
|
||||
urlObject.pathname += '/embed';
|
||||
return `<iframe class="chat-embed instagram-embed" src="${urlObject.href}" frameborder="0" allowfullscreen></iframe>`;
|
||||
}
|
||||
|
||||
function isMessageJustAnchor(embedText: string, message: string, anchors: HTMLAnchorElement[]) {
|
||||
if (embedText !== '' && anchors.length === 1) return false;
|
||||
return stripTags(message) === stripTags(anchors[0]?.innerHTML);
|
||||
}
|
||||
|
||||
function getMessageWithEmbeds(message: string) {
|
||||
let embedText = '';
|
||||
// Make a temporary element so we can actually parse the html and pull anchor tags from it.
|
||||
// This is a better approach than regex.
|
||||
const container = document.createElement('p');
|
||||
container.innerHTML = message;
|
||||
|
||||
const anchors = Array.from(container.querySelectorAll('a'));
|
||||
anchors.forEach(({ href }) => {
|
||||
if (href.includes('instagram.com/p/')) embedText += getInstagramEmbedFromURL(href);
|
||||
});
|
||||
|
||||
// If this message only consists of a single embeddable link
|
||||
// then only return the embed and strip the link url from the text.
|
||||
if (isMessageJustAnchor(embedText, message, anchors)) return embedText;
|
||||
return message + embedText;
|
||||
}
|
||||
|
||||
export function formatTimestamp(sentAt: Date) {
|
||||
const now = new Date(sentAt);
|
||||
if (Number.isNaN(now)) return '';
|
||||
|
||||
const diffInDays = getDiffInDaysFromNow(sentAt);
|
||||
|
||||
if (diffInDays >= 1) {
|
||||
const localeDate = now.toLocaleDateString('en-US', {
|
||||
dateStyle: 'medium',
|
||||
});
|
||||
return `at ${localeDate} at ${now.toLocaleTimeString()}`;
|
||||
}
|
||||
|
||||
return `${now.toLocaleTimeString()}`;
|
||||
}
|
||||
|
||||
/*
|
||||
You would call this when receiving a plain text
|
||||
value back from an API, and before inserting the
|
||||
text into the `contenteditable` area on a page.
|
||||
*/
|
||||
|
||||
export function formatMessageText(message: string) {
|
||||
let formattedText = getMessageWithEmbeds(message);
|
||||
formattedText = convertToMarkup(formattedText);
|
||||
return formattedText;
|
||||
// return await highlightUsername(formattedText, username);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user