@atlaskit/emoji 65.0.0 → 65.2.0
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 +36 -0
- package/dist/cjs/components/common/CachingEmoji.js +84 -151
- package/dist/cjs/components/common/Emoji.js +2 -2
- package/dist/cjs/components/common/EmojiActions.js +129 -175
- package/dist/cjs/components/common/EmojiErrorMessage.js +23 -59
- package/dist/cjs/components/common/EmojiPreviewComponent.js +1 -0
- package/dist/cjs/components/common/EmojiUploadPicker.js +235 -293
- package/dist/cjs/components/common/FileChooser.js +34 -71
- package/dist/cjs/components/common/Popup.js +105 -154
- package/dist/cjs/components/common/ResourcedEmojiComponent.js +10 -5
- package/dist/cjs/components/common/RetryableButton.js +43 -64
- package/dist/cjs/components/common/ToneSelector.js +50 -89
- package/dist/cjs/components/common/styles.js +14 -16
- package/dist/cjs/components/hooks.js +16 -0
- package/dist/cjs/components/picker/EmojiPickerCategoryHeading.js +16 -48
- package/dist/cjs/components/picker/EmojiPickerComponent.js +496 -508
- package/dist/cjs/components/picker/EmojiPickerEmojiRow.js +33 -60
- package/dist/cjs/components/picker/EmojiPickerFooter.js +13 -46
- package/dist/cjs/components/picker/styles.js +16 -13
- package/dist/cjs/components/uploader/EmojiUploadComponent.js +124 -109
- package/dist/cjs/hooks/useEmojiContext.js +16 -0
- package/dist/cjs/hooks/usePrevious.js +16 -0
- package/dist/cjs/index.js +16 -0
- package/dist/cjs/util/constants.js +3 -1
- package/dist/cjs/version.json +1 -1
- package/dist/es2019/components/common/CachingEmoji.js +65 -112
- package/dist/es2019/components/common/Emoji.js +2 -2
- package/dist/es2019/components/common/EmojiActions.js +124 -150
- package/dist/es2019/components/common/EmojiErrorMessage.js +22 -26
- package/dist/es2019/components/common/EmojiPreviewComponent.js +1 -0
- package/dist/es2019/components/common/EmojiUploadPicker.js +190 -253
- package/dist/es2019/components/common/FileChooser.js +37 -40
- package/dist/es2019/components/common/Popup.js +89 -109
- package/dist/es2019/components/common/ResourcedEmojiComponent.js +5 -4
- package/dist/es2019/components/common/RetryableButton.js +43 -34
- package/dist/es2019/components/common/ToneSelector.js +46 -59
- package/dist/es2019/components/common/styles.js +9 -9
- package/dist/es2019/components/hooks.js +8 -0
- package/dist/es2019/components/picker/EmojiPickerCategoryHeading.js +13 -17
- package/dist/es2019/components/picker/EmojiPickerComponent.js +417 -497
- package/dist/es2019/components/picker/EmojiPickerEmojiRow.js +32 -35
- package/dist/es2019/components/picker/EmojiPickerFooter.js +11 -19
- package/dist/es2019/components/picker/styles.js +16 -14
- package/dist/es2019/components/uploader/EmojiUploadComponent.js +81 -91
- package/dist/es2019/hooks/useEmojiContext.js +3 -0
- package/dist/es2019/hooks/usePrevious.js +8 -0
- package/dist/es2019/index.js +4 -1
- package/dist/es2019/util/constants.js +1 -0
- package/dist/es2019/version.json +1 -1
- package/dist/esm/components/common/CachingEmoji.js +86 -156
- package/dist/esm/components/common/Emoji.js +2 -2
- package/dist/esm/components/common/EmojiActions.js +129 -176
- package/dist/esm/components/common/EmojiErrorMessage.js +21 -56
- package/dist/esm/components/common/EmojiPreviewComponent.js +1 -0
- package/dist/esm/components/common/EmojiUploadPicker.js +233 -299
- package/dist/esm/components/common/FileChooser.js +34 -70
- package/dist/esm/components/common/Popup.js +104 -155
- package/dist/esm/components/common/ResourcedEmojiComponent.js +8 -4
- package/dist/esm/components/common/RetryableButton.js +40 -60
- package/dist/esm/components/common/ToneSelector.js +50 -87
- package/dist/esm/components/common/styles.js +10 -10
- package/dist/esm/components/hooks.js +8 -0
- package/dist/esm/components/picker/EmojiPickerCategoryHeading.js +14 -43
- package/dist/esm/components/picker/EmojiPickerComponent.js +486 -526
- package/dist/esm/components/picker/EmojiPickerEmojiRow.js +31 -59
- package/dist/esm/components/picker/EmojiPickerFooter.js +14 -47
- package/dist/esm/components/picker/styles.js +16 -14
- package/dist/esm/components/uploader/EmojiUploadComponent.js +119 -113
- package/dist/esm/hooks/useEmojiContext.js +5 -0
- package/dist/esm/hooks/usePrevious.js +8 -0
- package/dist/esm/index.js +4 -1
- package/dist/esm/util/constants.js +1 -0
- package/dist/esm/version.json +1 -1
- package/dist/types/api/EmojiResource.d.ts +2 -3
- package/dist/types/components/common/CachingEmoji.d.ts +3 -13
- package/dist/types/components/common/Emoji.d.ts +1 -2
- package/dist/types/components/common/EmojiActions.d.ts +6 -17
- package/dist/types/components/common/EmojiErrorMessage.d.ts +3 -6
- package/dist/types/components/common/EmojiPreviewComponent.d.ts +2 -2
- package/dist/types/components/common/EmojiUploadPicker.d.ts +3 -27
- package/dist/types/components/common/FileChooser.d.ts +3 -5
- package/dist/types/components/common/LoadingEmojiComponent.d.ts +3 -0
- package/dist/types/components/common/Popup.d.ts +3 -20
- package/dist/types/components/common/ResourcedEmojiComponent.d.ts +23 -11
- package/dist/types/components/common/RetryableButton.d.ts +3 -7
- package/dist/types/components/common/ToneSelector.d.ts +4 -10
- package/dist/types/components/common/setSkinToneAriaLabelText.d.ts +1 -1
- package/dist/types/components/common/styles.d.ts +1 -3
- package/dist/types/components/hooks.d.ts +1 -0
- package/dist/types/components/picker/CategorySelector.d.ts +1 -1
- package/dist/types/components/picker/EmojiPicker.d.ts +12 -3
- package/dist/types/components/picker/EmojiPickerCategoryHeading.d.ts +3 -4
- package/dist/types/components/picker/EmojiPickerComponent.d.ts +14 -76
- package/dist/types/components/picker/EmojiPickerEmojiRow.d.ts +3 -4
- package/dist/types/components/picker/EmojiPickerFooter.d.ts +3 -5
- package/dist/types/components/picker/styles.d.ts +1 -1
- package/dist/types/components/typeahead/EmojiTypeAheadComponent.d.ts +18 -0
- package/dist/types/components/uploader/EmojiUploadComponent.d.ts +3 -17
- package/dist/types/components/uploader/EmojiUploader.d.ts +5 -7
- package/dist/types/hooks/useEmojiContext.d.ts +1 -0
- package/dist/types/hooks/usePrevious.d.ts +1 -0
- package/dist/types/index.d.ts +3 -1
- package/dist/types/types.d.ts +2 -1
- package/dist/types/util/constants.d.ts +1 -0
- package/docs/0-intro.tsx +35 -27
- package/docs/1-resourced-emoji.tsx +74 -0
- package/docs/2-emoji-picker.tsx +56 -0
- package/docs/3-typeahead.tsx +20 -0
- package/docs/4-emoji-provider.tsx +98 -0
- package/local-config-example.ts +3 -1
- package/package.json +19 -6
- package/dist/cjs/components/common/EmojiPreview.js +0 -194
- package/dist/es2019/components/common/EmojiPreview.js +0 -152
- package/dist/esm/components/common/EmojiPreview.js +0 -170
- package/dist/types/components/common/EmojiPreview.d.ts +0 -31
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
-
|
|
3
1
|
/** @jsx jsx */
|
|
4
2
|
import { jsx } from '@emotion/core';
|
|
5
|
-
import {
|
|
3
|
+
import { useCallback, useEffect, useMemo, useRef, useState, createRef, memo } from 'react';
|
|
4
|
+
import { unstable_batchedUpdates as batchedUpdates } from 'react-dom';
|
|
6
5
|
import { FormattedMessage } from 'react-intl-next';
|
|
7
6
|
import { getEmojiVariation } from '../../api/EmojiRepository';
|
|
8
7
|
import { supportsUploadFeature } from '../../api/EmojiResource';
|
|
@@ -20,566 +19,487 @@ import EmojiPickerList from './EmojiPickerList';
|
|
|
20
19
|
import { createAndFireEventInElementsChannel, categoryClickedEvent, closedPickerEvent, deleteBeginEvent, deleteCancelEvent, deleteConfirmEvent, openedPickerEvent, pickerClickedEvent, pickerSearchedEvent, selectedFileEvent, uploadBeginButton, uploadCancelButton, uploadConfirmButton, toneSelectorClosedEvent, ufoExperiences } from '../../util/analytics';
|
|
21
20
|
import { emojiPicker } from './styles';
|
|
22
21
|
import LegacyEmojiContextProvider from '../../context/LegacyEmojiContextProvider';
|
|
22
|
+
import { useDidMount } from '../hooks';
|
|
23
23
|
const FREQUENTLY_USED_MAX = 16;
|
|
24
|
-
export default class EmojiPickerComponent extends PureComponent {
|
|
25
|
-
constructor(props) {
|
|
26
|
-
super(props);
|
|
27
|
-
|
|
28
|
-
_defineProperty(this, "onEmojiActive", (_emojiId, emoji) => {
|
|
29
|
-
if (this.state.selectedEmoji !== emoji) {
|
|
30
|
-
this.setState({
|
|
31
|
-
selectedEmoji: emoji
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
});
|
|
35
24
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
25
|
+
const EmojiPickerComponent = ({
|
|
26
|
+
emojiProvider,
|
|
27
|
+
onSelection,
|
|
28
|
+
onPickerRef,
|
|
29
|
+
hideToneSelector,
|
|
30
|
+
createAnalyticsEvent
|
|
31
|
+
}) => {
|
|
32
|
+
const [filteredEmojis, setFilteredEmojis] = useState([]);
|
|
33
|
+
const [searchEmojis, setSearchEmojis] = useState([]);
|
|
34
|
+
const [frequentlyUsedEmojis, setFrequentlyUsedEmojis] = useState([]);
|
|
35
|
+
const [query, setQuery] = useState('');
|
|
36
|
+
const [dynamicCategories, setDynamicCategories] = useState([]);
|
|
37
|
+
const [selectedTone, setSelectedTone] = useState(!hideToneSelector ? emojiProvider.getSelectedTone() : undefined);
|
|
38
|
+
const [loading, setLoading] = useState(true);
|
|
39
|
+
const [uploadSupported, setUploadSupported] = useState(false);
|
|
40
|
+
const [uploading, setUploading] = useState(false);
|
|
41
|
+
const [selectedEmoji, setSelectedEmoji] = useState();
|
|
42
|
+
const [activeCategory, setActiveCategory] = useState(null);
|
|
43
|
+
const [disableCategories, setDisableCategories] = useState(false);
|
|
44
|
+
const [uploadErrorMessage, setUploadErrorMessage] = useState();
|
|
45
|
+
const [emojiToDelete, setEmojiToDelete] = useState();
|
|
46
|
+
const [toneEmoji, setToneEmoji] = useState();
|
|
47
|
+
const emojiPickerList = useMemo(() => /*#__PURE__*/createRef(), []);
|
|
48
|
+
const openTime = useRef(0);
|
|
49
|
+
const isMounting = useRef(true);
|
|
50
|
+
const didMount = useDidMount();
|
|
51
|
+
const updateAfterDidMount = useRef(true);
|
|
52
|
+
const previousEmojiProvider = useRef(emojiProvider);
|
|
53
|
+
const currentUser = useMemo(() => {
|
|
54
|
+
return emojiProvider.getCurrentUser();
|
|
55
|
+
}, [emojiProvider]);
|
|
56
|
+
const fireAnalytics = useCallback(analyticsEvent => {
|
|
57
|
+
if (createAnalyticsEvent) {
|
|
58
|
+
createAndFireEventInElementsChannel(analyticsEvent)(createAnalyticsEvent);
|
|
59
|
+
}
|
|
60
|
+
}, [createAnalyticsEvent]);
|
|
61
|
+
const onEmojiActive = useCallback((emojiId, emoji) => {
|
|
62
|
+
if (!selectedEmoji || selectedEmoji.id !== (emojiId === null || emojiId === void 0 ? void 0 : emojiId.id)) {
|
|
63
|
+
setSelectedEmoji(emoji);
|
|
64
|
+
}
|
|
65
|
+
}, [selectedEmoji]);
|
|
66
|
+
const onCategoryActivated = useCallback(category => {
|
|
67
|
+
if (activeCategory !== category) {
|
|
68
|
+
setActiveCategory(category);
|
|
69
|
+
}
|
|
70
|
+
}, [activeCategory]);
|
|
71
|
+
|
|
72
|
+
const calculateElapsedTime = () => {
|
|
73
|
+
return Date.now() - openTime.current;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const onUploadSupported = useCallback(supported => {
|
|
77
|
+
setUploadSupported(supported);
|
|
78
|
+
}, []);
|
|
79
|
+
const onDynamicCategoryChange = useCallback(categories => {
|
|
80
|
+
setDynamicCategories(categories);
|
|
81
|
+
}, []);
|
|
82
|
+
const onUploadCancelled = useCallback(() => {
|
|
83
|
+
batchedUpdates(() => {
|
|
84
|
+
setUploading(false);
|
|
85
|
+
setUploadErrorMessage(undefined);
|
|
42
86
|
});
|
|
87
|
+
fireAnalytics(uploadCancelButton());
|
|
88
|
+
}, [fireAnalytics]);
|
|
89
|
+
const getDynamicCategories = useCallback(() => {
|
|
90
|
+
if (!emojiProvider.calculateDynamicCategories) {
|
|
91
|
+
return Promise.resolve([]);
|
|
92
|
+
}
|
|
43
93
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
emojiProvider.findInCategory(categoryId).then(emojisInCategory => {
|
|
54
|
-
const {
|
|
55
|
-
disableCategories
|
|
56
|
-
} = this.state;
|
|
57
|
-
|
|
58
|
-
if (!disableCategories) {
|
|
59
|
-
let selectedEmoji;
|
|
60
|
-
|
|
61
|
-
if (emojisInCategory && emojisInCategory.length > 0) {
|
|
62
|
-
selectedEmoji = getEmojiVariation(emojisInCategory[0], {
|
|
63
|
-
skinTone: this.state.selectedTone
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const emojiPickerList = this.refs.emojiPickerList;
|
|
68
|
-
|
|
69
|
-
if (emojiPickerList) {
|
|
70
|
-
emojiPickerList.reveal(categoryId);
|
|
71
|
-
}
|
|
94
|
+
return emojiProvider.calculateDynamicCategories();
|
|
95
|
+
}, [emojiProvider]);
|
|
96
|
+
/**
|
|
97
|
+
* Calculate and set the new state of the component in response to the list of emoji changing for some reason (a search has returned
|
|
98
|
+
* or the frequently used emoji have updated.)
|
|
99
|
+
*/
|
|
72
100
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
101
|
+
const setStateAfterEmojiChange = useCallback(({
|
|
102
|
+
searchQuery,
|
|
103
|
+
emojiToRender,
|
|
104
|
+
searchEmoji,
|
|
105
|
+
frequentEmoji
|
|
106
|
+
}) => {
|
|
107
|
+
// Only enable categories for full emoji list (non-search)
|
|
108
|
+
const disableCategories = !!searchQuery;
|
|
109
|
+
|
|
110
|
+
if (!disableCategories && emojiToRender && emojiToRender.length !== filteredEmojis.length) {
|
|
111
|
+
getDynamicCategories().then(categories => {
|
|
112
|
+
onDynamicCategoryChange(categories);
|
|
81
113
|
});
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
_defineProperty(this, "onFileChooserClicked", () => {
|
|
85
|
-
this.fireAnalytics(selectedFileEvent());
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
_defineProperty(this, "fireAnalytics", analyticsEvent => {
|
|
89
|
-
const {
|
|
90
|
-
createAnalyticsEvent
|
|
91
|
-
} = this.props;
|
|
92
|
-
|
|
93
|
-
if (createAnalyticsEvent) {
|
|
94
|
-
createAndFireEventInElementsChannel(analyticsEvent)(createAnalyticsEvent);
|
|
95
|
-
}
|
|
96
|
-
});
|
|
114
|
+
}
|
|
97
115
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
116
|
+
if (emojiToRender && !containsEmojiId(emojiToRender, selectedEmoji)) {
|
|
117
|
+
batchedUpdates(() => {
|
|
118
|
+
setSelectedEmoji(undefined); // Only enable categories for full emoji list (non-search)
|
|
101
119
|
|
|
102
|
-
|
|
103
|
-
this.setState({
|
|
104
|
-
uploadSupported: supported
|
|
120
|
+
setActiveCategory(null);
|
|
105
121
|
});
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
_defineProperty(this, "onSearch", query => {
|
|
109
|
-
const options = {
|
|
110
|
-
skinTone: this.state.selectedTone
|
|
111
|
-
};
|
|
122
|
+
}
|
|
112
123
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
queryLength: query.length,
|
|
117
|
-
source: 'picker'
|
|
118
|
-
});
|
|
124
|
+
batchedUpdates(() => {
|
|
125
|
+
if (emojiToRender) {
|
|
126
|
+
setFilteredEmojis(emojiToRender);
|
|
119
127
|
}
|
|
120
128
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
_defineProperty(this, "onSearchResult", searchResults => {
|
|
125
|
-
const frequentlyUsedEmoji = this.state.frequentlyUsedEmojis || [];
|
|
126
|
-
const searchQuery = searchResults.query || '';
|
|
127
|
-
const emojiToRender = this.buildQuerySpecificEmojiList(searchQuery, searchResults.emojis, frequentlyUsedEmoji);
|
|
128
|
-
|
|
129
|
-
if (searchQuery !== this.state.query) {
|
|
130
|
-
this.fireAnalytics(pickerSearchedEvent({
|
|
131
|
-
queryLength: searchQuery.length,
|
|
132
|
-
numMatches: emojiToRender.length
|
|
133
|
-
}));
|
|
134
|
-
ufoExperiences['emoji-searched'].success({
|
|
135
|
-
metadata: {
|
|
136
|
-
emojisLength: emojiToRender.length
|
|
137
|
-
}
|
|
138
|
-
});
|
|
129
|
+
if (searchEmoji) {
|
|
130
|
+
setSearchEmojis(searchEmoji);
|
|
139
131
|
}
|
|
140
132
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
_defineProperty(this, "onFrequentEmojiResult", frequentEmoji => {
|
|
145
|
-
const {
|
|
146
|
-
query,
|
|
147
|
-
searchEmojis
|
|
148
|
-
} = this.state; // change the category of each of the featured emoji
|
|
149
|
-
|
|
150
|
-
const recategorised = frequentEmoji.map(emoji => {
|
|
151
|
-
const clone = JSON.parse(JSON.stringify(emoji));
|
|
152
|
-
clone.category = frequentCategory;
|
|
153
|
-
return clone;
|
|
154
|
-
});
|
|
155
|
-
const emojiToRender = this.buildQuerySpecificEmojiList(query, searchEmojis, recategorised);
|
|
156
|
-
this.setStateAfterEmojiChange(query, emojiToRender, searchEmojis, recategorised);
|
|
157
|
-
});
|
|
133
|
+
if (frequentEmoji) {
|
|
134
|
+
setFrequentlyUsedEmojis(frequentEmoji);
|
|
135
|
+
}
|
|
158
136
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
dynamicCategories: categories
|
|
162
|
-
});
|
|
137
|
+
setLoading(false);
|
|
138
|
+
setDisableCategories(disableCategories);
|
|
163
139
|
});
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
140
|
+
}, [filteredEmojis.length, getDynamicCategories, onDynamicCategoryChange, selectedEmoji]);
|
|
141
|
+
const onFrequentEmojiResult = useCallback(frequentEmoji => {
|
|
142
|
+
// change the category of each of the featured emoji
|
|
143
|
+
const recategorised = frequentEmoji.map(emoji => {
|
|
144
|
+
const clone = JSON.parse(JSON.stringify(emoji));
|
|
145
|
+
clone.category = frequentCategory;
|
|
146
|
+
return clone;
|
|
167
147
|
});
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
this.setState({
|
|
171
|
-
selectedTone: toneValue
|
|
172
|
-
});
|
|
173
|
-
this.props.emojiProvider.setSelectedTone(toneValue);
|
|
174
|
-
const {
|
|
175
|
-
query = ''
|
|
176
|
-
} = this.state;
|
|
177
|
-
this.updateEmojis(query, {
|
|
178
|
-
skinTone: toneValue
|
|
179
|
-
});
|
|
148
|
+
setStateAfterEmojiChange({
|
|
149
|
+
frequentEmoji: recategorised
|
|
180
150
|
});
|
|
151
|
+
}, [setStateAfterEmojiChange]);
|
|
152
|
+
const onSearchResult = useCallback(searchResults => {
|
|
153
|
+
const frequentlyUsedEmoji = frequentlyUsedEmojis || [];
|
|
154
|
+
const searchQuery = searchResults.query || '';
|
|
155
|
+
/**
|
|
156
|
+
* If there is no user search in the EmojiPicker then it should display all emoji received from the EmojiRepository and should
|
|
157
|
+
* also include a special category of most frequently used emoji (if there are any). This method decides if we are in this 'no search'
|
|
158
|
+
* state and appends the frequent emoji if necessary.
|
|
159
|
+
*/
|
|
160
|
+
|
|
161
|
+
let emojiToRender;
|
|
162
|
+
|
|
163
|
+
if (!frequentlyUsedEmoji.length || query) {
|
|
164
|
+
emojiToRender = searchResults.emojis;
|
|
165
|
+
} else {
|
|
166
|
+
emojiToRender = [...searchResults.emojis, ...frequentlyUsedEmoji];
|
|
167
|
+
}
|
|
181
168
|
|
|
182
|
-
|
|
183
|
-
|
|
169
|
+
setStateAfterEmojiChange({
|
|
170
|
+
searchQuery,
|
|
171
|
+
emojiToRender,
|
|
172
|
+
searchEmoji: searchResults.emojis
|
|
184
173
|
});
|
|
174
|
+
}, [frequentlyUsedEmojis, query, setStateAfterEmojiChange]);
|
|
175
|
+
const onProviderChange = useMemo(() => {
|
|
176
|
+
return {
|
|
177
|
+
result: onSearchResult
|
|
178
|
+
};
|
|
179
|
+
}, [onSearchResult]);
|
|
180
|
+
/**
|
|
181
|
+
* Updates the emoji displayed by the picker. If there is no query specified then we expect to retrieve all emoji for display,
|
|
182
|
+
* by category, in the picker. This differs from when there is a query in which case we expect to receive a sorted result matching
|
|
183
|
+
* the search.
|
|
184
|
+
*/
|
|
185
185
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
if (!options.sort) {
|
|
195
|
-
options.sort = SearchSort.None;
|
|
196
|
-
} // take a copy of search options so that the frequently used can be limited to 16 without affecting the full emoji query
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
const frequentOptions = { ...options,
|
|
200
|
-
sort: SearchSort.None,
|
|
201
|
-
limit: FREQUENTLY_USED_MAX
|
|
202
|
-
};
|
|
203
|
-
this.props.emojiProvider.getFrequentlyUsed(frequentOptions).then(this.onFrequentEmojiResult);
|
|
186
|
+
const updateEmojis = useCallback((query, options) => {
|
|
187
|
+
// if the query is empty then we want the emoji to be in service defined order, unless specified otherwise
|
|
188
|
+
// and we want emoji for the 'frequently used' category to be refreshed as well.
|
|
189
|
+
if (!query) {
|
|
190
|
+
if (!options) {
|
|
191
|
+
options = {};
|
|
204
192
|
}
|
|
205
193
|
|
|
206
|
-
|
|
207
|
-
|
|
194
|
+
if (!options.sort) {
|
|
195
|
+
options.sort = SearchSort.None;
|
|
196
|
+
} // take a copy of search options so that the frequently used can be limited to 16 without affecting the full emoji query
|
|
208
197
|
|
|
209
|
-
_defineProperty(this, "onOpenUpload", () => {
|
|
210
|
-
// Prime upload token so it's ready when the user adds
|
|
211
|
-
const {
|
|
212
|
-
emojiProvider
|
|
213
|
-
} = this.props;
|
|
214
198
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
199
|
+
const frequentOptions = { ...options,
|
|
200
|
+
sort: SearchSort.None,
|
|
201
|
+
limit: FREQUENTLY_USED_MAX
|
|
202
|
+
};
|
|
203
|
+
emojiProvider.getFrequentlyUsed(frequentOptions).then(onFrequentEmojiResult);
|
|
204
|
+
}
|
|
218
205
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
206
|
+
emojiProvider.filter(query, options);
|
|
207
|
+
}, [emojiProvider, onFrequentEmojiResult]);
|
|
208
|
+
const onToneSelected = useCallback(toneValue => {
|
|
209
|
+
emojiProvider.setSelectedTone(toneValue);
|
|
210
|
+
updateEmojis(query, {
|
|
211
|
+
skinTone: toneValue
|
|
224
212
|
});
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
213
|
+
setSelectedTone(toneValue);
|
|
214
|
+
}, [emojiProvider, query, updateEmojis]);
|
|
215
|
+
const onToneSelectorCancelled = useCallback(() => {
|
|
216
|
+
fireAnalytics(toneSelectorClosedEvent());
|
|
217
|
+
}, [fireAnalytics]);
|
|
218
|
+
const onSelectWrapper = useCallback((emojiId, emoji, event) => {
|
|
219
|
+
if (onSelection) {
|
|
220
|
+
onSelection(emojiId, emoji, event);
|
|
221
|
+
fireAnalytics(pickerClickedEvent({
|
|
222
|
+
duration: calculateElapsedTime(),
|
|
223
|
+
emojiId: (emojiId === null || emojiId === void 0 ? void 0 : emojiId.id) || '',
|
|
224
|
+
category: emoji && emoji.category || '',
|
|
225
|
+
type: emoji && emoji.type || '',
|
|
226
|
+
queryLength: query && query.length || 0
|
|
232
227
|
}));
|
|
228
|
+
}
|
|
229
|
+
}, [fireAnalytics, onSelection, query]);
|
|
230
|
+
const onCategorySelected = useCallback(categoryId => {
|
|
231
|
+
if (!categoryId) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
233
234
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
});
|
|
238
|
-
};
|
|
235
|
+
emojiProvider.findInCategory(categoryId).then(emojisInCategory => {
|
|
236
|
+
if (!disableCategories) {
|
|
237
|
+
let newSelectedEmoji;
|
|
239
238
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
}); // this.loadEmoji(emojiProvider, emojiDescription);
|
|
239
|
+
if (emojisInCategory && emojisInCategory.length > 0) {
|
|
240
|
+
newSelectedEmoji = getEmojiVariation(emojisInCategory[0], {
|
|
241
|
+
skinTone: selectedTone
|
|
242
|
+
});
|
|
243
|
+
}
|
|
246
244
|
|
|
247
|
-
|
|
248
|
-
|
|
245
|
+
if (emojiPickerList.current) {
|
|
246
|
+
emojiPickerList.current.reveal(categoryId);
|
|
247
|
+
}
|
|
249
248
|
|
|
250
|
-
|
|
249
|
+
batchedUpdates(() => {
|
|
250
|
+
setActiveCategory(categoryId);
|
|
251
|
+
setSelectedEmoji(newSelectedEmoji);
|
|
252
|
+
});
|
|
253
|
+
fireAnalytics(categoryClickedEvent({
|
|
254
|
+
category: categoryId
|
|
255
|
+
}));
|
|
256
|
+
}
|
|
251
257
|
});
|
|
258
|
+
}, [disableCategories, emojiPickerList, emojiProvider, fireAnalytics, selectedTone]);
|
|
259
|
+
const recordUsageOnSelection = useMemo(() => createRecordSelectionDefault(emojiProvider, onSelectWrapper, analytic => fireAnalytics(analytic('picker'))), [emojiProvider, fireAnalytics, onSelectWrapper]);
|
|
260
|
+
const formattedErrorMessage = useMemo(() => uploadErrorMessage ? jsx(FormattedMessage, uploadErrorMessage) : null, [uploadErrorMessage]);
|
|
261
|
+
const emojiContextValue = useMemo(() => ({
|
|
262
|
+
emoji: {
|
|
263
|
+
emojiProvider
|
|
264
|
+
}
|
|
265
|
+
}), [emojiProvider]);
|
|
266
|
+
const onFileChooserClicked = useCallback(() => {
|
|
267
|
+
fireAnalytics(selectedFileEvent());
|
|
268
|
+
}, [fireAnalytics]);
|
|
269
|
+
const onSearch = useCallback(searchQuery => {
|
|
270
|
+
const options = {
|
|
271
|
+
skinTone: selectedTone
|
|
272
|
+
};
|
|
252
273
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
emojiToDelete: emoji
|
|
274
|
+
if (query) {
|
|
275
|
+
ufoExperiences['emoji-searched'].start();
|
|
276
|
+
ufoExperiences['emoji-searched'].addMetadata({
|
|
277
|
+
queryLength: query.length,
|
|
278
|
+
source: 'picker'
|
|
259
279
|
});
|
|
260
|
-
}
|
|
280
|
+
}
|
|
261
281
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
} = this.state;
|
|
266
|
-
this.fireAnalytics(deleteCancelEvent({
|
|
267
|
-
emojiId: emojiToDelete && emojiToDelete.id
|
|
268
|
-
}));
|
|
269
|
-
this.setState({
|
|
270
|
-
emojiToDelete: undefined
|
|
271
|
-
});
|
|
272
|
-
});
|
|
282
|
+
if (searchQuery !== query) {
|
|
283
|
+
setQuery(searchQuery);
|
|
284
|
+
}
|
|
273
285
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
emojiId: emojiToDelete && emojiToDelete.id
|
|
282
|
-
}));
|
|
283
|
-
return this.props.emojiProvider.deleteSiteEmoji(emoji).then(success => {
|
|
284
|
-
if (success) {
|
|
285
|
-
this.updateEmojis(query, {
|
|
286
|
-
skinTone: selectedTone
|
|
287
|
-
});
|
|
288
|
-
}
|
|
286
|
+
updateEmojis(query, options);
|
|
287
|
+
}, [query, selectedTone, updateEmojis]);
|
|
288
|
+
const onOpenUpload = useCallback(() => {
|
|
289
|
+
// Prime upload token so it's ready when the user adds
|
|
290
|
+
if (supportsUploadFeature(emojiProvider)) {
|
|
291
|
+
emojiProvider.prepareForUpload();
|
|
292
|
+
}
|
|
289
293
|
|
|
290
|
-
|
|
291
|
-
|
|
294
|
+
batchedUpdates(() => {
|
|
295
|
+
setUploadErrorMessage(undefined);
|
|
296
|
+
setUploading(true);
|
|
292
297
|
});
|
|
298
|
+
fireAnalytics(uploadBeginButton());
|
|
299
|
+
}, [emojiProvider, fireAnalytics]);
|
|
300
|
+
const scrollToEndOfList = useCallback(() => {
|
|
301
|
+
if (typeof window === 'undefined') {
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
293
304
|
|
|
294
|
-
|
|
295
|
-
|
|
305
|
+
if (emojiPickerList.current) {
|
|
306
|
+
// Wait a tick to ensure repaint and updated height for picker list
|
|
307
|
+
window.setTimeout(() => {
|
|
308
|
+
var _emojiPickerList$curr;
|
|
296
309
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
310
|
+
(_emojiPickerList$curr = emojiPickerList.current) === null || _emojiPickerList$curr === void 0 ? void 0 : _emojiPickerList$curr.scrollToBottom();
|
|
311
|
+
}, 0);
|
|
312
|
+
}
|
|
313
|
+
}, [emojiPickerList]);
|
|
314
|
+
const onUploadEmoji = useCallback((upload, retry) => {
|
|
315
|
+
fireAnalytics(uploadConfirmButton({
|
|
316
|
+
retry
|
|
317
|
+
}));
|
|
300
318
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
emojiPickerList.scrollToBottom();
|
|
305
|
-
}, 0);
|
|
306
|
-
}
|
|
307
|
-
});
|
|
319
|
+
const errorSetter = message => {
|
|
320
|
+
setUploadErrorMessage(message);
|
|
321
|
+
};
|
|
308
322
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
323
|
+
const onSuccess = emojiDescription => {
|
|
324
|
+
batchedUpdates(() => {
|
|
325
|
+
setActiveCategory(customCategory);
|
|
326
|
+
setSelectedEmoji(emojiDescription);
|
|
327
|
+
setUploading(false);
|
|
313
328
|
});
|
|
314
|
-
|
|
315
|
-
}
|
|
329
|
+
scrollToEndOfList();
|
|
330
|
+
};
|
|
316
331
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
332
|
+
uploadEmoji(upload, emojiProvider, errorSetter, onSuccess, fireAnalytics, retry);
|
|
333
|
+
}, [emojiProvider, fireAnalytics, scrollToEndOfList]);
|
|
334
|
+
const onTriggerDelete = useCallback((_emojiId, emoji) => {
|
|
335
|
+
if (_emojiId) {
|
|
336
|
+
fireAnalytics(deleteBeginEvent({
|
|
337
|
+
emojiId: _emojiId.id
|
|
338
|
+
}));
|
|
339
|
+
setEmojiToDelete(emoji);
|
|
340
|
+
}
|
|
341
|
+
}, [fireAnalytics]);
|
|
342
|
+
const onCloseDelete = useCallback(() => {
|
|
343
|
+
fireAnalytics(deleteCancelEvent({
|
|
344
|
+
emojiId: emojiToDelete && emojiToDelete.id
|
|
345
|
+
}));
|
|
346
|
+
setEmojiToDelete(undefined);
|
|
347
|
+
}, [emojiToDelete, fireAnalytics]);
|
|
348
|
+
const onDeleteEmoji = useCallback(emoji => {
|
|
349
|
+
fireAnalytics(deleteConfirmEvent({
|
|
350
|
+
emojiId: emojiToDelete && emojiToDelete.id
|
|
351
|
+
}));
|
|
352
|
+
return emojiProvider.deleteSiteEmoji(emoji).then(success => {
|
|
353
|
+
if (success) {
|
|
354
|
+
updateEmojis(query, {
|
|
355
|
+
skinTone: selectedTone
|
|
356
|
+
});
|
|
320
357
|
}
|
|
321
|
-
});
|
|
322
358
|
|
|
323
|
-
|
|
324
|
-
const {
|
|
325
|
-
onSelection
|
|
326
|
-
} = this.props;
|
|
327
|
-
const {
|
|
328
|
-
query
|
|
329
|
-
} = this.state;
|
|
330
|
-
|
|
331
|
-
if (onSelection) {
|
|
332
|
-
onSelection(emojiId, emoji, event);
|
|
333
|
-
this.fireAnalytics(pickerClickedEvent({
|
|
334
|
-
duration: this.calculateElapsedTime(),
|
|
335
|
-
emojiId: emojiId.id || '',
|
|
336
|
-
category: emoji && emoji.category || '',
|
|
337
|
-
type: emoji && emoji.type || '',
|
|
338
|
-
queryLength: query && query.length || 0
|
|
339
|
-
}));
|
|
340
|
-
}
|
|
359
|
+
return success;
|
|
341
360
|
});
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
} = props;
|
|
347
|
-
this.state = {
|
|
348
|
-
filteredEmojis: [],
|
|
349
|
-
searchEmojis: [],
|
|
350
|
-
frequentlyUsedEmojis: [],
|
|
351
|
-
query: '',
|
|
352
|
-
dynamicCategories: [],
|
|
353
|
-
selectedTone: !hideToneSelector ? _emojiProvider.getSelectedTone() : undefined,
|
|
354
|
-
loading: true,
|
|
355
|
-
uploadSupported: false,
|
|
356
|
-
uploading: false
|
|
357
|
-
};
|
|
358
|
-
this.openTime = 0;
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
UNSAFE_componentWillMount() {
|
|
362
|
-
ufoExperiences['emoji-picker-opened'].success();
|
|
363
|
-
this.openTime = Date.now();
|
|
364
|
-
this.fireAnalytics(openedPickerEvent());
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
componentDidMount() {
|
|
368
|
-
const {
|
|
369
|
-
emojiProvider,
|
|
370
|
-
hideToneSelector
|
|
371
|
-
} = this.props;
|
|
372
|
-
emojiProvider.subscribe(this.onProviderChange);
|
|
373
|
-
this.onSearch(this.state.query);
|
|
361
|
+
}, [emojiProvider, emojiToDelete, fireAnalytics, query, selectedTone, updateEmojis]);
|
|
362
|
+
const onComponentDidMount = useCallback(() => {
|
|
363
|
+
emojiProvider.subscribe(onProviderChange);
|
|
364
|
+
onSearch(query);
|
|
374
365
|
|
|
375
366
|
if (supportsUploadFeature(emojiProvider)) {
|
|
376
|
-
emojiProvider.isUploadSupported().then(
|
|
367
|
+
emojiProvider.isUploadSupported().then(onUploadSupported);
|
|
377
368
|
}
|
|
378
369
|
|
|
379
370
|
if (!hideToneSelector) {
|
|
380
371
|
const toneEmoji = getToneEmoji(emojiProvider);
|
|
381
372
|
|
|
382
373
|
if (isPromise(toneEmoji)) {
|
|
383
|
-
toneEmoji.then(emoji =>
|
|
384
|
-
toneEmoji: emoji
|
|
385
|
-
}));
|
|
374
|
+
toneEmoji.then(emoji => setToneEmoji(emoji));
|
|
386
375
|
} else if (toneEmoji === undefined || isEmojiDescription(toneEmoji)) {
|
|
387
|
-
|
|
388
|
-
toneEmoji
|
|
389
|
-
});
|
|
376
|
+
setToneEmoji(toneEmoji);
|
|
390
377
|
}
|
|
391
378
|
}
|
|
392
|
-
}
|
|
379
|
+
}, [emojiProvider, hideToneSelector, onProviderChange, onSearch, onUploadSupported, query]);
|
|
393
380
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
duration: this.calculateElapsedTime()
|
|
401
|
-
}));
|
|
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
|
-
});
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
417
|
-
const prevEmojiProvider = this.props.emojiProvider;
|
|
418
|
-
const nextEmojiProvider = nextProps.emojiProvider;
|
|
419
|
-
|
|
420
|
-
if (prevEmojiProvider !== nextEmojiProvider) {
|
|
421
|
-
if (supportsUploadFeature(nextEmojiProvider)) {
|
|
422
|
-
nextEmojiProvider.isUploadSupported().then(this.onUploadSupported);
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
componentDidUpdate(prevProps) {
|
|
428
|
-
const prevEmojiProvider = prevProps.emojiProvider;
|
|
429
|
-
const currentEmojiProvider = this.props.emojiProvider;
|
|
430
|
-
|
|
431
|
-
if (prevEmojiProvider !== currentEmojiProvider) {
|
|
432
|
-
prevEmojiProvider.unsubscribe(this.onProviderChange);
|
|
433
|
-
currentEmojiProvider.subscribe(this.onProviderChange); // We changed provider which means we subscribed to filter results for a new subscriber.
|
|
434
|
-
// So we refresh the emoji display with onSearch and we do it here, after the new props have
|
|
435
|
-
// been set since onSearch leads to filter being called on the current emojiProvider.
|
|
436
|
-
// (Calling onSearch in a '...Will...' lifecycle method would lead to filter being called on
|
|
437
|
-
// an emojiProvider we have already unsubscribed from)
|
|
438
|
-
|
|
439
|
-
this.onSearch(this.state.query);
|
|
440
|
-
}
|
|
381
|
+
if (isMounting.current) {
|
|
382
|
+
// componentWillMount equivalent
|
|
383
|
+
ufoExperiences['emoji-picker-opened'].success();
|
|
384
|
+
openTime.current = Date.now();
|
|
385
|
+
fireAnalytics(openedPickerEvent());
|
|
386
|
+
isMounting.current = false;
|
|
441
387
|
}
|
|
442
388
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
* @param searchEmoji the emoji last received from the EmojiRepository after a search (may be empty)
|
|
449
|
-
* @param frequentEmoji the frequently used emoji last received from the EmojiRepository (may be empty)
|
|
450
|
-
*/
|
|
451
|
-
buildQuerySpecificEmojiList(query, searchEmoji, frequentEmoji) {
|
|
452
|
-
// If there are no frequent emoji, or if there was a search query then we want to take the search result exactly as is.
|
|
453
|
-
if (!frequentEmoji.length || query) {
|
|
454
|
-
return searchEmoji;
|
|
389
|
+
useEffect(() => {
|
|
390
|
+
// componentDidMount logic
|
|
391
|
+
if (didMount && updateAfterDidMount.current) {
|
|
392
|
+
onComponentDidMount();
|
|
393
|
+
updateAfterDidMount.current = false;
|
|
455
394
|
}
|
|
395
|
+
}, [didMount, onComponentDidMount]);
|
|
396
|
+
useEffect(() => {
|
|
397
|
+
previousEmojiProvider.current.unsubscribe(onProviderChange);
|
|
398
|
+
previousEmojiProvider.current = emojiProvider;
|
|
399
|
+
emojiProvider.subscribe(onProviderChange);
|
|
456
400
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
/**
|
|
460
|
-
* Calculate and set the new state of the component in response to the list of emoji changing for some reason (a search has returned
|
|
461
|
-
* or the frequently used emoji have updated.)
|
|
462
|
-
*/
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
setStateAfterEmojiChange(query, emojiToRender, searchEmoji, frequentEmoji) {
|
|
466
|
-
const {
|
|
467
|
-
filteredEmojis
|
|
468
|
-
} = this.state; // Only enable categories for full emoji list (non-search)
|
|
469
|
-
|
|
470
|
-
const disableCategories = !!query;
|
|
471
|
-
|
|
472
|
-
if (!disableCategories && emojiToRender.length !== filteredEmojis.length) {
|
|
473
|
-
this.getDynamicCategories().then(categories => {
|
|
474
|
-
this.onDynamicCategoryChange(categories);
|
|
475
|
-
});
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
let selectedEmoji;
|
|
479
|
-
let activeCategory;
|
|
480
|
-
|
|
481
|
-
if (containsEmojiId(emojiToRender, this.state.selectedEmoji)) {
|
|
482
|
-
// Keep existing emoji selected if still in results
|
|
483
|
-
selectedEmoji = this.state.selectedEmoji;
|
|
484
|
-
activeCategory = this.state.activeCategory;
|
|
485
|
-
} else {
|
|
486
|
-
selectedEmoji = undefined; // Only enable categories for full emoji list (non-search)
|
|
487
|
-
|
|
488
|
-
activeCategory = undefined;
|
|
401
|
+
if (supportsUploadFeature(emojiProvider)) {
|
|
402
|
+
emojiProvider.isUploadSupported().then(onUploadSupported);
|
|
489
403
|
}
|
|
490
404
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
405
|
+
return () => {
|
|
406
|
+
emojiProvider.unsubscribe(onProviderChange);
|
|
407
|
+
};
|
|
408
|
+
}, [emojiProvider, onProviderChange, onUploadSupported]);
|
|
409
|
+
useEffect(() => {
|
|
410
|
+
// We changed provider which means we subscribed to filter results for a new subscriber.
|
|
411
|
+
// So we refresh the emoji display with onSearch and we do it here, after the new props have
|
|
412
|
+
// been set since onSearch leads to filter being called on the current emojiProvider.
|
|
413
|
+
// (Calling onSearch in a '...Will...' lifecycle method would lead to filter being called on
|
|
414
|
+
// an emojiProvider we have already unsubscribed from)
|
|
415
|
+
onSearch(query);
|
|
416
|
+
}, [emojiProvider, onSearch, query]);
|
|
417
|
+
useEffect(() => {
|
|
418
|
+
// Fire analytics event whenever query changes
|
|
419
|
+
fireAnalytics(pickerSearchedEvent({
|
|
420
|
+
queryLength: query.length,
|
|
421
|
+
numMatches: filteredEmojis.length
|
|
422
|
+
}));
|
|
423
|
+
ufoExperiences['emoji-searched'].success({
|
|
424
|
+
metadata: {
|
|
425
|
+
emojisLength: filteredEmojis.length
|
|
426
|
+
}
|
|
500
427
|
});
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
428
|
+
}, [filteredEmojis.length, fireAnalytics, query]);
|
|
429
|
+
useEffect(() => {
|
|
430
|
+
if (!frequentlyUsedEmojis.length || query) {
|
|
431
|
+
setFilteredEmojis(searchEmojis);
|
|
432
|
+
} else {
|
|
433
|
+
setFilteredEmojis([...searchEmojis, ...frequentlyUsedEmojis]);
|
|
506
434
|
}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
uploading,
|
|
527
|
-
uploadErrorMessage,
|
|
528
|
-
uploadSupported
|
|
529
|
-
} = this.state;
|
|
530
|
-
const recordUsageOnSelection = createRecordSelectionDefault(emojiProvider, this.onSelectWrapper, analytic => this.fireAnalytics(analytic('picker')));
|
|
531
|
-
const formattedErrorMessage = uploadErrorMessage ? jsx(FormattedMessage, uploadErrorMessage) : null;
|
|
532
|
-
const emojiContextValue = {
|
|
533
|
-
emoji: {
|
|
534
|
-
emojiProvider: this.props.emojiProvider
|
|
535
|
-
}
|
|
435
|
+
}, [frequentlyUsedEmojis, query, searchEmojis]);
|
|
436
|
+
useEffect(() => {
|
|
437
|
+
// Fire analytics on component unmount
|
|
438
|
+
return () => {
|
|
439
|
+
fireAnalytics(closedPickerEvent({
|
|
440
|
+
duration: calculateElapsedTime()
|
|
441
|
+
}));
|
|
442
|
+
ufoExperiences['emoji-picker-opened'].abort({
|
|
443
|
+
metadata: {
|
|
444
|
+
source: 'EmojiPickerComponent',
|
|
445
|
+
reason: 'unmount'
|
|
446
|
+
}
|
|
447
|
+
});
|
|
448
|
+
ufoExperiences['emoji-searched'].abort({
|
|
449
|
+
metadata: {
|
|
450
|
+
source: 'EmojiPickerComponent',
|
|
451
|
+
reason: 'unmount'
|
|
452
|
+
}
|
|
453
|
+
});
|
|
536
454
|
};
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
}
|
|
455
|
+
}, [fireAnalytics]);
|
|
456
|
+
useEffect(() => {
|
|
457
|
+
// Unsubscribe emojiProvider on component unmount
|
|
458
|
+
return () => {
|
|
459
|
+
emojiProvider.unsubscribe(onProviderChange);
|
|
460
|
+
};
|
|
461
|
+
}, [emojiProvider, onProviderChange]);
|
|
462
|
+
const showPreview = selectedEmoji && !uploading;
|
|
463
|
+
return jsx(LegacyEmojiContextProvider, {
|
|
464
|
+
emojiContextValue: emojiContextValue
|
|
465
|
+
}, jsx("div", {
|
|
466
|
+
css: emojiPicker(showPreview),
|
|
467
|
+
ref: onPickerRef,
|
|
468
|
+
"data-emoji-picker-container": true
|
|
469
|
+
}, jsx(CategorySelector, {
|
|
470
|
+
activeCategoryId: activeCategory,
|
|
471
|
+
dynamicCategories: dynamicCategories,
|
|
472
|
+
disableCategories: disableCategories,
|
|
473
|
+
onCategorySelected: onCategorySelected
|
|
474
|
+
}), jsx(EmojiPickerList, {
|
|
475
|
+
emojis: filteredEmojis,
|
|
476
|
+
currentUser: currentUser,
|
|
477
|
+
onEmojiSelected: recordUsageOnSelection,
|
|
478
|
+
onEmojiActive: onEmojiActive,
|
|
479
|
+
onEmojiDelete: onTriggerDelete,
|
|
480
|
+
onCategoryActivated: onCategoryActivated,
|
|
481
|
+
onSearch: onSearch,
|
|
482
|
+
query: query,
|
|
483
|
+
selectedTone: selectedTone,
|
|
484
|
+
loading: loading,
|
|
485
|
+
ref: emojiPickerList,
|
|
486
|
+
initialUploadName: query,
|
|
487
|
+
onToneSelected: onToneSelected,
|
|
488
|
+
onToneSelectorCancelled: onToneSelectorCancelled,
|
|
489
|
+
toneEmoji: toneEmoji,
|
|
490
|
+
uploading: uploading,
|
|
491
|
+
emojiToDelete: emojiToDelete,
|
|
492
|
+
uploadErrorMessage: formattedErrorMessage,
|
|
493
|
+
uploadEnabled: uploadSupported && !uploading,
|
|
494
|
+
onUploadEmoji: onUploadEmoji,
|
|
495
|
+
onUploadCancelled: onUploadCancelled,
|
|
496
|
+
onDeleteEmoji: onDeleteEmoji,
|
|
497
|
+
onCloseDelete: onCloseDelete,
|
|
498
|
+
onFileChooserClicked: onFileChooserClicked,
|
|
499
|
+
onOpenUpload: onOpenUpload
|
|
500
|
+
}), showPreview && jsx(EmojiPickerFooter, {
|
|
501
|
+
selectedEmoji: selectedEmoji
|
|
502
|
+
})));
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
export default /*#__PURE__*/memo(EmojiPickerComponent);
|