Fix jumbo emoji sizing being overridden by folds Text inherit.
Render emoji-only messages without folds Text, force 100px on the container and emoticon elements, and pass jumbo through scaleSystemEmoji for plain-text bodies.
This commit is contained in:
@@ -100,7 +100,7 @@ function RenderEmoticonElement({
|
|||||||
>
|
>
|
||||||
{element.key.startsWith('mxc://') ? (
|
{element.key.startsWith('mxc://') ? (
|
||||||
<img
|
<img
|
||||||
className={css.EmoticonImg}
|
className={css.EmoticonImg()}
|
||||||
src={mxcUrlToHttp(mx, element.key, useAuthentication) ?? element.key}
|
src={mxcUrlToHttp(mx, element.key, useAuthentication) ?? element.key}
|
||||||
alt={element.shortcode}
|
alt={element.shortcode}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ export function BrokenContent() {
|
|||||||
type RenderBodyProps = {
|
type RenderBodyProps = {
|
||||||
body: string;
|
body: string;
|
||||||
customBody?: string;
|
customBody?: string;
|
||||||
|
jumbo?: boolean;
|
||||||
};
|
};
|
||||||
type MTextProps = {
|
type MTextProps = {
|
||||||
edited?: boolean;
|
edited?: boolean;
|
||||||
@@ -87,17 +88,19 @@ export function MText({ edited, content, renderBody, renderUrlsPreview, style }:
|
|||||||
: undefined;
|
: undefined;
|
||||||
const urlsMatch = renderUrlsPreview && trimmedBody.match(URL_REG);
|
const urlsMatch = renderUrlsPreview && trimmedBody.match(URL_REG);
|
||||||
const urls = urlsMatch ? [...new Set(urlsMatch)] : undefined;
|
const urls = urlsMatch ? [...new Set(urlsMatch)] : undefined;
|
||||||
|
const jumbo = isJumboEmoji(trimmedBody, trimmedCustomBody);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<MessageTextBody
|
<MessageTextBody
|
||||||
preWrap={typeof customBody !== 'string'}
|
preWrap={typeof customBody !== 'string'}
|
||||||
jumboEmoji={isJumboEmoji(trimmedBody, trimmedCustomBody)}
|
jumboEmoji={jumbo}
|
||||||
style={style}
|
style={style}
|
||||||
>
|
>
|
||||||
{renderBody({
|
{renderBody({
|
||||||
body: trimmedBody,
|
body: trimmedBody,
|
||||||
customBody: trimmedCustomBody,
|
customBody: trimmedCustomBody,
|
||||||
|
jumbo,
|
||||||
})}
|
})}
|
||||||
{edited && <MessageEditedContent />}
|
{edited && <MessageEditedContent />}
|
||||||
</MessageTextBody>
|
</MessageTextBody>
|
||||||
@@ -165,17 +168,19 @@ export function MNotice({ edited, content, renderBody, renderUrlsPreview }: MNot
|
|||||||
: undefined;
|
: undefined;
|
||||||
const urlsMatch = renderUrlsPreview && trimmedBody.match(URL_REG);
|
const urlsMatch = renderUrlsPreview && trimmedBody.match(URL_REG);
|
||||||
const urls = urlsMatch ? [...new Set(urlsMatch)] : undefined;
|
const urls = urlsMatch ? [...new Set(urlsMatch)] : undefined;
|
||||||
|
const jumbo = isJumboEmoji(trimmedBody, trimmedCustomBody);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<MessageTextBody
|
<MessageTextBody
|
||||||
notice
|
notice
|
||||||
preWrap={typeof customBody !== 'string'}
|
preWrap={typeof customBody !== 'string'}
|
||||||
jumboEmoji={isJumboEmoji(trimmedBody, trimmedCustomBody)}
|
jumboEmoji={jumbo}
|
||||||
>
|
>
|
||||||
{renderBody({
|
{renderBody({
|
||||||
body: trimmedBody,
|
body: trimmedBody,
|
||||||
customBody: trimmedCustomBody,
|
customBody: trimmedCustomBody,
|
||||||
|
jumbo,
|
||||||
})}
|
})}
|
||||||
{edited && <MessageEditedContent />}
|
{edited && <MessageEditedContent />}
|
||||||
</MessageTextBody>
|
</MessageTextBody>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { highlightText, scaleSystemEmoji } from '../../plugins/react-custom-html
|
|||||||
type RenderBodyProps = {
|
type RenderBodyProps = {
|
||||||
body: string;
|
body: string;
|
||||||
customBody?: string;
|
customBody?: string;
|
||||||
|
jumbo?: boolean;
|
||||||
|
|
||||||
highlightRegex?: RegExp;
|
highlightRegex?: RegExp;
|
||||||
htmlReactParserOptions: HTMLReactParserOptions;
|
htmlReactParserOptions: HTMLReactParserOptions;
|
||||||
@@ -17,6 +18,7 @@ type RenderBodyProps = {
|
|||||||
export function RenderBody({
|
export function RenderBody({
|
||||||
body,
|
body,
|
||||||
customBody,
|
customBody,
|
||||||
|
jumbo = false,
|
||||||
highlightRegex,
|
highlightRegex,
|
||||||
htmlReactParserOptions,
|
htmlReactParserOptions,
|
||||||
linkifyOpts,
|
linkifyOpts,
|
||||||
@@ -29,8 +31,8 @@ export function RenderBody({
|
|||||||
return (
|
return (
|
||||||
<Linkify options={linkifyOpts}>
|
<Linkify options={linkifyOpts}>
|
||||||
{highlightRegex
|
{highlightRegex
|
||||||
? highlightText(highlightRegex, scaleSystemEmoji(body))
|
? highlightText(highlightRegex, scaleSystemEmoji(body, jumbo))
|
||||||
: scaleSystemEmoji(body)}
|
: scaleSystemEmoji(body, jumbo)}
|
||||||
</Linkify>
|
</Linkify>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,16 +29,35 @@ export const UsernameBold = as<'b'>(({ as: AsUsernameBold = 'b', className, ...p
|
|||||||
));
|
));
|
||||||
|
|
||||||
export const MessageTextBody = as<'div', css.MessageTextBodyVariants & { notice?: boolean }>(
|
export const MessageTextBody = as<'div', css.MessageTextBodyVariants & { notice?: boolean }>(
|
||||||
({ as: asComp = 'div', className, preWrap, jumboEmoji, emote, notice, ...props }, ref) => (
|
({ as: asComp = 'div', className, preWrap, jumboEmoji, emote, notice, ...props }, ref) => {
|
||||||
<Text
|
const bodyClass = classNames(
|
||||||
as={asComp}
|
css.MessageTextBody({ preWrap, jumboEmoji, emote }),
|
||||||
size={jumboEmoji ? 'Inherit' : 'T400'}
|
className
|
||||||
priority={notice ? '300' : '400'}
|
);
|
||||||
className={classNames(css.MessageTextBody({ preWrap, jumboEmoji, emote }), className)}
|
|
||||||
data-allow-text-selection="true"
|
// Skip folds Text for jumbo — its DefaultReset sets font-size:inherit and wins the cascade.
|
||||||
data-jumbo-emoji={jumboEmoji ? 'true' : undefined}
|
if (jumboEmoji) {
|
||||||
{...props}
|
return (
|
||||||
ref={ref}
|
<AsComp
|
||||||
/>
|
className={bodyClass}
|
||||||
)
|
data-allow-text-selection="true"
|
||||||
|
data-jumbo-emoji="true"
|
||||||
|
{...props}
|
||||||
|
ref={ref}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Text
|
||||||
|
as={asComp}
|
||||||
|
size="T400"
|
||||||
|
priority={notice ? '300' : '400'}
|
||||||
|
className={bodyClass}
|
||||||
|
data-allow-text-selection="true"
|
||||||
|
{...props}
|
||||||
|
ref={ref}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -248,15 +248,26 @@ export const MessageTextBody = recipe({
|
|||||||
|
|
||||||
export type MessageTextBodyVariants = RecipeVariants<typeof MessageTextBody>;
|
export type MessageTextBodyVariants = RecipeVariants<typeof MessageTextBody>;
|
||||||
|
|
||||||
// Prefer data-attr so sizing wins even if recipe class order fights folds Text sizes.
|
/** Jumbo emoji-only messages: 100×100px (system unicode + custom mx-emoticons). */
|
||||||
const jumboEmojiSelector = '[data-jumbo-emoji="true"]';
|
const jumboEmojiSelector = '[data-jumbo-emoji="true"]';
|
||||||
|
|
||||||
|
globalStyle(jumboEmojiSelector, {
|
||||||
|
fontSize: `${toRem(100)} !important`,
|
||||||
|
lineHeight: `${toRem(100)} !important`,
|
||||||
|
});
|
||||||
|
|
||||||
globalStyle(`${jumboEmojiSelector} .${htmlCss.EmoticonBase}`, {
|
globalStyle(`${jumboEmojiSelector} .${htmlCss.EmoticonBase}`, {
|
||||||
height: `${toRem(100)} !important`,
|
height: `${toRem(100)} !important`,
|
||||||
padding: '0 !important',
|
padding: '0 !important',
|
||||||
verticalAlign: 'bottom',
|
verticalAlign: 'bottom',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
globalStyle(`${jumboEmojiSelector} .${htmlCss.EmoticonBaseJumbo}`, {
|
||||||
|
height: `${toRem(100)} !important`,
|
||||||
|
padding: '0 !important',
|
||||||
|
verticalAlign: 'bottom',
|
||||||
|
});
|
||||||
|
|
||||||
globalStyle(`${jumboEmojiSelector} .${htmlCss.Emoticon.classNames.base}`, {
|
globalStyle(`${jumboEmojiSelector} .${htmlCss.Emoticon.classNames.base}`, {
|
||||||
fontSize: `${toRem(100)} !important`,
|
fontSize: `${toRem(100)} !important`,
|
||||||
height: `${toRem(100)} !important`,
|
height: `${toRem(100)} !important`,
|
||||||
@@ -265,7 +276,23 @@ globalStyle(`${jumboEmojiSelector} .${htmlCss.Emoticon.classNames.base}`, {
|
|||||||
top: '0 !important',
|
top: '0 !important',
|
||||||
});
|
});
|
||||||
|
|
||||||
globalStyle(`${jumboEmojiSelector} .${htmlCss.EmoticonImg}`, {
|
globalStyle(`${jumboEmojiSelector} .${htmlCss.Emoticon.classNames.variants.jumbo.true}`, {
|
||||||
|
fontSize: `${toRem(100)} !important`,
|
||||||
|
height: `${toRem(100)} !important`,
|
||||||
|
minWidth: `${toRem(100)} !important`,
|
||||||
|
lineHeight: `${toRem(100)} !important`,
|
||||||
|
top: '0 !important',
|
||||||
|
});
|
||||||
|
|
||||||
|
globalStyle(`${jumboEmojiSelector} .${htmlCss.EmoticonImg.classNames.base}`, {
|
||||||
|
height: `${toRem(100)} !important`,
|
||||||
|
width: `${toRem(100)} !important`,
|
||||||
|
maxHeight: `${toRem(100)} !important`,
|
||||||
|
maxWidth: `${toRem(100)} !important`,
|
||||||
|
objectFit: 'contain',
|
||||||
|
});
|
||||||
|
|
||||||
|
globalStyle(`${jumboEmojiSelector} .${htmlCss.EmoticonImg.classNames.variants.jumbo.true}`, {
|
||||||
height: `${toRem(100)} !important`,
|
height: `${toRem(100)} !important`,
|
||||||
width: `${toRem(100)} !important`,
|
width: `${toRem(100)} !important`,
|
||||||
maxHeight: `${toRem(100)} !important`,
|
maxHeight: `${toRem(100)} !important`,
|
||||||
|
|||||||
@@ -184,13 +184,19 @@ export const factoryRenderLinkifyWithMention = (
|
|||||||
return render;
|
return render;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const scaleSystemEmoji = (text: string): (string | JSX.Element)[] =>
|
export const scaleSystemEmoji = (text: string, jumbo = false): (string | JSX.Element)[] =>
|
||||||
findAndReplace(
|
findAndReplace(
|
||||||
text,
|
text,
|
||||||
EMOJI_REG_G,
|
EMOJI_REG_G,
|
||||||
(match, pushIndex) => (
|
(match, pushIndex) => (
|
||||||
<span key={`scaleSystemEmoji-${pushIndex}`} className={css.EmoticonBase}>
|
<span
|
||||||
<span className={css.Emoticon()} title={getShortcodeFor(getHexcodeForEmoji(match[0]))}>
|
key={`scaleSystemEmoji-${pushIndex}`}
|
||||||
|
className={jumbo ? css.EmoticonBaseJumbo : css.EmoticonBase}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className={css.Emoticon({ jumbo })}
|
||||||
|
title={getShortcodeFor(getHexcodeForEmoji(match[0]))}
|
||||||
|
>
|
||||||
{match[0]}
|
{match[0]}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
@@ -557,7 +563,7 @@ export const getReactCustomHtmlParser = (
|
|||||||
return (
|
return (
|
||||||
<span className={css.EmoticonBase}>
|
<span className={css.EmoticonBase}>
|
||||||
<span className={css.Emoticon()}>
|
<span className={css.Emoticon()}>
|
||||||
<AuthenticatedImg {...imgProps} className={css.EmoticonImg} src={htmlSrc} />
|
<AuthenticatedImg {...imgProps} className={css.EmoticonImg()} src={htmlSrc} />
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -261,6 +261,15 @@ export const EmoticonBase = style([
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
export const EmoticonBaseJumbo = style([
|
||||||
|
EmoticonBase,
|
||||||
|
{
|
||||||
|
height: toRem(100),
|
||||||
|
padding: 0,
|
||||||
|
verticalAlign: 'bottom',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
export const Emoticon = recipe({
|
export const Emoticon = recipe({
|
||||||
base: [
|
base: [
|
||||||
DefaultReset,
|
DefaultReset,
|
||||||
@@ -285,16 +294,38 @@ export const Emoticon = recipe({
|
|||||||
boxShadow: `0 0 0 ${config.borderWidth.B300} ${color.SurfaceVariant.OnContainer}`,
|
boxShadow: `0 0 0 ${config.borderWidth.B300} ${color.SurfaceVariant.OnContainer}`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
jumbo: {
|
||||||
|
true: {
|
||||||
|
fontSize: toRem(100),
|
||||||
|
height: toRem(100),
|
||||||
|
minWidth: toRem(100),
|
||||||
|
lineHeight: toRem(100),
|
||||||
|
top: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const EmoticonImg = style([
|
export const EmoticonImg = recipe({
|
||||||
DefaultReset,
|
base: [
|
||||||
{
|
DefaultReset,
|
||||||
height: '1em',
|
{
|
||||||
cursor: 'default',
|
height: '1em',
|
||||||
|
cursor: 'default',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
variants: {
|
||||||
|
jumbo: {
|
||||||
|
true: {
|
||||||
|
height: toRem(100),
|
||||||
|
width: toRem(100),
|
||||||
|
maxHeight: toRem(100),
|
||||||
|
maxWidth: toRem(100),
|
||||||
|
objectFit: 'contain',
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
]);
|
});
|
||||||
|
|
||||||
export const highlightText = style([
|
export const highlightText = style([
|
||||||
DefaultReset,
|
DefaultReset,
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export const JUMBO_EMOJI_REG = new RegExp(
|
|||||||
);
|
);
|
||||||
|
|
||||||
/** Zero-width / format chars that break naive emoji-only matching. */
|
/** Zero-width / format chars that break naive emoji-only matching. */
|
||||||
const JUMBO_IGNORE_CHARS_REG = /[\u200B-\u200D\uFEFF\u00AD\u2060]/g;
|
const JUMBO_IGNORE_CHARS_REG = /[\u200B-\u200D\uFEFF\u00AD\u2060\u00A0]/g;
|
||||||
|
|
||||||
const ONLY_MX_EMOTICON_HTML_REG =
|
const ONLY_MX_EMOTICON_HTML_REG =
|
||||||
/^(?:\s|<br\s*\/?>)*((?:<img\b[^>]*\bdata-mx-emoticon\b[^>]*\/?>)(?:\s|<br\s*\/?>)*){1,10}$/i;
|
/^(?:\s|<br\s*\/?>)*((?:<img\b[^>]*\bdata-mx-emoticon\b[^>]*\/?>)(?:\s|<br\s*\/?>)*){1,10}$/i;
|
||||||
|
|||||||
Reference in New Issue
Block a user