mirror of
https://github.com/navidrome/navidrome.git
synced 2025-04-13 02:37:18 +03:00
* feat: create vite project * feat: it's alive! * feat: `make dev` working! * feat: replace custom serviceWorker with vite plugin * test: replace Jest with Vitest * fix: run prettier * fix: skip eslint for now. * chore: remove ui.old folder * refactor: replace lodash.pick with simple destructuring * fix: eslint errors (wip) * fix: eslint errors (wip) * fix: display-name eslint errors (wip) * fix: no-console eslint errors (wip) * fix: react-refresh/only-export-components eslint errors (wip) * fix: react-refresh/only-export-components eslint errors (wip) * fix: react-refresh/only-export-components eslint errors (wip) * fix: react-refresh/only-export-components eslint errors (wip) * fix: build * fix: pwa manifest * refactor: pwa manifest * refactor: simplify PORT configuration * refactor: rename simple JS files * test: cover playlistUtils * fix: react-image-lightbox * feat(ui): add sourcemaps to help debug issues
77 lines
1.8 KiB
JavaScript
77 lines
1.8 KiB
JavaScript
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 Linkify = ({ 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
|
|
key={'last-span-key'}
|
|
dangerouslySetInnerHTML={{ __html: text.substring(lastIndex) }}
|
|
/>,
|
|
)
|
|
}
|
|
|
|
return elements.length === 1 ? elements[0] : elements
|
|
}, [linkify, text, rest, classes.link])
|
|
|
|
const parsedText = useMemo(() => parse(), [parse])
|
|
|
|
return <>{parsedText}</>
|
|
}
|
|
|
|
Linkify.propTypes = {
|
|
text: PropTypes.string,
|
|
}
|
|
|
|
export default React.memo(Linkify)
|