import React from 'react'; import LinkifyIt from 'linkify-it'; import { RenderTextCallbackType } from '../../types/Util'; import { isLinkSneaky } from '../../../js/modules/link_previews'; import { SessionHtmlRenderer } from '../session/SessionHTMLRenderer'; const linkify = LinkifyIt(); interface Props { text: string; /** Allows you to customize now non-links are rendered. Simplest is just a . */ renderNonLink?: RenderTextCallbackType; } const SUPPORTED_PROTOCOLS = /^(http|https):/i; const HAS_AT = /@/; export class Linkify extends React.Component { public static defaultProps: Partial = { renderNonLink: ({ text }) => text, }; public render() { const { text, renderNonLink } = this.props; const results: Array = []; let count = 1; const matchData = linkify.match(text) || []; let last = 0; // We have to do this, because renderNonLink is not required in our Props object, // but it is always provided via defaultProps. if (!renderNonLink) { return; } if (matchData.length === 0) { return renderNonLink({ text, key: 0 }); } matchData.forEach((match: { index: number; url: string; lastIndex: number; text: string }) => { if (last < match.index) { const textWithNoLink = text.slice(last, match.index); results.push(renderNonLink({ text: textWithNoLink, key: count++ })); } const { url, text: originalText } = match; if (SUPPORTED_PROTOCOLS.test(url) && !isLinkSneaky(url) && !HAS_AT.test(url)) { results.push( {originalText} ); } else { results.push(renderNonLink({ text: originalText, key: count++ })); } last = match.lastIndex; }); if (last < text.length) { results.push(renderNonLink({ text: text.slice(last), key: count++ })); } return results; } // disable click on elements so clicking a message containing a link doesn't // select the message.The link will still be opened in the browser. public handleClick = (e: any) => { e.stopPropagation(); }; }