diff --git a/src/components/compose.jsx b/src/components/compose.jsx
index d4918653..12f9f5f7 100644
--- a/src/components/compose.jsx
+++ b/src/components/compose.jsx
@@ -31,6 +31,7 @@ import { api, getPreferences } from '../utils/api';
import { langDetector } from '../utils/browser-translator';
import db from '../utils/db';
import emojifyText from '../utils/emojify-text';
+import escapeHTML from '../utils/escape-html';
import getDomain from '../utils/get-domain';
import i18nDuration from '../utils/i18n-duration';
import isRTL from '../utils/is-rtl';
@@ -160,14 +161,6 @@ const SCAN_RE = new RegExp(
);
const segmenter = new Intl.Segmenter();
-function escapeHTML(text) {
- return text
- .replace(/&/g, '&')
- .replace(//g, '>')
- .replace(/"/g, '"')
- .replace(/'/g, ''');
-}
function highlightText(text, { maxCharacters = Infinity }) {
// Exceeded characters limit
const { composerCharacterCount } = states;
diff --git a/src/components/icon.jsx b/src/components/icon.jsx
index 469da972..d0d6d36e 100644
--- a/src/components/icon.jsx
+++ b/src/components/icon.jsx
@@ -1,6 +1,8 @@
import moize from 'moize';
import { useEffect, useRef, useState } from 'preact/hooks';
+import escapeHTML from '../utils/escape-html';
+
import { ICONS } from './ICONS';
const SIZES = {
@@ -15,12 +17,20 @@ const SIZES = {
const ICONDATA = {};
// Memoize the dangerouslySetInnerHTML of the SVGs
+const INVALID_ID_CHARS_REGEX = /[^a-zA-Z0-9]/g;
const SVGICon = moize(
- function ({ width, height, body, rotate, flip }) {
+ function ({ icon, title, width, height, body, rotate, flip }) {
+ const titleID = title?.replace(INVALID_ID_CHARS_REGEX, '-') || '';
+ const id = `icon-${icon}-${titleID}`;
+ const html = title
+ ? `
${escapeHTML(title)}${body}`
+ : body;
return (