mirror of
https://github.com/balzack/databag.git
synced 2025-04-23 10:05:19 +00:00
sanitizing message for clickable links
This commit is contained in:
parent
383961d467
commit
6e0ddc1238
@ -24,6 +24,7 @@
|
||||
"@vitejs/plugin-react": "4.3.1",
|
||||
"crypto-js": "^4.2.0",
|
||||
"databag-client-sdk": "^0.0.20",
|
||||
"dompurify": "^3.2.3",
|
||||
"jest": "29.1.1",
|
||||
"jsencrypt": "^3.3.2",
|
||||
"react": "18.3.1",
|
||||
|
@ -14,8 +14,7 @@
|
||||
padding-left: 2px;
|
||||
padding-right: 2px;
|
||||
position: absolute;
|
||||
font-size: 8px;
|
||||
bottom: 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.close {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { ActionIcon, Image, Textarea } from '@mantine/core'
|
||||
import React, { useEffect } from 'react';
|
||||
import { ActionIcon, Image, Text } from '@mantine/core'
|
||||
import { useAudioFile } from './useAudioFile.hook';
|
||||
import classes from './AudioFile.module.css'
|
||||
import audio from '../../images/audio.png'
|
||||
@ -16,7 +16,7 @@ export function AudioFile({ source, updateLabel, disabled, remove }: {source: Fi
|
||||
return (
|
||||
<div className={classes.asset}>
|
||||
<Image radius="sm" className={classes.thumb} src={audio} />
|
||||
<Textarea className={classes.label} size="xs" value={state.label} onChange={(event) => setLabel(event.currentTarget.value)} disabled={disabled} />
|
||||
<Text className={classes.label}>{ state.label }</Text>
|
||||
<ActionIcon className={classes.close} variant="subtle" disabled={disabled} onClick={remove}>
|
||||
<IconX />
|
||||
</ActionIcon>
|
||||
|
@ -13,7 +13,7 @@ export function BinaryFile({ source, disabled, remove }: {source: File, disabled
|
||||
<Image radius="sm" className={classes.thumb} src={binary} />
|
||||
<Text className={classes.name}>{ state.name }</Text>
|
||||
<Text className={classes.extension}>{ state.extension }</Text>
|
||||
{ !state.disabled && (
|
||||
{ !disabled && (
|
||||
<ActionIcon className={classes.close} variant="subtle" onClick={remove}>
|
||||
<IconX />
|
||||
</ActionIcon>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useRef, useState, useCallback } from 'react';
|
||||
import { useRef, useEffect, useState, useCallback } from 'react';
|
||||
import { avatar } from '../constants/Icons'
|
||||
import { Topic, Card, Profile } from 'databag-client-sdk';
|
||||
import classes from './Message.module.css'
|
||||
@ -11,6 +11,7 @@ import type { MediaAsset } from '../conversation/Conversation';
|
||||
import { useMessage } from './useMessage.hook';
|
||||
import { IconForbid, IconTrash, IconShare, IconEdit, IconAlertSquareRounded, IconChevronLeft, IconChevronRight, IconFileAlert } from '@tabler/icons-react';
|
||||
import { useResizeDetector } from 'react-resize-detector';
|
||||
import DOMPurify from 'dompurify';
|
||||
|
||||
export function Message({ topic, card, profile, host }: { topic: Topic, card: Card | null, profile: Profile | null, host: boolean }) {
|
||||
const { state, actions } = useMessage();
|
||||
@ -21,6 +22,36 @@ export function Message({ topic, card, profile, host }: { topic: Topic, card: Ca
|
||||
const textStyle = textColor && textSize ? { color: textColor, fontSize: textSize } : textColor ? { color: textColor } : textSize ? { fontSize: textSize } : {}
|
||||
const logoUrl = profile ? profile.imageUrl : card ? card.imageUrl : avatar;
|
||||
const timestamp = actions.getTimestamp(created);
|
||||
const [message, setMessage] = useState(<p></p>);
|
||||
|
||||
useEffect(() => {
|
||||
const urlPattern = new RegExp('(https?:\\/\\/)?(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{2,256}\\.[a-z]{2,4}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)');
|
||||
const hostPattern = new RegExp('^https?:\\/\\/', 'i');
|
||||
|
||||
let plain = '';
|
||||
let clickable = [];
|
||||
const parsed = !text ? '' : DOMPurify.sanitize(text).split(' ');
|
||||
|
||||
if (parsed?.length > 0) {
|
||||
const words = parsed as string[];
|
||||
words.forEach((word, index) => {
|
||||
if (!!urlPattern.test(word)) {
|
||||
clickable.push(<span key={index}>{ plain }</span>);
|
||||
plain = '';
|
||||
const url = !!hostPattern.test(word) ? word : `https://${word}`;
|
||||
clickable.push(<a key={'link-'+index} target="_blank" rel="noopener noreferrer" href={url}>{ `${word} ` }</a>);
|
||||
}
|
||||
else {
|
||||
plain += `${word} `;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (plain) {
|
||||
clickable.push(<span key={parsed.length}>{ plain }</span>);
|
||||
}
|
||||
setMessage(<span>{ clickable }</span>)
|
||||
}, [text, locked]);
|
||||
|
||||
const [showScroll, setShowScroll] = useState(false);
|
||||
const onResize = useCallback(() => {
|
||||
@ -88,7 +119,7 @@ export function Message({ topic, card, profile, host }: { topic: Topic, card: Ca
|
||||
</div>
|
||||
{ !locked && status === 'confirmed' && text && (
|
||||
<div style={textStyle}>
|
||||
<span className={classes.text}>{ text }</span>
|
||||
<span className={classes.text}>{ message }</span>
|
||||
</div>
|
||||
)}
|
||||
{ !locked && status !== 'confirmed' && (
|
||||
|
@ -13,7 +13,7 @@ export function AudioAsset({ topicId, asset }: { topicId: string, asset: MediaAs
|
||||
const { label } = asset.encrypted || asset.audio || { label: '' };
|
||||
const [ loaded, setLoaded ] = useState(false);
|
||||
const [ playing, setPlaying ] = useState(false);
|
||||
const player = useRef();
|
||||
const player = useRef(null as HTMLVideoElement | null);
|
||||
|
||||
const show = () => {
|
||||
setShowModal(true);
|
||||
|
@ -23,7 +23,7 @@ export function useAudioAsset(topicId: string, asset: MediaAsset) {
|
||||
},
|
||||
loadAudio: async () => {
|
||||
const { focus } = app.state;
|
||||
const assetId = asset.audio ? asset.audio.hd : asset.encrypted ? asset.encrypted.parts : null;
|
||||
const assetId = asset.audio ? asset.audio.full : asset.encrypted ? asset.encrypted.parts : null;
|
||||
if (focus && assetId != null && !state.loading) {
|
||||
updateState({ loading: true, loadPercent: 0 });
|
||||
try {
|
||||
|
@ -7,7 +7,7 @@ import { MediaAsset } from '../../conversation/Conversation';
|
||||
export function useBinaryAsset(topicId: string, asset: MediaAsset) {
|
||||
const app = useContext(AppContext) as ContextType
|
||||
const [state, setState] = useState({
|
||||
dataUrl: null,
|
||||
dataUrl: '',
|
||||
loading: false,
|
||||
loadPercent: 0,
|
||||
})
|
||||
@ -23,7 +23,7 @@ export function useBinaryAsset(topicId: string, asset: MediaAsset) {
|
||||
},
|
||||
loadBinary: async () => {
|
||||
const { focus } = app.state;
|
||||
const assetId = asset.audio ? asset.audio.hd : asset.encrypted ? asset.encrypted.parts : null;
|
||||
const assetId = asset.binary ? asset.binary.data : asset.encrypted ? asset.encrypted.parts : null;
|
||||
if (focus && assetId != null && !state.loading) {
|
||||
updateState({ loading: true, loadPercent: 0 });
|
||||
try {
|
||||
|
@ -1293,6 +1293,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304"
|
||||
integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==
|
||||
|
||||
"@types/trusted-types@^2.0.7":
|
||||
version "2.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11"
|
||||
integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==
|
||||
|
||||
"@types/yargs-parser@*":
|
||||
version "21.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15"
|
||||
@ -2098,6 +2103,13 @@ domexception@^4.0.0:
|
||||
dependencies:
|
||||
webidl-conversions "^7.0.0"
|
||||
|
||||
dompurify@^3.2.3:
|
||||
version "3.2.3"
|
||||
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.2.3.tgz#05dd2175225324daabfca6603055a09b2382a4cd"
|
||||
integrity sha512-U1U5Hzc2MO0oW3DF+G9qYN0aT7atAou4AgI0XjWz061nyBPbdxkfdhfy5uMgGn6+oLFCfn44ZGbdDqCzVmlOWA==
|
||||
optionalDependencies:
|
||||
"@types/trusted-types" "^2.0.7"
|
||||
|
||||
dot-case@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751"
|
||||
|
Loading…
x
Reference in New Issue
Block a user