nicolium: nyaize post-emojified text only

Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
nicole mikołajczyk
2026-03-15 10:56:54 +01:00
parent 2f14d6d7c1
commit ab317f2a9c
2 changed files with 85 additions and 83 deletions

View File

@ -15,7 +15,6 @@ import { Link } from '@/components/link';
import Emojify from '@/features/emoji/emojify';
import { useSettings } from '@/stores/settings';
import { makeEmojiMap } from '@/utils/normalizers';
import nyaize from '@/utils/nyaize';
import Purify from '@/utils/url-purify';
import HoverAccountWrapper from '../accounts/hover-account-wrapper';
@ -197,10 +196,8 @@ function parseContent(
let hasSuspiciousUrl = false;
const transformText = (data: string, key?: React.Key) => {
const text = speakAsCat ? nyaize(data) : data;
return <Emojify key={key} text={text} emojis={emojiMap} />;
const transformText = (text: string, key?: React.Key) => {
return <Emojify key={key} text={text} emojis={emojiMap} nyaize={speakAsCat} />;
};
const options: HTMLReactParserOptions = {

View File

@ -4,6 +4,7 @@ import React from 'react';
import Emoji from '@/components/ui/emoji';
import { useSettings } from '@/stores/settings';
import { makeEmojiMap } from '@/utils/normalizers';
import nyaize from '@/utils/nyaize';
import { joinPublicPath } from '@/utils/static';
import unicodeMapping from './mapping';
@ -15,9 +16,10 @@ import type { CustomEmoji } from 'pl-api';
interface IMaybeEmoji {
text: string;
emojis: Record<string, CustomEmoji>;
nyaize: boolean;
}
const MaybeEmoji: React.FC<IMaybeEmoji> = ({ text, emojis }) => {
const MaybeEmoji: React.FC<IMaybeEmoji> = ({ text, emojis, nyaize: shouldNyaize }) => {
if (text.length < 3) return text;
if (text in emojis) {
const emoji = emojis[text];
@ -28,100 +30,103 @@ const MaybeEmoji: React.FC<IMaybeEmoji> = ({ text, emojis }) => {
}
}
return text;
return shouldNyaize ? nyaize(text) : text;
};
interface IEmojify {
text: string;
emojis?: Array<CustomEmoji> | Record<string, CustomEmoji>;
nyaize?: boolean;
}
const Emojify: React.FC<IEmojify> = React.memo(({ text, emojis = {} }) => {
const { disableUserProvidedMedia, systemEmojiFont } = useSettings();
const Emojify: React.FC<IEmojify> = React.memo(
({ text, emojis = {}, nyaize: shouldNyaize = false }) => {
const { disableUserProvidedMedia, systemEmojiFont } = useSettings();
if (Array.isArray(emojis)) emojis = makeEmojiMap(emojis);
if (Array.isArray(emojis)) emojis = makeEmojiMap(emojis);
const nodes = [];
const nodes = [];
let stack = '';
let open = false;
let stack = '';
let open = false;
const clearStack = () => {
if (stack.length) nodes.push(stack);
open = false;
stack = '';
};
const clearStack = () => {
if (stack.length) nodes.push(shouldNyaize ? nyaize(stack) : stack);
open = false;
stack = '';
};
const splitText = split(text);
const splitText = split(text);
for (const index in splitText) {
let c = splitText[index];
for (const index in splitText) {
let c = splitText[index];
// convert FE0E selector to FE0F so it can be found in unimap
if (c.codePointAt(c.length - 1) === 65038) {
c = c.slice(0, -1) + String.fromCodePoint(65039);
// convert FE0E selector to FE0F so it can be found in unimap
if (c.codePointAt(c.length - 1) === 65038) {
c = c.slice(0, -1) + String.fromCodePoint(65039);
}
// unqualified emojis aren't in emoji-mart's mappings so we just add FEOF
const unqualified = c + String.fromCodePoint(65039);
if (!systemEmojiFont && c in unicodeMapping) {
clearStack();
const { unified, shortcode } = unicodeMapping[c];
nodes.push(
<img
key={index}
draggable={false}
className='emojione ⁂-emoji'
alt={c}
title={`:${shortcode}:`}
src={joinPublicPath(`packs/emoji/${unified}.svg`)}
/>,
);
} else if (!systemEmojiFont && unqualified in unicodeMapping) {
clearStack();
const { unified, shortcode } = unicodeMapping[unqualified];
nodes.push(
<img
key={index}
draggable={false}
className='emojione ⁂-emoji'
alt={unqualified}
title={`:${shortcode}:`}
src={joinPublicPath(`packs/emoji/${unified}.svg`)}
/>,
);
} else if (!disableUserProvidedMedia && c === ':') {
if (!open) {
clearStack();
}
stack += ':';
// we see another : we convert it and clear the stack buffer
if (open) {
nodes.push(<MaybeEmoji key={index} text={stack} emojis={emojis} nyaize={shouldNyaize} />);
stack = '';
}
open = !open;
} else {
stack += c;
if (open && !validEmojiChar(c)) {
clearStack();
}
}
}
// unqualified emojis aren't in emoji-mart's mappings so we just add FEOF
const unqualified = c + String.fromCodePoint(65039);
if (stack.length) nodes.push(shouldNyaize ? nyaize(stack) : stack);
if (!systemEmojiFont && c in unicodeMapping) {
clearStack();
const { unified, shortcode } = unicodeMapping[c];
nodes.push(
<img
key={index}
draggable={false}
className='emojione ⁂-emoji'
alt={c}
title={`:${shortcode}:`}
src={joinPublicPath(`packs/emoji/${unified}.svg`)}
/>,
);
} else if (!systemEmojiFont && unqualified in unicodeMapping) {
clearStack();
const { unified, shortcode } = unicodeMapping[unqualified];
nodes.push(
<img
key={index}
draggable={false}
className='emojione ⁂-emoji'
alt={unqualified}
title={`:${shortcode}:`}
src={joinPublicPath(`packs/emoji/${unified}.svg`)}
/>,
);
} else if (!disableUserProvidedMedia && c === ':') {
if (!open) {
clearStack();
}
stack += ':';
// we see another : we convert it and clear the stack buffer
if (open) {
nodes.push(<MaybeEmoji key={index} text={stack} emojis={emojis} />);
stack = '';
}
open = !open;
} else {
stack += c;
if (open && !validEmojiChar(c)) {
clearStack();
}
}
}
if (stack.length) nodes.push(stack);
return nodes;
});
return nodes;
},
);
Emojify.displayName = 'Emojify';