Linkify urls in album comments. Fixes #1053, supersedes #1570 and #1169

Simple approach, may be extended/enhanced in the future.
This commit is contained in:
Deluan 2022-10-06 23:46:30 -04:00
parent fa5dc5af10
commit c530ccf138
2 changed files with 75 additions and 1 deletions

View File

@ -33,6 +33,7 @@ import {
import config from '../config'
import { intersperse } from '../utils'
import AlbumExternalLinks from './AlbumExternalLinks'
import AnchorMe from '../common/AnchorMe'
const useStyles = makeStyles(
(theme) => ({
@ -116,7 +117,7 @@ const AlbumComment = ({ record }) => {
const formatted = useMemo(() => {
return lines.map((line, idx) => (
<span key={record.id + '-comment-' + idx}>
<span dangerouslySetInnerHTML={{ __html: line }} />
<AnchorMe text={line} />
<br />
</span>
))

73
ui/src/common/AnchorMe.js Normal file
View File

@ -0,0 +1,73 @@
import React, { useCallback, useMemo } from 'react'
import { Link } from '@material-ui/core'
import { makeStyles } from '@material-ui/core/styles'
import PropTypes from 'prop-types'
const useStyles = makeStyles(
(theme) => ({
link: {
textDecoration: 'none',
color: theme.palette.primary.main,
},
}),
{ name: 'RaLink' }
)
const AnchorMe = ({ text, ...rest }) => {
const classes = useStyles()
const linkify = useCallback((text) => {
const urlRegex =
/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#/%?=~_|!:,.;]*[-A-Z0-9+&@#/%=~_|])/gi
return [...text.matchAll(urlRegex)]
}, [])
const parse = useCallback(() => {
const matches = linkify(text)
if (matches.length === 0) return text
const elements = []
let lastIndex = 0
matches.forEach((match, index) => {
// Push text located before matched string
if (match.index > lastIndex) {
elements.push(text.substring(lastIndex, match.index))
}
const href = match[0]
// Push Link component
elements.push(
<Link
{...rest}
target="_blank"
className={classes.link}
rel="noopener noreferrer"
key={index}
href={href}
>
{href}
</Link>
)
lastIndex = match.index + href.length
})
// Push remaining text
if (text.length > lastIndex) {
elements.push(
<span dangerouslySetInnerHTML={{ __html: text.substring(lastIndex) }} />
)
}
return elements.length === 1 ? elements[0] : elements
}, [linkify, text, rest, classes.link])
const parsedText = useMemo(() => parse(), [parse])
return <>{parsedText}</>
}
AnchorMe.propTypes = {
text: PropTypes.string,
}
export default React.memo(AnchorMe)