Skip to content

Commit

Permalink
[IMPROVE] Respect convert ascii to emoji preference for new message t…
Browse files Browse the repository at this point in the history
…emplate (#27038)
  • Loading branch information
yash-rajpal authored Oct 14, 2022
1 parent c2e93cc commit 9d42405
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Box, MessageBody } from '@rocket.chat/fuselage';
import colors from '@rocket.chat/fuselage-tokens/colors';
import { MarkupInteractionContext, Markup, UserMention, ChannelMention } from '@rocket.chat/gazzodown';
import { escapeRegExp } from '@rocket.chat/string-helpers';
import { useLayout } from '@rocket.chat/ui-contexts';
import { useLayout, useUserPreference } from '@rocket.chat/ui-contexts';
import { FlowRouter } from 'meteor/kadira:flow-router';
import React, { ReactElement, UIEvent, useCallback, useMemo } from 'react';

Expand All @@ -19,7 +19,6 @@ const detectEmoji = (text: string): { name: string; className: string; image?: s
const html = Object.values(emoji.packages)
.reverse()
.reduce((html, { render }) => render(html), text);

const div = document.createElement('div');
div.innerHTML = html;
return Array.from(div.querySelectorAll('span')).map((span) => ({
Expand Down Expand Up @@ -125,6 +124,8 @@ const MessageContentBody = ({ mentions, channels, md }: MessageContentBodyProps)
}
`;

const convertAsciiToEmoji = useUserPreference<boolean>('convertAsciiEmoji', true);

return (
<MessageBody>
<Box className={messageBodyAdditionalStyles}>
Expand All @@ -136,6 +137,7 @@ const MessageContentBody = ({ mentions, channels, md }: MessageContentBodyProps)
onUserMentionClick,
resolveChannelMention,
onChannelMentionClick,
convertAsciiToEmoji,
}}
>
<Markup tokens={md} />
Expand Down
31 changes: 29 additions & 2 deletions packages/gazzodown/src/Markup.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { render, screen, cleanup, waitFor } from '@testing-library/react';
import { Suspense } from 'react';

import Markup from './Markup';

import '@testing-library/jest-dom';
import { MarkupInteractionContext } from '.';
import Markup from './Markup';

afterEach(cleanup);

Expand Down Expand Up @@ -33,6 +33,33 @@ it('renders a big emoji block', () => {
expect(screen.getAllByRole('img', { name: ':smile:' })).toHaveLength(2);
});

it('renders a big emoji block with ASCII emoji', () => {
render(
<MarkupInteractionContext.Provider
value={{
convertAsciiToEmoji: false,
}}
>
<Markup
tokens={[
{
type: 'BIG_EMOJI',
value: [
{ type: 'EMOJI', value: { type: 'PLAIN_TEXT', value: 'slight_smile' }, shortCode: 'slight_smile' },
{ type: 'EMOJI', value: undefined, unicode: '🙂' },
{ type: 'EMOJI', value: { type: 'PLAIN_TEXT', value: ':)' }, shortCode: 'slight_smile' },
],
},
]}
/>
</MarkupInteractionContext.Provider>,
);

expect(screen.getByRole('presentation')).toHaveTextContent(':slight_smile:🙂:)');
expect(screen.getAllByRole('img')).toHaveLength(2);
expect(screen.getAllByRole('img', { name: ':slight_smile:' })).toHaveLength(1);
});

it('renders a paragraph', () => {
render(
<Markup
Expand Down
1 change: 1 addition & 0 deletions packages/gazzodown/src/MarkupInteractionContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type MarkupInteractionContextValue = {
onUserMentionClick?: (mentionedUser: UserMention) => ((e: UIEvent) => void) | undefined;
resolveChannelMention?: (mention: string) => ChannelMention | undefined;
onChannelMentionClick?: (mentionedChannel: ChannelMention) => ((e: UIEvent) => void) | undefined;
convertAsciiToEmoji?: boolean;
};

export const MarkupInteractionContext = createContext<MarkupInteractionContextValue>({});
39 changes: 11 additions & 28 deletions packages/gazzodown/src/emoji/Emoji.tsx
Original file line number Diff line number Diff line change
@@ -1,45 +1,28 @@
import { MessageEmoji, ThreadMessageEmoji } from '@rocket.chat/fuselage';
import type * as MessageParser from '@rocket.chat/message-parser';
import { ReactElement, useMemo, useContext, memo } from 'react';

import { MarkupInteractionContext } from '../MarkupInteractionContext';
import PlainSpan from '../elements/PlainSpan';
import EmojiRenderer from './EmojiRenderer';

type EmojiProps = MessageParser.Emoji & {
big?: boolean;
preview?: boolean;
};

const Emoji = ({ big = false, preview = false, ...emoji }: EmojiProps): ReactElement => {
const { detectEmoji } = useContext(MarkupInteractionContext);
const { convertAsciiToEmoji } = useContext(MarkupInteractionContext);

const fallback = useMemo(() => ('unicode' in emoji ? emoji.unicode : `:${emoji.shortCode ?? emoji.value.value}:`), [emoji]);
const asciiEmoji = useMemo(
() => ('shortCode' in emoji && emoji.value.value !== emoji.shortCode ? emoji.value.value : undefined),
[emoji],
);

const descriptors = useMemo(() => {
const detected = detectEmoji?.(fallback);
return detected?.length !== 0 ? detected : undefined;
}, [detectEmoji, fallback]);
if (!convertAsciiToEmoji && asciiEmoji) {
return <PlainSpan text={asciiEmoji} />;
}

return (
<>
{descriptors?.map(({ name, className, image, content }, i) => (
<span key={i} title={name}>
{preview ? (
<ThreadMessageEmoji className={className} name={name} image={image}>
{content}
</ThreadMessageEmoji>
) : (
<MessageEmoji big={big} className={className} name={name} image={image}>
{content}
</MessageEmoji>
)}
</span>
)) ?? (
<span role='img' aria-label={fallback.charAt(0) === ':' ? fallback : undefined}>
{fallback}
</span>
)}
</>
);
return <EmojiRenderer big={big} preview={preview} {...emoji} />;
};

export default memo(Emoji);
45 changes: 45 additions & 0 deletions packages/gazzodown/src/emoji/EmojiRenderer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { MessageEmoji, ThreadMessageEmoji } from '@rocket.chat/fuselage';
import type * as MessageParser from '@rocket.chat/message-parser';
import { ReactElement, useMemo, useContext, memo } from 'react';

import { MarkupInteractionContext } from '../MarkupInteractionContext';

type EmojiProps = MessageParser.Emoji & {
big?: boolean;
preview?: boolean;
};

const EmojiRenderer = ({ big = false, preview = false, ...emoji }: EmojiProps): ReactElement => {
const { detectEmoji } = useContext(MarkupInteractionContext);

const fallback = useMemo(() => ('unicode' in emoji ? emoji.unicode : `:${emoji.shortCode ?? emoji.value.value}:`), [emoji]);

const descriptors = useMemo(() => {
const detected = detectEmoji?.(fallback);
return detected?.length !== 0 ? detected : undefined;
}, [detectEmoji, fallback]);

return (
<>
{descriptors?.map(({ name, className, image, content }, i) => (
<span key={i} title={name}>
{preview ? (
<ThreadMessageEmoji className={className} name={name} image={image}>
{content}
</ThreadMessageEmoji>
) : (
<MessageEmoji big={big} className={className} name={name} image={image}>
{content}
</MessageEmoji>
)}
</span>
)) ?? (
<span role='img' aria-label={fallback.charAt(0) === ':' ? fallback : undefined}>
{fallback}
</span>
)}
</>
);
};

export default memo(EmojiRenderer);

0 comments on commit 9d42405

Please sign in to comment.