Files
iNaturalistReactNative/src/components/SharedComponents/UserText.js
Amanda Bullington 21b9cc6a97 Update Eslint to support TypeScript (#1419)
* Add typescript parser and fix Flow errors in JS files

* Uninstall packages from react-native/eslint-config

* Fix all flow errors (or ignore them for unknowns
2024-04-18 21:35:26 -07:00

136 lines
2.9 KiB
JavaScript

import { fontRegular } from "appConstants/fontFamilies.ts";
import linkifyHtml from "linkify-html";
import { isEqual, trim } from "lodash";
import MarkdownIt from "markdown-it";
import * as React from "react";
import { useWindowDimensions } from "react-native";
import HTML, { defaultSystemFonts } from "react-native-render-html";
import WebView from "react-native-webview";
import sanitizeHtml from "sanitize-html";
const ALLOWED_TAGS = ( `
a
abbr
acronym
b
blockquote
br
cite
code
del
div
dl
dt
em
h1
h2
h3
h4
h5
h6
hr
i
img
ins
li
ol
p
pre
s
small
strike
strong
sub
sup
table
tbody
td
t
th
thead
tr
tt
ul
` ).split( /\s+/m ).filter( e => e !== "" );
const ALLOWED_ATTRIBUTES_NAMES = (
"href src width height alt cite title class name abbr value align target rel"
).split( " " );
const ALLOWED_ATTRIBUTES = { a: ["href"] };
ALLOWED_TAGS.filter( tag => tag !== "a" )
.forEach( tag => { ALLOWED_ATTRIBUTES[tag] = ALLOWED_ATTRIBUTES_NAMES; } );
const SANITIZE_HTML_CONFIG = {
allowedTags: ALLOWED_TAGS,
allowedAttributes: ALLOWED_ATTRIBUTES,
allowedSchemes: ["http", "https"]
};
const LINKIFY_OPTIONS = {
className: null,
attributes: { rel: "nofollow noopener" },
ignoreTags: ["a", "code", "pre"]
};
function hyperlinkMentions( text ) {
return text.replace( /(\B)@([a-z][\\\w\\\-_]*)/g, "$1<a href='https://www.inaturalist.org/people/$2'>@$2</a>" );
}
type Props = {
text:string,
htmlStyle?:Object,
}
const UserText = ( {
children,
htmlStyle,
text: textProp
} : Props ): React.Node => {
// Allow stringified children to serve as text if no prop provided
const text = textProp || children.toString( );
const { width } = useWindowDimensions( );
let html = trim( text );
// replace ampersands in URL params with entities so they don't get
// interpretted by safeHtml
html = html.replace( /&(\w+=)/g, "&amp;$1" );
const md = new MarkdownIt( {
html: true,
breaks: true
} );
md.renderer.rules.table_open = ( ) => "<table class=\"table\">\n";
html = md.render( html );
html = sanitizeHtml( hyperlinkMentions( html ), SANITIZE_HTML_CONFIG );
// Note: markdown-it has a linkifier option too, but it does not allow you
// to specify attributes like nofollow, so we're using linkifyjs, but we
// are ignoring URLs in the existing tags that might have them like <a> and
// <code>
html = linkifyHtml( html, LINKIFY_OPTIONS );
const baseStyle = {
fontFamily: fontRegular,
fontSize: 16,
lineHeight: 22,
...htmlStyle
};
const fonts = [fontRegular, ...defaultSystemFonts];
return (
<HTML
baseStyle={baseStyle}
contentWidth={width}
source={{ html }}
WebView={WebView}
systemFonts={fonts}
/>
);
};
// Memoize to prevent excessive re-renders when HTML component is in a list
export default React.memo( UserText, ( oldProps, newProps ) => isEqual( oldProps, newProps ) );