Skip to content

Commit 3b4e74c

Browse files
authored
Merge pull request #530 from GetStream/feature/strip-emoji-mart
Optimize emoji-mart
2 parents 52973cb + ba0cd88 commit 3b4e74c

34 files changed

+711
-345
lines changed

.babelrc

Lines changed: 0 additions & 20 deletions
This file was deleted.

.eslintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
node_modules
22
build
3+
src/stream-emoji.json
34
/.cache/
45
/public/static/styles.js
56
examples/*/src/serviceWorker.js

.prettierignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ node_modules
22
/.cache/
33
/public/static/styles.js
44
examples/*/src/serviceWorker.js
5+
src/stream-emoji.json
56
/dist/
67
/lib/
78
/bin/
89
/include/
910
/build/
1011
build
11-
/built
12+
/built

babel.config.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
module.exports = {
2+
presets: ['@babel/preset-typescript', '@babel/env', '@babel/react'],
3+
plugins: [
4+
'@babel/plugin-proposal-class-properties',
5+
'@babel/plugin-transform-runtime',
6+
],
7+
env: {
8+
production: {
9+
presets: [
10+
[
11+
'@babel/env',
12+
{
13+
modules: false,
14+
},
15+
],
16+
],
17+
},
18+
test: {
19+
presets: ['@babel/preset-env', '@babel/preset-react'],
20+
plugins: [
21+
'@babel/plugin-proposal-class-properties',
22+
'transform-es2015-modules-commonjs',
23+
'babel-plugin-dynamic-import-node',
24+
],
25+
},
26+
},
27+
ignore: ['src/@types/*'],
28+
};

emojiMartDataParser.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* This script allows us to generate a version of the emoji-mart json data stripped of skin variation information.
3+
* Emoji data has a significant contribution to the bundle size, and skin variation information takes a good part
4+
* of the full data set json file.
5+
*/
6+
const path = require('path');
7+
const fs = require('fs');
8+
9+
// Getting the facebook dataset as it is the smallest one.
10+
const defaultDataSetJson = fs.readFileSync(
11+
path.join(__dirname, 'node_modules', 'emoji-mart', 'data', 'facebook.json'),
12+
'utf8',
13+
);
14+
15+
const dataSet = JSON.parse(defaultDataSetJson);
16+
17+
const removeSkinTonesInfo = (emojiKey) => {
18+
if (dataSet.emojis[emojiKey].skin_variations) {
19+
delete dataSet.emojis[emojiKey].skin_variations;
20+
}
21+
};
22+
23+
Object.keys(dataSet.emojis).map(removeSkinTonesInfo);
24+
25+
const newDataSetJson = JSON.stringify(dataSet);
26+
27+
fs.writeFileSync(
28+
path.join(__dirname, 'src', 'stream-emoji.json'),
29+
newDataSetJson,
30+
);

jest.config.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,18 @@ module.exports = {
1010
* "src/components/MessageList/__tests__/MessageList.test.js"
1111
*/
1212
],
13-
transformIgnorePatterns: ['/node_modules'],
13+
transformIgnorePatterns: [],
1414
testPathIgnorePatterns: ['/node_modules/', '/examples/', '__snapshots__'],
1515
transform: {
16-
'^.+\\.jsx?$': 'babel-jest',
16+
'^.+\\.(js|jsx)?$': 'babel-jest',
1717
},
1818
moduleNameMapper: {
1919
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
2020
'<rootDir>/assetsTransformer.js',
2121
'mock-builders(.*)$': '<rootDir>/src/mock-builders$1',
2222
},
2323
setupFiles: ['core-js'],
24+
globals: {
25+
ICAL: {},
26+
},
2427
};

