diff --git a/app/mobile/src/session/conversation/topicItem/TopicItem.jsx b/app/mobile/src/session/conversation/topicItem/TopicItem.jsx index d590eaf7..46bccc1c 100644 --- a/app/mobile/src/session/conversation/topicItem/TopicItem.jsx +++ b/app/mobile/src/session/conversation/topicItem/TopicItem.jsx @@ -162,8 +162,8 @@ export function TopicItem({ item, focused, focus, hosting, remove, update, block )} - { state.message && !state.sealed && ( - { state.message } + { state.clickable && !state.sealed && ( + { state.clickable } )} { state.sealed && ( sealed message diff --git a/app/mobile/src/session/conversation/topicItem/useTopicItem.hook.js b/app/mobile/src/session/conversation/topicItem/useTopicItem.hook.js index 7be7e10a..d3b5b4a1 100644 --- a/app/mobile/src/session/conversation/topicItem/useTopicItem.hook.js +++ b/app/mobile/src/session/conversation/topicItem/useTopicItem.hook.js @@ -20,6 +20,7 @@ export function useTopicItem(item, hosting, remove, contentKey) { logo: null, timestamp: null, message: null, + clickable: null, carousel: false, carouselIndex: 0, width: null, @@ -92,12 +93,13 @@ export function useTopicItem(item, hosting, remove, contentKey) { } } - let parsed, sealed, message, assets, fontSize, fontColor; + let parsed, sealed, message, clickable, assets, fontSize, fontColor; if (dataType === 'superbasictopic') { try { sealed = false; parsed = JSON.parse(data); - message = clickableText(parsed.text); + message = parsed?.text; + clickable = clickableText(parsed.text); assets = parsed.assets; if (parsed.textSize === 'small') { fontSize = 10; @@ -140,7 +142,8 @@ export function useTopicItem(item, hosting, remove, contentKey) { if (unsealed) { sealed = false; parsed = unsealed.message; - message = clickableText(parsed?.text); + message = parsed?.text; + clickable = clickableText(parsed?.text); if (parsed?.textSize === 'small') { fontSize = 10; } @@ -179,7 +182,7 @@ export function useTopicItem(item, hosting, remove, contentKey) { const editable = guid === identity?.guid && parsed; const deletable = editable || hosting; - updateState({ logo, name, nameSet, known, sealed, message, fontSize, fontColor, timestamp, transform, status, assets, deletable, editable, editData: parsed, editMessage: message, editType: dataType }); + updateState({ logo, name, nameSet, known, sealed, message, clickable, fontSize, fontColor, timestamp, transform, status, assets, deletable, editable, editData: parsed, editMessage: message, editType: dataType }); }, [conversation.state, card.state, account.state, item, contentKey]); const unsealTopic = async (topicId, revision, topicDetail) => { @@ -206,7 +209,7 @@ export function useTopicItem(item, hosting, remove, contentKey) { let clickable = []; let group = ''; - const words = text == null ? '' : text.split(' '); + const words = text == null ? [''] : text.split(' '); words.forEach((word, index) => { if (!!pattern.test(word)) { clickable.push({ group }); diff --git a/net/web/src/session/conversation/topicItem/TopicItem.jsx b/net/web/src/session/conversation/topicItem/TopicItem.jsx index 4ab16f06..0136eb9b 100644 --- a/net/web/src/session/conversation/topicItem/TopicItem.jsx +++ b/net/web/src/session/conversation/topicItem/TopicItem.jsx @@ -7,7 +7,6 @@ import { Space, Skeleton, Button, Modal, Input } from 'antd'; import { ExclamationCircleOutlined, DeleteOutlined, EditOutlined, FireOutlined, PictureOutlined } from '@ant-design/icons'; import { Carousel } from 'carousel/Carousel'; import { useTopicItem } from './useTopicItem.hook'; -import * as DOMPurify from 'dompurify'; export function TopicItem({ host, sealed, topic, update, remove }) { @@ -124,7 +123,7 @@ export function TopicItem({ host, sealed, topic, update, remove }) { )} { !sealed && !state.editing && (
-
+
{ topic.clickable }
)} { state.editing && ( diff --git a/net/web/src/session/conversation/useConversation.hook.js b/net/web/src/session/conversation/useConversation.hook.js index 58f84f89..ff1608b4 100644 --- a/net/web/src/session/conversation/useConversation.hook.js +++ b/net/web/src/session/conversation/useConversation.hook.js @@ -9,6 +9,7 @@ import { ProfileContext } from 'context/ProfileContext'; import { isUnsealed, getChannelSeals, getContentKey, encryptTopicSubject } from 'context/sealUtil'; import { decryptTopicSubject } from 'context/sealUtil'; import { getProfileByGuid } from 'context/cardUtil'; +import * as DOMPurify from 'dompurify'; export function useConversation(cardId, channelId) { @@ -140,17 +141,21 @@ export function useConversation(cardId, channelId) { '(\\?[;&a-z\\d%_.~+=-]*)?'+ // query string '(\\#[-a-z\\d_]*)?$','i'); // fragment locator - let clickable = ''; - const words = text == null ? '' : text.split(' '); - words.forEach(word => { + let group = ''; + let clickable = []; + const words = text == null ? '' : DOMPurify.sanitize(text).split(' '); + words.forEach((word, index) => { if (!!pattern.test(word)) { - clickable += `${word} `; + clickable.push({ group }); + group = ''; + clickable.push({ `${word} ` }); } else { - clickable += `${word} `; + group += `${word} `; } }) - return `

${clickable}

`; + clickable.push({ group }); + return

{ clickable }

; }; const syncTopic = (item, value) => { @@ -210,14 +215,16 @@ export function useConversation(cardId, channelId) { if (detail.dataType === 'superbasictopic') { const message = JSON.parse(detail.data); item.assets = message.assets; - item.text = clickableText(message.text); + item.text = message.text; + item.clickable = clickableText(message.text); item.textColor = message.textColor ? message.textColor : '#444444'; item.textSize = message.textSize ? message.textSize : 14; } if (detail.dataType === 'sealedtopic' && state.contentKey) { const subject = decryptTopicSubject(detail.data, state.contentKey); item.assets = subject.message.assets; - item.text = clickableText(subject.message.text); + item.text = subject.message.text; + item.clickable = clickableText(subject.message.text); item.textColor = subject.message.textColor ? subject.message.textColor : '#444444'; item.textSize = subject.message.textSize ? subject.message.textSize : 14; }