mirror of
https://github.com/cheeaun/phanpy.git
synced 2025-02-25 09:18:51 +01:00
EmojiText component replacing dangerouslySetInnerHTML
This commit is contained in:
parent
d2826085e1
commit
3b3e0e6fde
8 changed files with 69 additions and 46 deletions
|
@ -2,11 +2,11 @@ import './account-block.css';
|
|||
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import emojifyText from '../utils/emojify-text';
|
||||
import niceDateTime from '../utils/nice-date-time';
|
||||
import states from '../utils/states';
|
||||
|
||||
import Avatar from './avatar';
|
||||
import EmojiText from './emoji-text';
|
||||
|
||||
function AccountBlock({
|
||||
skeleton,
|
||||
|
@ -46,7 +46,6 @@ function AccountBlock({
|
|||
lastStatusAt,
|
||||
bot,
|
||||
} = account;
|
||||
const displayNameWithEmoji = emojifyText(displayName, emojis);
|
||||
const [_, acct1, acct2] = acct.match(/([^@]+)(@.+)/i) || [, acct];
|
||||
|
||||
return (
|
||||
|
@ -72,11 +71,9 @@ function AccountBlock({
|
|||
<Avatar url={avatar} size={avatarSize} squircle={bot} />
|
||||
<span>
|
||||
{displayName ? (
|
||||
<b
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: displayNameWithEmoji,
|
||||
}}
|
||||
/>
|
||||
<b>
|
||||
<EmojiText text={displayName} emojis={emojis} />
|
||||
</b>
|
||||
) : (
|
||||
<b>{username}</b>
|
||||
)}
|
||||
|
|
|
@ -4,7 +4,6 @@ import { Menu, MenuDivider, MenuItem, SubMenu } from '@szhsin/react-menu';
|
|||
import { useEffect, useReducer, useRef, useState } from 'preact/hooks';
|
||||
|
||||
import { api } from '../utils/api';
|
||||
import emojifyText from '../utils/emojify-text';
|
||||
import enhanceContent from '../utils/enhance-content';
|
||||
import getHTMLText from '../utils/getHTMLText';
|
||||
import handleContentLinks from '../utils/handle-content-links';
|
||||
|
@ -16,6 +15,7 @@ import store from '../utils/store';
|
|||
|
||||
import AccountBlock from './account-block';
|
||||
import Avatar from './avatar';
|
||||
import EmojiText from './emoji-text';
|
||||
import Icon from './icon';
|
||||
import Link from './link';
|
||||
import ListAddEdit from './list-add-edit';
|
||||
|
@ -301,11 +301,7 @@ function AccountInfo({
|
|||
key={name}
|
||||
>
|
||||
<b>
|
||||
<span
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: emojifyText(name, emojis),
|
||||
}}
|
||||
/>{' '}
|
||||
<EmojiText text={name} emojis={emojis} />{' '}
|
||||
{!!verifiedAt && <Icon icon="check-circle" size="s" />}
|
||||
</b>
|
||||
<p
|
||||
|
|
42
src/components/emoji-text.jsx
Normal file
42
src/components/emoji-text.jsx
Normal file
|
@ -0,0 +1,42 @@
|
|||
function EmojiText({ text, emojis }) {
|
||||
if (!text) return '';
|
||||
if (!emojis?.length) return text;
|
||||
if (text.indexOf(':') === -1) return text;
|
||||
|
||||
const components = [];
|
||||
let lastIndex = 0;
|
||||
|
||||
emojis.forEach((shortcodeObj) => {
|
||||
const { shortcode, staticUrl, url } = shortcodeObj;
|
||||
const regex = new RegExp(`:${shortcode}:`, 'g');
|
||||
let match;
|
||||
|
||||
while ((match = regex.exec(text))) {
|
||||
const beforeText = text.substring(lastIndex, match.index);
|
||||
if (beforeText) {
|
||||
components.push(beforeText);
|
||||
}
|
||||
components.push(
|
||||
<img
|
||||
src={url}
|
||||
alt={shortcode}
|
||||
class="shortcode-emoji emoji"
|
||||
width="12"
|
||||
height="12"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
/>,
|
||||
);
|
||||
lastIndex = match.index + match[0].length;
|
||||
}
|
||||
});
|
||||
|
||||
const afterText = text.substring(lastIndex);
|
||||
if (afterText) {
|
||||
components.push(afterText);
|
||||
}
|
||||
|
||||
return components;
|
||||
}
|
||||
|
||||
export default EmojiText;
|
|
@ -1,9 +1,9 @@
|
|||
import './name-text.css';
|
||||
|
||||
import emojifyText from '../utils/emojify-text';
|
||||
import states from '../utils/states';
|
||||
|
||||
import Avatar from './avatar';
|
||||
import EmojiText from './emoji-text';
|
||||
|
||||
function NameText({
|
||||
account,
|
||||
|
@ -18,8 +18,6 @@ function NameText({
|
|||
account;
|
||||
let { username } = account;
|
||||
|
||||
const displayNameWithEmoji = emojifyText(displayName, emojis);
|
||||
|
||||
const trimmedUsername = username.toLowerCase().trim();
|
||||
const trimmedDisplayName = (displayName || '').toLowerCase().trim();
|
||||
const shortenedDisplayName = trimmedDisplayName
|
||||
|
@ -58,11 +56,9 @@ function NameText({
|
|||
)}
|
||||
{displayName && !short ? (
|
||||
<>
|
||||
<b
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: displayNameWithEmoji,
|
||||
}}
|
||||
/>
|
||||
<b>
|
||||
<EmojiText text={displayName} emojis={emojis} />
|
||||
</b>
|
||||
{!showAcct && username && (
|
||||
<>
|
||||
{' '}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { useEffect, useRef, useState } from 'preact/hooks';
|
||||
|
||||
import emojifyText from '../utils/emojify-text';
|
||||
import shortenNumber from '../utils/shorten-number';
|
||||
|
||||
import EmojiText from './emoji-text';
|
||||
import Icon from './icon';
|
||||
import RelativeTime from './relative-time';
|
||||
|
||||
|
@ -112,11 +112,9 @@ export default function Poll({
|
|||
}}
|
||||
>
|
||||
<div class="poll-option-title">
|
||||
<span
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: emojifyText(title, emojis),
|
||||
}}
|
||||
/>
|
||||
<span>
|
||||
<EmojiText text={title} emojis={emojis} />
|
||||
</span>
|
||||
{voted && ownVotes.includes(i) && (
|
||||
<>
|
||||
{' '}
|
||||
|
@ -179,12 +177,9 @@ export default function Poll({
|
|||
disabled={uiState === 'loading'}
|
||||
readOnly={readOnly}
|
||||
/>
|
||||
<span
|
||||
class="poll-option-title"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: emojifyText(title, emojis),
|
||||
}}
|
||||
/>
|
||||
<span class="poll-option-title">
|
||||
<EmojiText text={title} emojis={emojis} />
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -26,12 +26,12 @@ import { useSnapshot } from 'valtio';
|
|||
import { snapshot } from 'valtio/vanilla';
|
||||
|
||||
import AccountBlock from '../components/account-block';
|
||||
import EmojiText from '../components/emoji-text';
|
||||
import Loader from '../components/loader';
|
||||
import Modal from '../components/modal';
|
||||
import NameText from '../components/name-text';
|
||||
import Poll from '../components/poll';
|
||||
import { api } from '../utils/api';
|
||||
import emojifyText from '../utils/emojify-text';
|
||||
import enhanceContent from '../utils/enhance-content';
|
||||
import getTranslateTargetLanguage from '../utils/get-translate-target-language';
|
||||
import getHTMLText from '../utils/getHTMLText';
|
||||
|
@ -926,11 +926,9 @@ function Status({
|
|||
ref={spoilerContentRef}
|
||||
data-read-more={readMoreText}
|
||||
>
|
||||
<p
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: emojifyText(spoilerText, emojis),
|
||||
}}
|
||||
/>
|
||||
<p>
|
||||
<EmojiText text={spoilerText} emojis={emojis} />
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
class={`light spoiler ${showSpoiler ? 'spoiling' : ''}`}
|
||||
|
|
|
@ -4,12 +4,12 @@ import { useParams, useSearchParams } from 'react-router-dom';
|
|||
import { useSnapshot } from 'valtio';
|
||||
|
||||
import AccountInfo from '../components/account-info';
|
||||
import EmojiText from '../components/emoji-text';
|
||||
import Icon from '../components/icon';
|
||||
import Link from '../components/link';
|
||||
import Menu2 from '../components/menu2';
|
||||
import Timeline from '../components/timeline';
|
||||
import { api } from '../utils/api';
|
||||
import emojifyText from '../utils/emojify-text';
|
||||
import showToast from '../utils/show-toast';
|
||||
import states from '../utils/states';
|
||||
import { saveStatus } from '../utils/states';
|
||||
|
@ -236,11 +236,9 @@ function AccountStatuses() {
|
|||
// };
|
||||
// }}
|
||||
>
|
||||
<b
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: emojifyText(displayName, emojis),
|
||||
}}
|
||||
/>
|
||||
<b>
|
||||
<EmojiText text={displayName} emojis={emojis} />
|
||||
</b>
|
||||
<div>
|
||||
<span>@{acct}</span>
|
||||
</div>
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
function emojifyText(text, emojis = []) {
|
||||
if (!text) return '';
|
||||
if (!emojis.length) return text;
|
||||
if (text.indexOf(':') === -1) return text;
|
||||
// Replace shortcodes in text with emoji
|
||||
// emojis = [{ shortcode: 'smile', url: 'https://example.com/emoji.png' }]
|
||||
emojis.forEach((emoji) => {
|
||||
const { shortcode, staticUrl, url } = emoji;
|
||||
text = text.replace(
|
||||
new RegExp(`:${shortcode}:`, 'g'),
|
||||
`<img class="shortcode-emoji emoji" src="${url}" alt=":${shortcode}:" width="12" height="12" loading="lazy" />`,
|
||||
`<img class="shortcode-emoji emoji" src="${url}" alt=":${shortcode}:" width="12" height="12" loading="lazy" decoding="async" />`,
|
||||
);
|
||||
});
|
||||
// console.log(text, emojis);
|
||||
|
|
Loading…
Reference in a new issue