package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,13 @@
8484
"@types/uuid": "^8.3.0",
8585
"autoprefixer": "^10.0.3",
8686
"babel-eslint": "^10.1.0",
87+
"babel-jest": "^26.3.0",
8788
"babel-loader": "8.2.2",
8889
"babel-plugin-i18next-extract": "^0.8.2",
90+
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.2",
8991
"codecov": "^3.8.1",
9092
"css-loader": "^5.0.1",
93+
"core-js": "^3.6.5",
9194
"eslint": "7.14.0",
9295
"eslint-config-airbnb": "^18.2.1",
9396
"eslint-config-prettier": "^6.15.0",
@@ -161,8 +164,8 @@
161164
"docs-server": "styleguidist server",
162165
"watch-styles": "sass --watch src/styles/index.scss dist/css/index.css",
163166
"build-styles": "sass src/styles/index.scss dist/css/index.css --style compressed",
164-
"prettier": "prettier --list-different '**/*.{js,ts,md,json}' .eslintrc.json .prettierrc .babelrc",
165-
"prettier-fix": "prettier --write '**/*.{js,ts,md,json}' .eslintrc.json .prettierrc .babelrc",
167+
"prettier": "prettier --list-different '**/*.{js,ts,md,json}' .eslintrc.json .prettierrc",
168+
"prettier-fix": "prettier --write '**/*.{js,ts,md,json}' .eslintrc.json .prettierrc",
166169
"eslint": "eslint '**/*.{js,md}' --max-warnings 0",
167170
"stylelint": "stylelint '**/*.{css,scss}'",
168171
"analyze": "yarn build -- --stats && webpack-bundle-analyzer build/bundle-stats.json",

rollup.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ const basePlugins = [
115115
},
116116
),
117117
// Json to ES modules conversion
118-
json(),
118+
json({ compact: true }),
119119
process.env.BUNDLE_SIZE ? visualizer() : null,
120120
];
121121

src/components/Channel/Channel.js

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,20 @@ import React, {
77
useReducer,
88
useLayoutEffect,
99
} from 'react';
10+
// @ts-expect-error
11+
import DefaultEmoji from 'emoji-mart/dist-modern/components/emoji/nimble-emoji';
12+
// @ts-expect-error
13+
import DefaultEmojiIndex from 'emoji-mart/dist-modern/utils/emoji-index/nimble-emoji-index';
14+
// @ts-expect-error
15+
import DefaultEmojiPicker from 'emoji-mart/dist-modern/components/picker/nimble-picker';
1016
import debounce from 'lodash.debounce';
1117
import throttle from 'lodash.throttle';
1218
import PropTypes from 'prop-types';
1319
import { logChatPromiseExecution, Channel as StreamChannel } from 'stream-chat';
1420
import { v4 as uuidv4 } from 'uuid';
1521

