@atlaskit/emoji 64.6.0 → 64.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/dist/cjs/api/EmojiRepository.js +4 -0
- package/dist/cjs/api/EmojiResource.js +13 -1
- package/dist/cjs/api/EmojiUtils.js +4 -0
- package/dist/cjs/api/internal/UsageFrequencyTracker.js +4 -1
- package/dist/cjs/components/common/CachingEmoji.js +33 -12
- package/dist/cjs/components/common/DeleteButton.js +2 -1
- package/dist/cjs/components/common/Emoji.js +98 -21
- package/dist/cjs/components/common/EmojiPlaceholder.js +1 -0
- package/dist/cjs/components/common/Popup.js +21 -1
- package/dist/cjs/components/common/RecordSelectionDefault.js +13 -1
- package/dist/cjs/components/common/ResourcedEmoji.js +13 -3
- package/dist/cjs/components/common/ResourcedEmojiComponent.js +32 -3
- package/dist/cjs/components/common/UfoErrorBoundary.js +30 -4
- package/dist/cjs/components/common/UploadEmoji.js +17 -1
- package/dist/cjs/components/picker/EmojiPickerComponent.js +16 -4
- package/dist/cjs/components/picker/EmojiPickerListSearch.js +5 -0
- package/dist/cjs/components/picker/styles.js +2 -0
- package/dist/cjs/components/typeahead/EmojiTypeAheadComponent.js +18 -2
- package/dist/cjs/components/uploader/EmojiUploadComponent.js +6 -1
- package/dist/cjs/types.js +27 -2
- package/dist/cjs/util/analytics/ufoExperiences.js +46 -3
- package/dist/cjs/util/analytics/useSampledUFOComponentExperience.js +2 -1
- package/dist/cjs/util/browser-support.js +8 -0
- package/dist/cjs/util/constants.js +4 -5
- package/dist/cjs/util/storage-available.js +4 -0
- package/dist/cjs/util/useInView.js +23 -0
- package/dist/cjs/version.json +1 -1
- package/dist/es2019/api/EmojiRepository.js +4 -0
- package/dist/es2019/api/EmojiResource.js +13 -1
- package/dist/es2019/api/EmojiUtils.js +4 -0
- package/dist/es2019/api/internal/UsageFrequencyTracker.js +4 -1
- package/dist/es2019/components/common/CachingEmoji.js +28 -9
- package/dist/es2019/components/common/DeleteButton.js +2 -1
- package/dist/es2019/components/common/Emoji.js +76 -24
- package/dist/es2019/components/common/EmojiPlaceholder.js +1 -0
- package/dist/es2019/components/common/Popup.js +21 -1
- package/dist/es2019/components/common/RecordSelectionDefault.js +13 -1
- package/dist/es2019/components/common/ResourcedEmoji.js +14 -4
- package/dist/es2019/components/common/ResourcedEmojiComponent.js +28 -3
- package/dist/es2019/components/common/UfoErrorBoundary.js +14 -2
- package/dist/es2019/components/common/UploadEmoji.js +10 -1
- package/dist/es2019/components/picker/EmojiPickerComponent.js +16 -3
- package/dist/es2019/components/picker/EmojiPickerListSearch.js +5 -0
- package/dist/es2019/components/picker/styles.js +3 -0
- package/dist/es2019/components/typeahead/EmojiTypeAheadComponent.js +18 -2
- package/dist/es2019/components/uploader/EmojiUploadComponent.js +6 -1
- package/dist/es2019/types.js +24 -1
- package/dist/es2019/util/analytics/ufoExperiences.js +37 -2
- package/dist/es2019/util/analytics/useSampledUFOComponentExperience.js +2 -1
- package/dist/es2019/util/browser-support.js +1 -0
- package/dist/es2019/util/constants.js +2 -2
- package/dist/es2019/util/storage-available.js +4 -0
- package/dist/es2019/util/useInView.js +12 -0
- package/dist/es2019/version.json +1 -1
- package/dist/esm/api/EmojiRepository.js +4 -0
- package/dist/esm/api/EmojiResource.js +13 -1
- package/dist/esm/api/EmojiUtils.js +4 -0
- package/dist/esm/api/internal/UsageFrequencyTracker.js +4 -1
- package/dist/esm/components/common/CachingEmoji.js +32 -13
- package/dist/esm/components/common/DeleteButton.js +2 -1
- package/dist/esm/components/common/Emoji.js +84 -24
- package/dist/esm/components/common/EmojiPlaceholder.js +1 -0
- package/dist/esm/components/common/Popup.js +21 -1
- package/dist/esm/components/common/RecordSelectionDefault.js +13 -1
- package/dist/esm/components/common/ResourcedEmoji.js +14 -4
- package/dist/esm/components/common/ResourcedEmojiComponent.js +30 -3
- package/dist/esm/components/common/UfoErrorBoundary.js +30 -4
- package/dist/esm/components/common/UploadEmoji.js +15 -1
- package/dist/esm/components/picker/EmojiPickerComponent.js +16 -3
- package/dist/esm/components/picker/EmojiPickerListSearch.js +5 -0
- package/dist/esm/components/picker/styles.js +2 -0
- package/dist/esm/components/typeahead/EmojiTypeAheadComponent.js +18 -2
- package/dist/esm/components/uploader/EmojiUploadComponent.js +6 -1
- package/dist/esm/types.js +24 -1
- package/dist/esm/util/analytics/ufoExperiences.js +39 -2
- package/dist/esm/util/analytics/useSampledUFOComponentExperience.js +2 -1
- package/dist/esm/util/browser-support.js +1 -0
- package/dist/esm/util/constants.js +2 -2
- package/dist/esm/util/storage-available.js +4 -0
- package/dist/esm/util/useInView.js +12 -0
- package/dist/esm/version.json +1 -1
- package/dist/types/components/common/CachingEmoji.d.ts +0 -1
- package/dist/types/components/common/Emoji.d.ts +2 -0
- package/dist/types/components/common/ResourcedEmojiComponent.d.ts +1 -0
- package/dist/types/components/common/UfoErrorBoundary.d.ts +2 -2
- package/dist/types/types.d.ts +17 -0
- package/dist/types/util/analytics/samplingUfo.d.ts +7 -0
- package/dist/types/util/analytics/ufoExperiences.d.ts +4 -1
- package/dist/types/util/browser-support.d.ts +1 -0
- package/dist/types/util/constants.d.ts +1 -1
- package/dist/types/util/useInView.d.ts +4 -0
- package/package.json +7 -6
|
@@ -2,14 +2,18 @@ import _extends from "@babel/runtime/helpers/extends";
|
|
|
2
2
|
|
|
3
3
|
/** @jsx jsx */
|
|
4
4
|
import { jsx } from '@emotion/core';
|
|
5
|
-
import React from 'react';
|
|
5
|
+
import React, { useEffect, useCallback } from 'react';
|
|
6
6
|
import { shouldUseAltRepresentation } from '../../api/EmojiUtils';
|
|
7
|
-
import { deleteEmojiLabel } from '../../util/constants';
|
|
7
|
+
import { deleteEmojiLabel, SAMPLING_RATE_EMOJI_RENDERED_EXP } from '../../util/constants';
|
|
8
8
|
import { isImageRepresentation, isMediaRepresentation, isSpriteRepresentation, toEmojiId } from '../../util/type-helpers';
|
|
9
|
+
import { UfoEmojiTimings } from '../../types';
|
|
9
10
|
import { leftClick } from '../../util/mouse';
|
|
10
11
|
import DeleteButton from './DeleteButton';
|
|
11
12
|
import { emojiContainer, emojiNodeStyles, commonSelectedStyles, selectOnHoverStyles, emojiSprite, emojiMainStyle, emojiStyles, emojiImage } from './styles';
|
|
12
|
-
import { sampledUfoRenderedEmoji } from '../../util/analytics';
|
|
13
|
+
import { sampledUfoRenderedEmoji, ufoExperiences, useSampledUFOComponentExperience } from '../../util/analytics';
|
|
14
|
+
import { isIntersectionObserverSupported } from '../../util/browser-support';
|
|
15
|
+
import { useInView } from '../../util/useInView';
|
|
16
|
+
import { hasUfoMarked } from '../../util/analytics/ufoExperiences';
|
|
13
17
|
|
|
14
18
|
const handleMouseDown = (props, event) => {
|
|
15
19
|
// Clicked emoji delete button
|
|
@@ -83,9 +87,10 @@ const handleImageError = (props, event) => {
|
|
|
83
87
|
}
|
|
84
88
|
}; // Pure functional components are used in favour of class based components, due to the performance!
|
|
85
89
|
// When rendering 1500+ emoji using class based components had a significant impact.
|
|
90
|
+
// TODO: add UFO tracking for sprite emoji
|
|
86
91
|
|
|
87
92
|
|
|
88
|
-
const
|
|
93
|
+
export const SpriteEmoji = props => {
|
|
89
94
|
const {
|
|
90
95
|
emoji,
|
|
91
96
|
fitToHeight,
|
|
@@ -117,11 +122,9 @@ const renderAsSprite = props => {
|
|
|
117
122
|
backgroundSize: `${sprite.column * 100}% ${sprite.row * 100}%`,
|
|
118
123
|
...sizing
|
|
119
124
|
};
|
|
120
|
-
const emojiNode = jsx("span", {
|
|
121
|
-
className: emojiSprite,
|
|
122
|
-
style: style
|
|
123
|
-
}, "\xA0");
|
|
124
125
|
return jsx("span", {
|
|
126
|
+
"data-testid": `sprite-emoji-${emoji.shortName}`,
|
|
127
|
+
"data-emoji-type": "sprite",
|
|
125
128
|
tabIndex: shouldBeInteractive ? 0 : undefined,
|
|
126
129
|
role: shouldBeInteractive ? 'button' : undefined,
|
|
127
130
|
css: emojiContainer,
|
|
@@ -135,11 +138,13 @@ const renderAsSprite = props => {
|
|
|
135
138
|
},
|
|
136
139
|
"aria-label": emoji.shortName,
|
|
137
140
|
title: showTooltip ? emoji.shortName : ''
|
|
138
|
-
},
|
|
141
|
+
}, jsx("span", {
|
|
142
|
+
className: emojiSprite,
|
|
143
|
+
style: style
|
|
144
|
+
}, "\xA0"));
|
|
139
145
|
}; // Keep as pure functional component, see renderAsSprite.
|
|
140
146
|
|
|
141
|
-
|
|
142
|
-
const renderAsImage = props => {
|
|
147
|
+
export const ImageEmoji = props => {
|
|
143
148
|
const {
|
|
144
149
|
emoji,
|
|
145
150
|
fitToHeight,
|
|
@@ -150,6 +155,10 @@ const renderAsImage = props => {
|
|
|
150
155
|
showDelete,
|
|
151
156
|
shouldBeInteractive
|
|
152
157
|
} = props;
|
|
158
|
+
const [ref, inView] = useInView({
|
|
159
|
+
triggerOnce: true
|
|
160
|
+
});
|
|
161
|
+
const ufoExp = sampledUfoRenderedEmoji(emoji);
|
|
153
162
|
const classes = `${emojiMainStyle} ${emojiNodeStyles} ${selected ? commonSelectedStyles : ''} ${selectOnHover ? selectOnHoverStyles : ''} ${emojiImage} ${className ? className : ''}`;
|
|
154
163
|
let width;
|
|
155
164
|
let height;
|
|
@@ -188,17 +197,41 @@ const renderAsImage = props => {
|
|
|
188
197
|
handleImageError(props, event);
|
|
189
198
|
};
|
|
190
199
|
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
// We currently have the following error: property 'loading' does not exist on type 'DetailedHTMLProps<ImgHTMLAttributes, HTMLImageElement>'
|
|
196
|
-
// The fix for this was added as a part of @types/react@16.9.20 - https://github.com/facebook/react/issues/16942
|
|
197
|
-
// TODO: remove @ts-ignore for the <img> below when the @types/react will be bumped from v16.8.0 to v16.9.20 or higher.
|
|
200
|
+
const markStartLoadFromMountedTime = useCallback(() => {
|
|
201
|
+
const mountedMark = ufoExp.metrics.marks.find(mark => mark.name === UfoEmojiTimings.MOUNTED_END);
|
|
202
|
+
ufoExp.mark(UfoEmojiTimings.ONLOAD_START, mountedMark === null || mountedMark === void 0 ? void 0 : mountedMark.time);
|
|
203
|
+
}, [ufoExp]);
|
|
198
204
|
|
|
205
|
+
const onLoad = () => {
|
|
206
|
+
// onload could trigger before onBeforeLoad when emojis in viewport at start, so we need to mark onload start manually.
|
|
207
|
+
if (!hasUfoMarked(ufoExp, UfoEmojiTimings.ONLOAD_START)) {
|
|
208
|
+
markStartLoadFromMountedTime();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (!hasUfoMarked(ufoExp, UfoEmojiTimings.ONLOAD_END)) {
|
|
212
|
+
ufoExp.mark(UfoEmojiTimings.ONLOAD_END);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
ufoExp.success({
|
|
216
|
+
metadata: {
|
|
217
|
+
IBSupported: isIntersectionObserverSupported
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
};
|
|
199
221
|
|
|
222
|
+
const onBeforeLoad = useCallback(() => {
|
|
223
|
+
if (!hasUfoMarked(ufoExp, UfoEmojiTimings.ONLOAD_START)) {
|
|
224
|
+
ufoExp.mark(UfoEmojiTimings.ONLOAD_START);
|
|
225
|
+
}
|
|
226
|
+
}, [ufoExp]); // because of the lack of browser support of on before load natively, used IntersectionObserver helper hook to mimic the before load time mark for UFO.
|
|
227
|
+
|
|
228
|
+
useEffect(() => {
|
|
229
|
+
if (inView) {
|
|
230
|
+
onBeforeLoad();
|
|
231
|
+
}
|
|
232
|
+
}, [inView, onBeforeLoad]);
|
|
200
233
|
const emojiNode = jsx("img", _extends({
|
|
201
|
-
|
|
234
|
+
//@ts-ignore
|
|
202
235
|
loading: "lazy",
|
|
203
236
|
src: src,
|
|
204
237
|
key: src,
|
|
@@ -214,6 +247,8 @@ const renderAsImage = props => {
|
|
|
214
247
|
onLoad: onLoad
|
|
215
248
|
}, sizing));
|
|
216
249
|
return jsx("span", {
|
|
250
|
+
"data-testid": `image-emoji-${emoji.shortName}`,
|
|
251
|
+
"data-emoji-type": "image",
|
|
217
252
|
css: emojiStyles,
|
|
218
253
|
tabIndex: shouldBeInteractive ? 0 : undefined,
|
|
219
254
|
role: shouldBeInteractive ? 'button' : undefined,
|
|
@@ -226,19 +261,36 @@ const renderAsImage = props => {
|
|
|
226
261
|
handleMouseMove(props, event);
|
|
227
262
|
},
|
|
228
263
|
"aria-label": emoji.shortName,
|
|
229
|
-
title: showTooltip ? emoji.shortName : ''
|
|
264
|
+
title: showTooltip ? emoji.shortName : '',
|
|
265
|
+
ref: ref
|
|
230
266
|
}, deleteButton, emojiNode);
|
|
231
267
|
};
|
|
232
|
-
|
|
233
268
|
export const Emoji = props => {
|
|
234
269
|
const {
|
|
235
270
|
emoji
|
|
236
|
-
} = props; //
|
|
271
|
+
} = props; // start emoji rendered experience, it may have already started earlier in ResourcedEmoji or CachingEmoji
|
|
272
|
+
|
|
273
|
+
useSampledUFOComponentExperience(ufoExperiences['emoji-rendered'].getInstance(emoji.id || emoji.shortName), SAMPLING_RATE_EMOJI_RENDERED_EXP, {
|
|
274
|
+
source: 'emoji',
|
|
275
|
+
emoji: emoji.shortName
|
|
276
|
+
});
|
|
277
|
+
useEffect(() => {
|
|
278
|
+
const ufoExp = sampledUfoRenderedEmoji(emoji);
|
|
279
|
+
|
|
280
|
+
if (!hasUfoMarked(ufoExp, 'fmp')) {
|
|
281
|
+
ufoExp.markFMP();
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (!hasUfoMarked(ufoExp, UfoEmojiTimings.MOUNTED_END)) {
|
|
285
|
+
ufoExp.mark(UfoEmojiTimings.MOUNTED_END);
|
|
286
|
+
} // eslint-disable-next-line react-hooks/exhaustive-deps
|
|
287
|
+
|
|
288
|
+
}, []); // TODO: We always prefer render as image as having accessibility issues with sprite representation
|
|
237
289
|
|
|
238
290
|
if (isSpriteRepresentation(emoji.representation)) {
|
|
239
|
-
return
|
|
291
|
+
return jsx(SpriteEmoji, props);
|
|
240
292
|
}
|
|
241
293
|
|
|
242
|
-
return
|
|
294
|
+
return jsx(ImageEmoji, props);
|
|
243
295
|
};
|
|
244
296
|
export default Emoji;
|
|
@@ -25,6 +25,10 @@ export default class Popup extends PureComponent {
|
|
|
25
25
|
if (this.debounced) {
|
|
26
26
|
clearTimeout(this.debounced);
|
|
27
27
|
this.debounced = null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (typeof window === 'undefined') {
|
|
31
|
+
return;
|
|
28
32
|
} // Timeout set to 30ms as to not throttle IE11
|
|
29
33
|
|
|
30
34
|
|
|
@@ -39,7 +43,11 @@ export default class Popup extends PureComponent {
|
|
|
39
43
|
this.popup = document.createElement('div');
|
|
40
44
|
document.body.appendChild(this.popup);
|
|
41
45
|
this.popup.style.position = 'absolute';
|
|
42
|
-
|
|
46
|
+
|
|
47
|
+
if (typeof window !== 'undefined') {
|
|
48
|
+
window.addEventListener('resize', this.handleResize);
|
|
49
|
+
}
|
|
50
|
+
|
|
43
51
|
this.applyAbsolutePosition();
|
|
44
52
|
this.renderContent();
|
|
45
53
|
}
|
|
@@ -50,6 +58,10 @@ export default class Popup extends PureComponent {
|
|
|
50
58
|
}
|
|
51
59
|
|
|
52
60
|
componentWillUnmount() {
|
|
61
|
+
if (typeof window === 'undefined') {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
53
65
|
window.removeEventListener('resize', this.handleResize);
|
|
54
66
|
|
|
55
67
|
if (this.popup) {
|
|
@@ -73,6 +85,10 @@ export default class Popup extends PureComponent {
|
|
|
73
85
|
}
|
|
74
86
|
|
|
75
87
|
applyAbovePosition() {
|
|
88
|
+
if (typeof window === 'undefined') {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
76
92
|
const targetNode = getTargetNode(this.props.target);
|
|
77
93
|
|
|
78
94
|
if (targetNode && this.popup) {
|
|
@@ -86,6 +102,10 @@ export default class Popup extends PureComponent {
|
|
|
86
102
|
}
|
|
87
103
|
|
|
88
104
|
applyAbsolutePosition() {
|
|
105
|
+
if (typeof window === 'undefined') {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
89
109
|
if (this.props.relativePosition === 'above') {
|
|
90
110
|
this.applyAbovePosition();
|
|
91
111
|
} else if (this.props.relativePosition === 'below') {
|
|
@@ -18,7 +18,19 @@ export const createRecordSelectionDefault = (provider, onSelect, fireAnalytics)
|
|
|
18
18
|
ufoExperiences['emoji-selection-recorded'].success();
|
|
19
19
|
}).catch(() => {
|
|
20
20
|
fireAnalytics && fireAnalytics(recordFailed);
|
|
21
|
-
ufoExperiences['emoji-selection-recorded'].failure(
|
|
21
|
+
ufoExperiences['emoji-selection-recorded'].failure({
|
|
22
|
+
metadata: {
|
|
23
|
+
reason: 'recordSelection error',
|
|
24
|
+
source: 'RecordSelectionDefault',
|
|
25
|
+
data: {
|
|
26
|
+
emoji: {
|
|
27
|
+
id: emoji.id,
|
|
28
|
+
name: emoji.name,
|
|
29
|
+
shortName: emoji.shortName
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
});
|
|
22
34
|
});
|
|
23
35
|
}
|
|
24
36
|
} finally {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import _extends from "@babel/runtime/helpers/extends";
|
|
2
2
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
3
3
|
import React from 'react';
|
|
4
|
-
import { defaultEmojiHeight,
|
|
4
|
+
import { defaultEmojiHeight, SAMPLING_RATE_EMOJI_RENDERED_EXP } from '../../util/constants';
|
|
5
5
|
import EmojiPlaceholder from './EmojiPlaceholder';
|
|
6
6
|
import LoadingEmojiComponent from './LoadingEmojiComponent';
|
|
7
7
|
import { sampledUfoRenderedEmoji, ufoExperiences } from '../../util/analytics';
|
|
@@ -24,15 +24,25 @@ export default class ResourcedEmoji extends LoadingEmojiComponent {
|
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
sampledUfoRenderedEmoji(props.emojiId).start({
|
|
27
|
-
samplingRate:
|
|
27
|
+
samplingRate: SAMPLING_RATE_EMOJI_RENDERED_EXP
|
|
28
28
|
});
|
|
29
29
|
ufoExperiences['emoji-rendered'].getInstance(props.emojiId.id || props.emojiId.shortName).addMetadata({
|
|
30
|
-
source: 'resourced-emoji'
|
|
30
|
+
source: 'resourced-emoji',
|
|
31
|
+
emoji: props.emojiId.shortName
|
|
31
32
|
});
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
componentWillUnmount() {
|
|
35
|
-
sampledUfoRenderedEmoji(this.props.emojiId).abort(
|
|
36
|
+
sampledUfoRenderedEmoji(this.props.emojiId).abort({
|
|
37
|
+
metadata: {
|
|
38
|
+
source: 'ResourcedEmoji',
|
|
39
|
+
reason: 'unmount',
|
|
40
|
+
data: {
|
|
41
|
+
emojiId: this.props.emojiId.id,
|
|
42
|
+
shortName: this.props.emojiId.shortName
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
});
|
|
36
46
|
}
|
|
37
47
|
|
|
38
48
|
asyncLoadComponent() {
|
|
@@ -3,10 +3,12 @@ import React from 'react';
|
|
|
3
3
|
import { Component } from 'react';
|
|
4
4
|
import { defaultEmojiHeight } from '../../util/constants';
|
|
5
5
|
import { isPromise } from '../../util/type-helpers';
|
|
6
|
+
import { UfoEmojiTimings } from '../../types';
|
|
6
7
|
import CachingEmoji from './CachingEmoji';
|
|
7
8
|
import EmojiPlaceholder from './EmojiPlaceholder';
|
|
8
9
|
import { sampledUfoRenderedEmoji } from '../../util/analytics';
|
|
9
10
|
import LegacyEmojiContextProvider from '../../context/LegacyEmojiContextProvider';
|
|
11
|
+
import { hasUfoMarked } from '../../util/analytics/ufoExperiences';
|
|
10
12
|
export default class ResourcedEmojiComponent extends Component {
|
|
11
13
|
constructor(props) {
|
|
12
14
|
super(props);
|
|
@@ -21,6 +23,7 @@ export default class ResourcedEmojiComponent extends Component {
|
|
|
21
23
|
|
|
22
24
|
refreshEmoji(emojiProvider, emojiId) {
|
|
23
25
|
const foundEmoji = emojiProvider.findByEmojiId(emojiId);
|
|
26
|
+
sampledUfoRenderedEmoji(emojiId).mark(UfoEmojiTimings.METADATA_START);
|
|
24
27
|
|
|
25
28
|
if (isPromise(foundEmoji)) {
|
|
26
29
|
this.setState({
|
|
@@ -33,12 +36,20 @@ export default class ResourcedEmojiComponent extends Component {
|
|
|
33
36
|
emoji,
|
|
34
37
|
loaded: true
|
|
35
38
|
});
|
|
39
|
+
sampledUfoRenderedEmoji(emojiId).mark(UfoEmojiTimings.METADATA_END);
|
|
36
40
|
|
|
37
41
|
if (!emoji) {
|
|
38
42
|
// emoji is undefined
|
|
39
43
|
sampledUfoRenderedEmoji(emojiId).failure({
|
|
40
44
|
metadata: {
|
|
41
|
-
reason: 'failed to find'
|
|
45
|
+
reason: 'failed to find',
|
|
46
|
+
source: 'ResourcedEmojiComponent',
|
|
47
|
+
data: {
|
|
48
|
+
emoji: {
|
|
49
|
+
id: emojiId.id,
|
|
50
|
+
shortName: emojiId.shortName
|
|
51
|
+
}
|
|
52
|
+
}
|
|
42
53
|
}
|
|
43
54
|
});
|
|
44
55
|
}
|
|
@@ -46,12 +57,20 @@ export default class ResourcedEmojiComponent extends Component {
|
|
|
46
57
|
}).catch(() => {
|
|
47
58
|
sampledUfoRenderedEmoji(emojiId).failure({
|
|
48
59
|
metadata: {
|
|
49
|
-
reason: 'failed to load'
|
|
60
|
+
reason: 'failed to load',
|
|
61
|
+
source: 'ResourcedEmojiComponent',
|
|
62
|
+
data: {
|
|
63
|
+
emoji: {
|
|
64
|
+
id: emojiId.id,
|
|
65
|
+
shortName: emojiId.shortName
|
|
66
|
+
}
|
|
67
|
+
}
|
|
50
68
|
}
|
|
51
69
|
});
|
|
52
70
|
});
|
|
53
71
|
} else {
|
|
54
|
-
// loaded
|
|
72
|
+
sampledUfoRenderedEmoji(emojiId).mark(UfoEmojiTimings.METADATA_END); // loaded
|
|
73
|
+
|
|
55
74
|
this.setState({
|
|
56
75
|
emoji: foundEmoji,
|
|
57
76
|
loaded: true
|
|
@@ -73,6 +92,12 @@ export default class ResourcedEmojiComponent extends Component {
|
|
|
73
92
|
this.ready = false;
|
|
74
93
|
}
|
|
75
94
|
|
|
95
|
+
componentDidMount() {
|
|
96
|
+
if (!hasUfoMarked(sampledUfoRenderedEmoji(this.props.emojiId), UfoEmojiTimings.FMP_END)) {
|
|
97
|
+
sampledUfoRenderedEmoji(this.props.emojiId).markFMP();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
76
101
|
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
77
102
|
if (nextProps.emojiProvider !== this.props.emojiProvider || nextProps.emojiId !== this.props.emojiId) {
|
|
78
103
|
this.refreshEmoji(nextProps.emojiProvider, nextProps.emojiId);
|
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
export class UfoErrorBoundary extends React.Component {
|
|
3
|
-
componentDidCatch() {
|
|
4
|
-
this.props.experiences
|
|
3
|
+
componentDidCatch(error, errorInfo) {
|
|
4
|
+
for (const exp of this.props.experiences) {
|
|
5
|
+
exp.failure({
|
|
6
|
+
metadata: {
|
|
7
|
+
source: 'UfoErrorBoundary',
|
|
8
|
+
reason: 'error',
|
|
9
|
+
error: {
|
|
10
|
+
name: error.name,
|
|
11
|
+
message: error.message,
|
|
12
|
+
infoStack: errorInfo.componentStack
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
}
|
|
5
17
|
}
|
|
6
18
|
|
|
7
19
|
render() {
|
|
@@ -22,7 +22,16 @@ export const uploadEmoji = (upload, emojiProvider, errorSetter, onSuccess, fireA
|
|
|
22
22
|
duration: Date.now() - startTime,
|
|
23
23
|
reason: messages.emojiUploadFailed.defaultMessage
|
|
24
24
|
}));
|
|
25
|
-
ufoExperiences['emoji-uploaded'].failure(
|
|
25
|
+
ufoExperiences['emoji-uploaded'].failure({
|
|
26
|
+
metadata: {
|
|
27
|
+
source: 'UploadEmoji',
|
|
28
|
+
error: err,
|
|
29
|
+
data: {
|
|
30
|
+
upload: { ...upload
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
});
|
|
26
35
|
});
|
|
27
36
|
}
|
|
28
37
|
};
|
|
@@ -294,6 +294,10 @@ export default class EmojiPickerComponent extends PureComponent {
|
|
|
294
294
|
_defineProperty(this, "scrollToEndOfList", () => {
|
|
295
295
|
const emojiPickerList = this.refs.emojiPickerList;
|
|
296
296
|
|
|
297
|
+
if (typeof window === 'undefined') {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
|
|
297
301
|
if (emojiPickerList) {
|
|
298
302
|
// Wait a tick to ensure repaint and updated height for picker list
|
|
299
303
|
window.setTimeout(() => {
|
|
@@ -395,9 +399,18 @@ export default class EmojiPickerComponent extends PureComponent {
|
|
|
395
399
|
this.fireAnalytics(closedPickerEvent({
|
|
396
400
|
duration: this.calculateElapsedTime()
|
|
397
401
|
}));
|
|
398
|
-
ufoExperiences['emoji-picker-opened'].abort(
|
|
399
|
-
|
|
400
|
-
|
|
402
|
+
ufoExperiences['emoji-picker-opened'].abort({
|
|
403
|
+
metadata: {
|
|
404
|
+
source: 'EmojiPickerComponent',
|
|
405
|
+
reason: 'unmount'
|
|
406
|
+
}
|
|
407
|
+
});
|
|
408
|
+
ufoExperiences['emoji-searched'].abort({
|
|
409
|
+
metadata: {
|
|
410
|
+
source: 'EmojiPickerComponent',
|
|
411
|
+
reason: 'unmount'
|
|
412
|
+
}
|
|
413
|
+
});
|
|
401
414
|
}
|
|
402
415
|
|
|
403
416
|
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
@@ -158,6 +158,9 @@ export const emojiItem = css({
|
|
|
158
158
|
minWidth: '24px',
|
|
159
159
|
maxWidth: '24px'
|
|
160
160
|
},
|
|
161
|
+
[`& .${emojiNodeStyles} .${placeholder}`]: {
|
|
162
|
+
margin: '0'
|
|
163
|
+
},
|
|
161
164
|
// Fit non-square emoji to square
|
|
162
165
|
[`& .${emojiNodeStyles} > img`]: {
|
|
163
166
|
position: 'relative',
|
|
@@ -172,8 +172,24 @@ export default class EmojiTypeAheadComponent extends PureComponent {
|
|
|
172
172
|
this.fireAnalyticsEvent(typeaheadCancelledEvent(Date.now() - this.openTime, query, emojis));
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
-
ufoExperiences['emoji-searched'].abort(
|
|
176
|
-
|
|
175
|
+
ufoExperiences['emoji-searched'].abort({
|
|
176
|
+
metadata: {
|
|
177
|
+
source: 'EmojiTypeAheadComponent',
|
|
178
|
+
reason: 'unmount',
|
|
179
|
+
data: {
|
|
180
|
+
query
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
ufoExperiences['emoji-selection-recorded'].abort({
|
|
185
|
+
metadata: {
|
|
186
|
+
source: 'EmojiTypeAheadComponent',
|
|
187
|
+
reason: 'unmount',
|
|
188
|
+
data: {
|
|
189
|
+
query
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
});
|
|
177
193
|
this.sessionId = uuid();
|
|
178
194
|
this.selected = false;
|
|
179
195
|
}
|
|
@@ -84,7 +84,12 @@ export default class EmojiUploadComponent extends PureComponent {
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
componentWillUnmount() {
|
|
87
|
-
ufoExperiences['emoji-uploaded'].abort(
|
|
87
|
+
ufoExperiences['emoji-uploaded'].abort({
|
|
88
|
+
metadata: {
|
|
89
|
+
source: 'EmojiUploadComponent',
|
|
90
|
+
reason: 'unmount'
|
|
91
|
+
}
|
|
92
|
+
});
|
|
88
93
|
}
|
|
89
94
|
|
|
90
95
|
render() {
|
package/dist/es2019/types.js
CHANGED
|
@@ -61,4 +61,27 @@ export let UfoComponentName;
|
|
|
61
61
|
UfoComponentName["EMOJI"] = "emoji";
|
|
62
62
|
UfoComponentName["EMOJI_PICKER"] = "emoji-picker";
|
|
63
63
|
UfoComponentName["EMOJI_PROVIDER"] = "emoji-provider";
|
|
64
|
-
})(UfoComponentName || (UfoComponentName = {}));
|
|
64
|
+
})(UfoComponentName || (UfoComponentName = {}));
|
|
65
|
+
|
|
66
|
+
export let UfoEmojiTimingsKeys;
|
|
67
|
+
|
|
68
|
+
(function (UfoEmojiTimingsKeys) {
|
|
69
|
+
UfoEmojiTimingsKeys["FMP"] = "fmp";
|
|
70
|
+
UfoEmojiTimingsKeys["MOUNTED"] = "emoji-mount";
|
|
71
|
+
UfoEmojiTimingsKeys["METADATA"] = "emoji-metadata";
|
|
72
|
+
UfoEmojiTimingsKeys["MEDIADATA"] = "emoji-media";
|
|
73
|
+
UfoEmojiTimingsKeys["ONLOAD"] = "emoji-onload";
|
|
74
|
+
})(UfoEmojiTimingsKeys || (UfoEmojiTimingsKeys = {}));
|
|
75
|
+
|
|
76
|
+
export let UfoEmojiTimings;
|
|
77
|
+
|
|
78
|
+
(function (UfoEmojiTimings) {
|
|
79
|
+
UfoEmojiTimings["FMP_END"] = "fmp";
|
|
80
|
+
UfoEmojiTimings["MOUNTED_END"] = "emoji-mount_end";
|
|
81
|
+
UfoEmojiTimings["METADATA_START"] = "emoji-metadata_start";
|
|
82
|
+
UfoEmojiTimings["METADATA_END"] = "emoji-metadata_end";
|
|
83
|
+
UfoEmojiTimings["MEDIA_START"] = "emoji-media_start";
|
|
84
|
+
UfoEmojiTimings["MEDIA_END"] = "emoji-media_end";
|
|
85
|
+
UfoEmojiTimings["ONLOAD_START"] = "emoji-onload_start";
|
|
86
|
+
UfoEmojiTimings["ONLOAD_END"] = "emoji-onload_end";
|
|
87
|
+
})(UfoEmojiTimings || (UfoEmojiTimings = {}));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { UfoComponentName, UfoExperienceName } from '../../types';
|
|
1
|
+
import { UfoComponentName, UfoEmojiTimings, UfoEmojiTimingsKeys, UfoExperienceName } from '../../types';
|
|
2
2
|
import { ExperiencePerformanceTypes, ExperienceTypes, ConcurrentExperience, UFOExperience } from '@atlaskit/ufo';
|
|
3
3
|
import { withSampling } from './samplingUfo';
|
|
4
4
|
|
|
@@ -22,8 +22,37 @@ const createInlineExperience = componentName => {
|
|
|
22
22
|
};
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
+
const customEmojiTimings = [{
|
|
26
|
+
key: UfoEmojiTimingsKeys.FMP,
|
|
27
|
+
endMark: UfoEmojiTimings.FMP_END
|
|
28
|
+
}, {
|
|
29
|
+
key: UfoEmojiTimingsKeys.METADATA,
|
|
30
|
+
component: 'resourced-emoji',
|
|
31
|
+
startMark: UfoEmojiTimings.METADATA_START,
|
|
32
|
+
endMark: UfoEmojiTimings.METADATA_END
|
|
33
|
+
}, {
|
|
34
|
+
key: UfoEmojiTimingsKeys.MEDIADATA,
|
|
35
|
+
component: 'caching-emoji',
|
|
36
|
+
startMark: UfoEmojiTimings.MEDIA_START,
|
|
37
|
+
endMark: UfoEmojiTimings.MEDIA_END
|
|
38
|
+
}, {
|
|
39
|
+
key: UfoEmojiTimingsKeys.MOUNTED,
|
|
40
|
+
component: 'emoji',
|
|
41
|
+
endMark: UfoEmojiTimings.MOUNTED_END
|
|
42
|
+
}, {
|
|
43
|
+
key: UfoEmojiTimingsKeys.ONLOAD,
|
|
44
|
+
startMark: UfoEmojiTimings.ONLOAD_START,
|
|
45
|
+
endMark: UfoEmojiTimings.ONLOAD_END
|
|
46
|
+
}];
|
|
25
47
|
export const ufoExperiences = {
|
|
26
|
-
'emoji-rendered': new ConcurrentExperience(UfoExperienceName.EMOJI_RENDERED,
|
|
48
|
+
'emoji-rendered': new ConcurrentExperience(UfoExperienceName.EMOJI_RENDERED, {
|
|
49
|
+
platform: {
|
|
50
|
+
component: UfoComponentName.EMOJI
|
|
51
|
+
},
|
|
52
|
+
type: ExperienceTypes.Operation,
|
|
53
|
+
performanceType: ExperiencePerformanceTypes.Custom,
|
|
54
|
+
timings: customEmojiTimings
|
|
55
|
+
}),
|
|
27
56
|
'emoji-resource-fetched': new ConcurrentExperience(UfoExperienceName.EMOJI_RESOURCE_FETCHED, createRenderExperience(UfoComponentName.EMOJI_PROVIDER)),
|
|
28
57
|
'emoji-picker-opened': new UFOExperience(UfoExperienceName.EMOJI_PICKER_OPENED, createRenderExperience(UfoComponentName.EMOJI_PICKER)),
|
|
29
58
|
'emoji-selection-recorded': new UFOExperience(UfoExperienceName.EMOJI_SELECTION_RECORDED, createInlineExperience(UfoComponentName.EMOJI_PROVIDER)),
|
|
@@ -32,4 +61,10 @@ export const ufoExperiences = {
|
|
|
32
61
|
};
|
|
33
62
|
export const sampledUfoRenderedEmoji = emojiId => {
|
|
34
63
|
return withSampling(ufoExperiences['emoji-rendered'].getInstance(emojiId.id || emojiId.shortName));
|
|
64
|
+
};
|
|
65
|
+
export const hasUfoMarked = (ufoExperience, name) => {
|
|
66
|
+
return ufoExperience.metrics.marks.some(mask => mask.name === name);
|
|
67
|
+
};
|
|
68
|
+
export const sampledUfoEmojiResourceFetched = providerType => {
|
|
69
|
+
return withSampling(ufoExperiences['emoji-resource-fetched'].getInstance(providerType));
|
|
35
70
|
};
|
|
@@ -30,8 +30,9 @@ export const useSampledUFOComponentExperience = (experience, samplingRate, metad
|
|
|
30
30
|
withSampling(experience).start({
|
|
31
31
|
samplingRate
|
|
32
32
|
});
|
|
33
|
+
const isMetadataEmpty = Object.keys(experience.metadata).length === 0;
|
|
33
34
|
|
|
34
|
-
if (metadata) {
|
|
35
|
+
if (metadata && isMetadataEmpty) {
|
|
35
36
|
experience.addMetadata(metadata);
|
|
36
37
|
}
|
|
37
38
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const isIntersectionObserverSupported = typeof window !== 'undefined' && 'IntersectionObserver' in window && 'IntersectionObserverEntry' in window && 'intersectionRatio' in window.IntersectionObserverEntry.prototype;
|
|
@@ -21,6 +21,6 @@ export const defaultListLimit = 50;
|
|
|
21
21
|
export const migrationUserId = 'hipchat_migration_emoticons';
|
|
22
22
|
export const analyticsEmojiPrefix = 'atlassian.fabric.emoji.picker'; // This is the base sampling rate in Emoji
|
|
23
23
|
|
|
24
|
-
export const SAMPLING_RATE_EMOJI_RENDERED_EXP = 0.05; // This rate is used in
|
|
24
|
+
export const SAMPLING_RATE_EMOJI_RENDERED_EXP = 0.05; // This rate is used in fetching emoji resource
|
|
25
25
|
|
|
26
|
-
export const
|
|
26
|
+
export const SAMPLING_RATE_EMOJI_RESOURCE_FETCHED_EXP = 0.01;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
// Copied from https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API#feature-detecting_localstorage
|
|
2
2
|
// a brief history of local storage - https://gist.github.com/paulirish/5558557
|
|
3
3
|
export default function storageAvailable(type) {
|
|
4
|
+
if (typeof window === 'undefined') {
|
|
5
|
+
return false;
|
|
6
|
+
}
|
|
7
|
+
|
|
4
8
|
var storage;
|
|
5
9
|
|
|
6
10
|
try {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useInView as useInViewLib } from 'react-intersection-observer';
|
|
2
|
+
import { isIntersectionObserverSupported } from './browser-support';
|
|
3
|
+
export const useInView = options => {
|
|
4
|
+
const hookResult = useInViewLib(options);
|
|
5
|
+
|
|
6
|
+
if (!isIntersectionObserverSupported) {
|
|
7
|
+
// Unsupported, return no `ref` and default `inView` true value
|
|
8
|
+
return [undefined, true];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return hookResult;
|
|
12
|
+
};
|
package/dist/es2019/version.json
CHANGED