feat: add a link parser to tasks

pull/694/head
HannesOberreiter 2 years ago
parent b46fb43e6f
commit f2072acfc0

@ -10,6 +10,7 @@
"classnames": "^2.3.2", "classnames": "^2.3.2",
"date-fns": "^2.30.0", "date-fns": "^2.30.0",
"dequal": "^2.0.3", "dequal": "^2.0.3",
"dompurify": "^3.1.0",
"easymde": "^2.18.0", "easymde": "^2.18.0",
"history": "^5.3.0", "history": "^5.3.0",
"i18next": "^23.7.6", "i18next": "^23.7.6",
@ -7016,6 +7017,11 @@
"url": "https://github.com/fb55/domhandler?sponsor=1" "url": "https://github.com/fb55/domhandler?sponsor=1"
} }
}, },
"node_modules/dompurify": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.0.tgz",
"integrity": "sha512-yoU4rhgPKCo+p5UrWWWNKiIq+ToGqmVVhk0PmMYBK4kRsR3/qhemNFL8f6CFmBd4gMwm3F4T7HBoydP5uY07fA=="
},
"node_modules/domutils": { "node_modules/domutils": {
"version": "2.8.0", "version": "2.8.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",

@ -63,6 +63,7 @@
"classnames": "^2.3.2", "classnames": "^2.3.2",
"date-fns": "^2.30.0", "date-fns": "^2.30.0",
"dequal": "^2.0.3", "dequal": "^2.0.3",
"dompurify": "^3.1.0",
"easymde": "^2.18.0", "easymde": "^2.18.0",
"history": "^5.3.0", "history": "^5.3.0",
"i18next": "^23.7.6", "i18next": "^23.7.6",

@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
import classNames from 'classnames'; import classNames from 'classnames';
import { Draggable } from 'react-beautiful-dnd'; import { Draggable } from 'react-beautiful-dnd';
import { Button, Checkbox, Icon } from 'semantic-ui-react'; import { Button, Checkbox, Icon } from 'semantic-ui-react';
import { sanitize } from 'dompurify';
import { usePopup } from '../../../lib/popup'; import { usePopup } from '../../../lib/popup';
import NameEdit from './NameEdit'; import NameEdit from './NameEdit';
@ -15,11 +16,14 @@ const Item = React.memo(
({ id, index, name, isCompleted, isPersisted, canEdit, onUpdate, onDelete }) => { ({ id, index, name, isCompleted, isPersisted, canEdit, onUpdate, onDelete }) => {
const nameEdit = useRef(null); const nameEdit = useRef(null);
const handleClick = useCallback(() => { const handleClick = useCallback(
if (isPersisted && canEdit) { (event) => {
if (!event.target.closest('a') && isPersisted && canEdit) {
nameEdit.current.open(); nameEdit.current.open();
} }
}, [isPersisted, canEdit]); },
[isPersisted, canEdit],
);
const handleNameUpdate = useCallback( const handleNameUpdate = useCallback(
(newName) => { (newName) => {
@ -40,6 +44,16 @@ const Item = React.memo(
nameEdit.current.open(); nameEdit.current.open();
}, []); }, []);
const parseLinks = (text) => {
const regex = /(http[s]?:\/\/[^\s]+)/g;
return sanitize(text).replace(regex, (match) => {
const url = new URL(match);
return `<a href="${match}" target="_blank">${
url.hostname === window.location.hostname ? url.pathname : match
}</a>`;
});
};
const ActionsPopup = usePopup(ActionsStep); const ActionsPopup = usePopup(ActionsStep);
return ( return (
@ -64,9 +78,11 @@ const Item = React.memo(
className={classNames(styles.text, canEdit && styles.textEditable)} className={classNames(styles.text, canEdit && styles.textEditable)}
onClick={handleClick} onClick={handleClick}
> >
<span className={classNames(styles.task, isCompleted && styles.taskCompleted)}> <span
{name} className={classNames(styles.task, isCompleted && styles.taskCompleted)}
</span> // eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: parseLinks(name) }}
/>
</span> </span>
{isPersisted && canEdit && ( {isPersisted && canEdit && (
<ActionsPopup onNameEdit={handleNameEdit} onDelete={onDelete}> <ActionsPopup onNameEdit={handleNameEdit} onDelete={onDelete}>

Loading…
Cancel
Save