1622
import { Attachment as DefaultAttachment } from '../Attachment';
23+
import { commonEmoji, defaultMinimalEmojis, emojiSetDef } from './emojiData';
1724
import { MessageSimple } from '../Message';
1825
import {
1926
LoadingIndicator as DefaultLoadingIndicator,
@@ -26,6 +33,7 @@ import useEditMessageHandler from './hooks/useEditMessageHandler';
2633
import useIsMounted from './hooks/useIsMounted';
2734

2835
import { ChatContext, ChannelContext, TranslationContext } from '../../context';
36+
import defaultEmojiData from '../../stream-emoji.json';
2937

3038
/** @type {React.FC<import('types').ChannelProps>}>} */
3139
const Channel = ({ EmptyPlaceholder = null, ...props }) => {
@@ -42,6 +50,10 @@ const Channel = ({ EmptyPlaceholder = null, ...props }) => {
4250
const ChannelInner = ({
4351
Attachment = DefaultAttachment,
4452
doMarkReadRequest,
53+
Emoji = DefaultEmoji,
54+
emojiData = defaultEmojiData,
55+
EmojiIndex = DefaultEmojiIndex,
56+
EmojiPicker = DefaultEmojiPicker,
4557
LoadingErrorIndicator = DefaultLoadingErrorIndicator,
4658
LoadingIndicator = DefaultLoadingIndicator,
4759
Message = MessageSimple,
@@ -61,6 +73,16 @@ const ChannelInner = ({
6173
const lastRead = useRef(new Date());
6274
const online = useRef(true);
6375

76+
const emojiConfig = {
77+
commonEmoji,
78+
defaultMinimalEmojis,
79+
Emoji,
80+
emojiData,
81+
EmojiIndex,
82+
EmojiPicker,
83+
emojiSetDef,
84+
};
85+
6486
// eslint-disable-next-line react-hooks/exhaustive-deps
6587
const throttledCopyStateFromChannel = useCallback(
6688
throttle(
@@ -456,6 +478,8 @@ const ChannelInner = ({
456478
updateMessage,
457479
// from chatContext, for legacy reasons
458480
client,
481+
// emoji config and customization object, potentially find a better home
482+
emojiConfig,
459483
};
460484

461485
let core;
@@ -500,7 +524,7 @@ Channel.propTypes = {
500524
LoadingErrorIndicator: PropTypes.elementType,
501525
/**
502526
* Loading indicator UI component. This will be shown on the screen until the messages are
503-
* being queried from channelœ. Once the messages are loaded, loading indicator is removed from the screen
527+
* being queried from channel. Once the messages are loaded, loading indicator is removed from the screen
504528
* and replaced with children of the Channel component.
505529
*
506530
* Defaults to and accepts same props as: [LoadingIndicator](https://github.com/GetStream/stream-chat-react/blob/master/src/components/LoadingIndicator.js)
@@ -565,6 +589,26 @@ Channel.propTypes = {
565589
* @param {Object} updatedMessage
566590
*/
567591
doUpdateMessageRequest: PropTypes.func,
592+
/**
593+
* Optional component to override default NimbleEmoji from emoji-mart
594+
*/
595+
// @ts-expect-error import type when converted to TS
596+
Emoji: /** @type {PropTypes.Validator<React.ElementType<NimbleEmojiProps>>} */ (PropTypes.elementType),
597+
/**
598+
* Optional prop to override default facebook.json emoji data set from emoji-mart
599+
*/
600+
// @ts-expect-error import type when converted to TS
601+
emojiData: /** @type {PropTypes.Validator<EmojiMartData>} */ (PropTypes.object),
602+
/**
603+
* Optional component to override default NimbleEmojiIndex from emoji-mart
604+
*/
605+
// @ts-expect-error import type when converted to TS
606+
EmojiIndex: /** @type {PropTypes.Validator<NimbleEmojiIndex>} */ (PropTypes.object),
607+
/**
608+
* Optional component to override default NimblePicker from emoji-mart
609+
*/
610+
// @ts-expect-error import type when converted to TS
611+
EmojiPicker: /** @type {PropTypes.Validator<React.ElementType<NimblePickerProps>>} */ (PropTypes.elementType),
568612
};
569613

570614
export default React.memo(Channel);

src/components/Channel/__tests__/Channel.test.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,32 @@ describe('Channel', () => {
216216

217217
// eslint-disable-next-line sonarjs/cognitive-complexity
218218
describe('Children that consume ChannelContext', () => {
219+
it('should expose the emoji config', async () => {
220+
let context;
221+
const emojiData = {
222+
compressed: true,
223+
categories: [],
224+
emojis: {},
225+
aliases: {},
226+
};
227+
const CustomEmojiPicker = () => <div />;
228+
const CustomEmoji = () => <span />;
229+
230+
renderComponent(
231+
{ emojiData, Emoji: CustomEmoji, EmojiPicker: CustomEmojiPicker },
232+
(ctx) => {
233+
context = ctx;
234+
},
235+
);
236+
237+
await waitFor(() => {
238+
expect(context).toBeInstanceOf(Object);
239+
expect(context.emojiConfig.emojiData).toBe(emojiData);
240+
expect(context.emojiConfig.EmojiPicker).toBe(CustomEmojiPicker);
241+
expect(context.emojiConfig.Emoji).toBe(CustomEmoji);
242+
});
243+
});
244+
219245
it('should be able to open threads', async () => {
220246
const threadMessage = messages[0];
221247
const hasThread = jest.fn();

0 commit comments

Comments
 (0)