@atlaskit/emoji 65.1.1 → 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 +19 -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/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 -516
- package/dist/cjs/components/picker/EmojiPickerEmojiRow.js +33 -60
- package/dist/cjs/components/picker/EmojiPickerFooter.js +13 -48
- package/dist/cjs/components/uploader/EmojiUploadComponent.js +124 -109
- package/dist/cjs/hooks/useEmojiContext.js +16 -0
- package/dist/cjs/hooks/{index.js → usePrevious.js} +0 -0
- package/dist/cjs/index.js +16 -0
- 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/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 -506
- package/dist/es2019/components/picker/EmojiPickerEmojiRow.js +32 -35
- package/dist/es2019/components/picker/EmojiPickerFooter.js +11 -21
- package/dist/es2019/components/uploader/EmojiUploadComponent.js +81 -91
- package/dist/es2019/hooks/useEmojiContext.js +3 -0
- package/dist/es2019/hooks/{index.js → usePrevious.js} +0 -0
- package/dist/es2019/index.js +4 -1
- 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/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 -535
- package/dist/esm/components/picker/EmojiPickerEmojiRow.js +31 -59
- package/dist/esm/components/picker/EmojiPickerFooter.js +14 -49
- package/dist/esm/components/uploader/EmojiUploadComponent.js +119 -113
- package/dist/esm/hooks/useEmojiContext.js +5 -0
- package/dist/esm/hooks/{index.js → usePrevious.js} +0 -0
- package/dist/esm/index.js +4 -1
- package/dist/esm/version.json +1 -1
- package/dist/types/components/common/CachingEmoji.d.ts +3 -13
- package/dist/types/components/common/Emoji.d.ts +1 -1
- 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/Popup.d.ts +3 -20
- 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 +3 -3
- package/dist/types/components/picker/EmojiPickerCategoryHeading.d.ts +3 -4
- package/dist/types/components/picker/EmojiPickerComponent.d.ts +4 -80
- package/dist/types/components/picker/EmojiPickerEmojiRow.d.ts +3 -4
- package/dist/types/components/picker/EmojiPickerFooter.d.ts +3 -6
- 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/{index.d.ts → usePrevious.d.ts} +0 -0
- package/dist/types/index.d.ts +3 -1
- package/dist/types/types.d.ts +2 -1
- package/local-config-example.ts +3 -1
- package/package.json +17 -4
- 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,575 +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
|
-
}
|
|
72
|
-
|
|
73
|
-
this.setState({
|
|
74
|
-
activeCategory: categoryId,
|
|
75
|
-
selectedEmoji
|
|
76
|
-
});
|
|
77
|
-
this.fireAnalytics(categoryClickedEvent({
|
|
78
|
-
category: categoryId
|
|
79
|
-
}));
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
});
|
|
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
|
+
*/
|
|
83
100
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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);
|
|
87
113
|
});
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
_defineProperty(this, "onFileChooserClicked", () => {
|
|
91
|
-
this.fireAnalytics(selectedFileEvent());
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
_defineProperty(this, "fireAnalytics", analyticsEvent => {
|
|
95
|
-
const {
|
|
96
|
-
createAnalyticsEvent
|
|
97
|
-
} = this.props;
|
|
98
|
-
|
|
99
|
-
if (createAnalyticsEvent) {
|
|
100
|
-
createAndFireEventInElementsChannel(analyticsEvent)(createAnalyticsEvent);
|
|
101
|
-
}
|
|
102
|
-
});
|
|
114
|
+
}
|
|
103
115
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
116
|
+
if (emojiToRender && !containsEmojiId(emojiToRender, selectedEmoji)) {
|
|
117
|
+
batchedUpdates(() => {
|
|
118
|
+
setSelectedEmoji(undefined); // Only enable categories for full emoji list (non-search)
|
|
107
119
|
|
|
108
|
-
|
|
109
|
-
this.setState({
|
|
110
|
-
uploadSupported: supported
|
|
120
|
+
setActiveCategory(null);
|
|
111
121
|
});
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
_defineProperty(this, "onSearch", query => {
|
|
115
|
-
const options = {
|
|
116
|
-
skinTone: this.state.selectedTone
|
|
117
|
-
};
|
|
122
|
+
}
|
|
118
123
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
queryLength: query.length,
|
|
123
|
-
source: 'picker'
|
|
124
|
-
});
|
|
124
|
+
batchedUpdates(() => {
|
|
125
|
+
if (emojiToRender) {
|
|
126
|
+
setFilteredEmojis(emojiToRender);
|
|
125
127
|
}
|
|
126
128
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
_defineProperty(this, "onSearchResult", searchResults => {
|
|
131
|
-
const frequentlyUsedEmoji = this.state.frequentlyUsedEmojis || [];
|
|
132
|
-
const searchQuery = searchResults.query || '';
|
|
133
|
-
const emojiToRender = this.buildQuerySpecificEmojiList(searchQuery, searchResults.emojis, frequentlyUsedEmoji);
|
|
134
|
-
|
|
135
|
-
if (searchQuery !== this.state.query) {
|
|
136
|
-
this.fireAnalytics(pickerSearchedEvent({
|
|
137
|
-
queryLength: searchQuery.length,
|
|
138
|
-
numMatches: emojiToRender.length
|
|
139
|
-
}));
|
|
140
|
-
ufoExperiences['emoji-searched'].success({
|
|
141
|
-
metadata: {
|
|
142
|
-
emojisLength: emojiToRender.length
|
|
143
|
-
}
|
|
144
|
-
});
|
|
129
|
+
if (searchEmoji) {
|
|
130
|
+
setSearchEmojis(searchEmoji);
|
|
145
131
|
}
|
|
146
132
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
_defineProperty(this, "onFrequentEmojiResult", frequentEmoji => {
|
|
151
|
-
const {
|
|
152
|
-
query,
|
|
153
|
-
searchEmojis
|
|
154
|
-
} = this.state; // change the category of each of the featured emoji
|
|
155
|
-
|
|
156
|
-
const recategorised = frequentEmoji.map(emoji => {
|
|
157
|
-
const clone = JSON.parse(JSON.stringify(emoji));
|
|
158
|
-
clone.category = frequentCategory;
|
|
159
|
-
return clone;
|
|
160
|
-
});
|
|
161
|
-
const emojiToRender = this.buildQuerySpecificEmojiList(query, searchEmojis, recategorised);
|
|
162
|
-
this.setStateAfterEmojiChange(query, emojiToRender, searchEmojis, recategorised);
|
|
163
|
-
});
|
|
133
|
+
if (frequentEmoji) {
|
|
134
|
+
setFrequentlyUsedEmojis(frequentEmoji);
|
|
135
|
+
}
|
|
164
136
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
dynamicCategories: categories
|
|
168
|
-
});
|
|
137
|
+
setLoading(false);
|
|
138
|
+
setDisableCategories(disableCategories);
|
|
169
139
|
});
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
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;
|
|
173
147
|
});
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
this.setState({
|
|
177
|
-
selectedTone: toneValue
|
|
178
|
-
});
|
|
179
|
-
this.props.emojiProvider.setSelectedTone(toneValue);
|
|
180
|
-
const {
|
|
181
|
-
query = ''
|
|
182
|
-
} = this.state;
|
|
183
|
-
this.updateEmojis(query, {
|
|
184
|
-
skinTone: toneValue
|
|
185
|
-
});
|
|
148
|
+
setStateAfterEmojiChange({
|
|
149
|
+
frequentEmoji: recategorised
|
|
186
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
|
+
}
|
|
187
168
|
|
|
188
|
-
|
|
189
|
-
|
|
169
|
+
setStateAfterEmojiChange({
|
|
170
|
+
searchQuery,
|
|
171
|
+
emojiToRender,
|
|
172
|
+
searchEmoji: searchResults.emojis
|
|
190
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
|
+
*/
|
|
191
185
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
if (!options.sort) {
|
|
201
|
-
options.sort = SearchSort.None;
|
|
202
|
-
} // take a copy of search options so that the frequently used can be limited to 16 without affecting the full emoji query
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
const frequentOptions = { ...options,
|
|
206
|
-
sort: SearchSort.None,
|
|
207
|
-
limit: FREQUENTLY_USED_MAX
|
|
208
|
-
};
|
|
209
|
-
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 = {};
|
|
210
192
|
}
|
|
211
193
|
|
|
212
|
-
|
|
213
|
-
|
|
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
|
|
214
197
|
|
|
215
|
-
_defineProperty(this, "onOpenUpload", () => {
|
|
216
|
-
// Prime upload token so it's ready when the user adds
|
|
217
|
-
const {
|
|
218
|
-
emojiProvider
|
|
219
|
-
} = this.props;
|
|
220
198
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
199
|
+
const frequentOptions = { ...options,
|
|
200
|
+
sort: SearchSort.None,
|
|
201
|
+
limit: FREQUENTLY_USED_MAX
|
|
202
|
+
};
|
|
203
|
+
emojiProvider.getFrequentlyUsed(frequentOptions).then(onFrequentEmojiResult);
|
|
204
|
+
}
|
|
224
205
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
206
|
+
emojiProvider.filter(query, options);
|
|
207
|
+
}, [emojiProvider, onFrequentEmojiResult]);
|
|
208
|
+
const onToneSelected = useCallback(toneValue => {
|
|
209
|
+
emojiProvider.setSelectedTone(toneValue);
|
|
210
|
+
updateEmojis(query, {
|
|
211
|
+
skinTone: toneValue
|
|
230
212
|
});
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
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
|
|
238
227
|
}));
|
|
228
|
+
}
|
|
229
|
+
}, [fireAnalytics, onSelection, query]);
|
|
230
|
+
const onCategorySelected = useCallback(categoryId => {
|
|
231
|
+
if (!categoryId) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
239
234
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
});
|
|
244
|
-
};
|
|
235
|
+
emojiProvider.findInCategory(categoryId).then(emojisInCategory => {
|
|
236
|
+
if (!disableCategories) {
|
|
237
|
+
let newSelectedEmoji;
|
|
245
238
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
}); // this.loadEmoji(emojiProvider, emojiDescription);
|
|
239
|
+
if (emojisInCategory && emojisInCategory.length > 0) {
|
|
240
|
+
newSelectedEmoji = getEmojiVariation(emojisInCategory[0], {
|
|
241
|
+
skinTone: selectedTone
|
|
242
|
+
});
|
|
243
|
+
}
|
|
252
244
|
|
|
253
|
-
|
|
254
|
-
|
|
245
|
+
if (emojiPickerList.current) {
|
|
246
|
+
emojiPickerList.current.reveal(categoryId);
|
|
247
|
+
}
|
|
255
248
|
|
|
256
|
-
|
|
249
|
+
batchedUpdates(() => {
|
|
250
|
+
setActiveCategory(categoryId);
|
|
251
|
+
setSelectedEmoji(newSelectedEmoji);
|
|
252
|
+
});
|
|
253
|
+
fireAnalytics(categoryClickedEvent({
|
|
254
|
+
category: categoryId
|
|
255
|
+
}));
|
|
256
|
+
}
|
|
257
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
|
+
};
|
|
258
273
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
emojiToDelete: emoji
|
|
274
|
+
if (query) {
|
|
275
|
+
ufoExperiences['emoji-searched'].start();
|
|
276
|
+
ufoExperiences['emoji-searched'].addMetadata({
|
|
277
|
+
queryLength: query.length,
|
|
278
|
+
source: 'picker'
|
|
265
279
|
});
|
|
266
|
-
}
|
|
280
|
+
}
|
|
267
281
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
} = this.state;
|
|
272
|
-
this.fireAnalytics(deleteCancelEvent({
|
|
273
|
-
emojiId: emojiToDelete && emojiToDelete.id
|
|
274
|
-
}));
|
|
275
|
-
this.setState({
|
|
276
|
-
emojiToDelete: undefined
|
|
277
|
-
});
|
|
278
|
-
});
|
|
282
|
+
if (searchQuery !== query) {
|
|
283
|
+
setQuery(searchQuery);
|
|
284
|
+
}
|
|
279
285
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
emojiId: emojiToDelete && emojiToDelete.id
|
|
288
|
-
}));
|
|
289
|
-
return this.props.emojiProvider.deleteSiteEmoji(emoji).then(success => {
|
|
290
|
-
if (success) {
|
|
291
|
-
this.updateEmojis(query, {
|
|
292
|
-
skinTone: selectedTone
|
|
293
|
-
});
|
|
294
|
-
}
|
|
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
|
+
}
|
|
295
293
|
|
|
296
|
-
|
|
297
|
-
|
|
294
|
+
batchedUpdates(() => {
|
|
295
|
+
setUploadErrorMessage(undefined);
|
|
296
|
+
setUploading(true);
|
|
298
297
|
});
|
|
298
|
+
fireAnalytics(uploadBeginButton());
|
|
299
|
+
}, [emojiProvider, fireAnalytics]);
|
|
300
|
+
const scrollToEndOfList = useCallback(() => {
|
|
301
|
+
if (typeof window === 'undefined') {
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
299
304
|
|
|
300
|
-
|
|
301
|
-
|
|
305
|
+
if (emojiPickerList.current) {
|
|
306
|
+
// Wait a tick to ensure repaint and updated height for picker list
|
|
307
|
+
window.setTimeout(() => {
|
|
308
|
+
var _emojiPickerList$curr;
|
|
302
309
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
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
|
+
}));
|
|
306
318
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
emojiPickerList.scrollToBottom();
|
|
311
|
-
}, 0);
|
|
312
|
-
}
|
|
313
|
-
});
|
|
319
|
+
const errorSetter = message => {
|
|
320
|
+
setUploadErrorMessage(message);
|
|
321
|
+
};
|
|
314
322
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
323
|
+
const onSuccess = emojiDescription => {
|
|
324
|
+
batchedUpdates(() => {
|
|
325
|
+
setActiveCategory(customCategory);
|
|
326
|
+
setSelectedEmoji(emojiDescription);
|
|
327
|
+
setUploading(false);
|
|
319
328
|
});
|
|
320
|
-
|
|
321
|
-
}
|
|
329
|
+
scrollToEndOfList();
|
|
330
|
+
};
|
|
322
331
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
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
|
+
});
|
|
326
357
|
}
|
|
327
|
-
});
|
|
328
358
|
|
|
329
|
-
|
|
330
|
-
const {
|
|
331
|
-
onSelection
|
|
332
|
-
} = this.props;
|
|
333
|
-
const {
|
|
334
|
-
query
|
|
335
|
-
} = this.state;
|
|
336
|
-
|
|
337
|
-
if (onSelection) {
|
|
338
|
-
onSelection(emojiId, emoji, event);
|
|
339
|
-
this.fireAnalytics(pickerClickedEvent({
|
|
340
|
-
duration: this.calculateElapsedTime(),
|
|
341
|
-
emojiId: emojiId.id || '',
|
|
342
|
-
category: emoji && emoji.category || '',
|
|
343
|
-
type: emoji && emoji.type || '',
|
|
344
|
-
queryLength: query && query.length || 0
|
|
345
|
-
}));
|
|
346
|
-
}
|
|
359
|
+
return success;
|
|
347
360
|
});
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
} = props;
|
|
353
|
-
this.state = {
|
|
354
|
-
filteredEmojis: [],
|
|
355
|
-
searchEmojis: [],
|
|
356
|
-
frequentlyUsedEmojis: [],
|
|
357
|
-
query: '',
|
|
358
|
-
dynamicCategories: [],
|
|
359
|
-
selectedTone: !hideToneSelector ? _emojiProvider.getSelectedTone() : undefined,
|
|
360
|
-
loading: true,
|
|
361
|
-
uploadSupported: false,
|
|
362
|
-
uploading: false,
|
|
363
|
-
isPreviewDisplayed: false
|
|
364
|
-
};
|
|
365
|
-
this.openTime = 0;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
UNSAFE_componentWillMount() {
|
|
369
|
-
ufoExperiences['emoji-picker-opened'].success();
|
|
370
|
-
this.openTime = Date.now();
|
|
371
|
-
this.fireAnalytics(openedPickerEvent());
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
componentDidMount() {
|
|
375
|
-
const {
|
|
376
|
-
emojiProvider,
|
|
377
|
-
hideToneSelector
|
|
378
|
-
} = this.props;
|
|
379
|
-
emojiProvider.subscribe(this.onProviderChange);
|
|
380
|
-
this.onSearch(this.state.query);
|
|
361
|
+
}, [emojiProvider, emojiToDelete, fireAnalytics, query, selectedTone, updateEmojis]);
|
|
362
|
+
const onComponentDidMount = useCallback(() => {
|
|
363
|
+
emojiProvider.subscribe(onProviderChange);
|
|
364
|
+
onSearch(query);
|
|
381
365
|
|
|
382
366
|
if (supportsUploadFeature(emojiProvider)) {
|
|
383
|
-
emojiProvider.isUploadSupported().then(
|
|
367
|
+
emojiProvider.isUploadSupported().then(onUploadSupported);
|
|
384
368
|
}
|
|
385
369
|
|
|
386
370
|
if (!hideToneSelector) {
|
|
387
371
|
const toneEmoji = getToneEmoji(emojiProvider);
|
|
388
372
|
|
|
389
373
|
if (isPromise(toneEmoji)) {
|
|
390
|
-
toneEmoji.then(emoji =>
|
|
391
|
-
toneEmoji: emoji
|
|
392
|
-
}));
|
|
374
|
+
toneEmoji.then(emoji => setToneEmoji(emoji));
|
|
393
375
|
} else if (toneEmoji === undefined || isEmojiDescription(toneEmoji)) {
|
|
394
|
-
|
|
395
|
-
toneEmoji
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
componentWillUnmount() {
|
|
402
|
-
const {
|
|
403
|
-
emojiProvider
|
|
404
|
-
} = this.props;
|
|
405
|
-
emojiProvider.unsubscribe(this.onProviderChange);
|
|
406
|
-
this.fireAnalytics(closedPickerEvent({
|
|
407
|
-
duration: this.calculateElapsedTime()
|
|
408
|
-
}));
|
|
409
|
-
ufoExperiences['emoji-picker-opened'].abort({
|
|
410
|
-
metadata: {
|
|
411
|
-
source: 'EmojiPickerComponent',
|
|
412
|
-
reason: 'unmount'
|
|
413
|
-
}
|
|
414
|
-
});
|
|
415
|
-
ufoExperiences['emoji-searched'].abort({
|
|
416
|
-
metadata: {
|
|
417
|
-
source: 'EmojiPickerComponent',
|
|
418
|
-
reason: 'unmount'
|
|
419
|
-
}
|
|
420
|
-
});
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
424
|
-
const prevEmojiProvider = this.props.emojiProvider;
|
|
425
|
-
const nextEmojiProvider = nextProps.emojiProvider;
|
|
426
|
-
|
|
427
|
-
if (prevEmojiProvider !== nextEmojiProvider) {
|
|
428
|
-
if (supportsUploadFeature(nextEmojiProvider)) {
|
|
429
|
-
nextEmojiProvider.isUploadSupported().then(this.onUploadSupported);
|
|
376
|
+
setToneEmoji(toneEmoji);
|
|
430
377
|
}
|
|
431
378
|
}
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
componentDidUpdate(prevProps) {
|
|
435
|
-
const prevEmojiProvider = prevProps.emojiProvider;
|
|
436
|
-
const currentEmojiProvider = this.props.emojiProvider;
|
|
437
|
-
|
|
438
|
-
if (prevEmojiProvider !== currentEmojiProvider) {
|
|
439
|
-
prevEmojiProvider.unsubscribe(this.onProviderChange);
|
|
440
|
-
currentEmojiProvider.subscribe(this.onProviderChange); // We changed provider which means we subscribed to filter results for a new subscriber.
|
|
441
|
-
// So we refresh the emoji display with onSearch and we do it here, after the new props have
|
|
442
|
-
// been set since onSearch leads to filter being called on the current emojiProvider.
|
|
443
|
-
// (Calling onSearch in a '...Will...' lifecycle method would lead to filter being called on
|
|
444
|
-
// an emojiProvider we have already unsubscribed from)
|
|
445
|
-
|
|
446
|
-
this.onSearch(this.state.query);
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
/**
|
|
451
|
-
* If there is no user search in the EmojiPicker then it should display all emoji received from the EmojiRepository and should
|
|
452
|
-
* also include a special category of most frequently used emoji (if there are any). This method decides if we are in this 'no search'
|
|
453
|
-
* state and appends the frequent emoji if necessary.
|
|
454
|
-
*
|
|
455
|
-
* @param searchEmoji the emoji last received from the EmojiRepository after a search (may be empty)
|
|
456
|
-
* @param frequentEmoji the frequently used emoji last received from the EmojiRepository (may be empty)
|
|
457
|
-
*/
|
|
458
|
-
buildQuerySpecificEmojiList(query, searchEmoji, frequentEmoji) {
|
|
459
|
-
// If there are no frequent emoji, or if there was a search query then we want to take the search result exactly as is.
|
|
460
|
-
if (!frequentEmoji.length || query) {
|
|
461
|
-
return searchEmoji;
|
|
462
|
-
}
|
|
379
|
+
}, [emojiProvider, hideToneSelector, onProviderChange, onSearch, onUploadSupported, query]);
|
|
463
380
|
|
|
464
|
-
|
|
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;
|
|
465
387
|
}
|
|
466
|
-
/**
|
|
467
|
-
* Calculate and set the new state of the component in response to the list of emoji changing for some reason (a search has returned
|
|
468
|
-
* or the frequently used emoji have updated.)
|
|
469
|
-
*/
|
|
470
|
-
|
|
471
388
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
const disableCategories = !!query;
|
|
478
|
-
|
|
479
|
-
if (!disableCategories && emojiToRender.length !== filteredEmojis.length) {
|
|
480
|
-
this.getDynamicCategories().then(categories => {
|
|
481
|
-
this.onDynamicCategoryChange(categories);
|
|
482
|
-
});
|
|
389
|
+
useEffect(() => {
|
|
390
|
+
// componentDidMount logic
|
|
391
|
+
if (didMount && updateAfterDidMount.current) {
|
|
392
|
+
onComponentDidMount();
|
|
393
|
+
updateAfterDidMount.current = false;
|
|
483
394
|
}
|
|
395
|
+
}, [didMount, onComponentDidMount]);
|
|
396
|
+
useEffect(() => {
|
|
397
|
+
previousEmojiProvider.current.unsubscribe(onProviderChange);
|
|
398
|
+
previousEmojiProvider.current = emojiProvider;
|
|
399
|
+
emojiProvider.subscribe(onProviderChange);
|
|
484
400
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
if (containsEmojiId(emojiToRender, this.state.selectedEmoji)) {
|
|
489
|
-
// Keep existing emoji selected if still in results
|
|
490
|
-
selectedEmoji = this.state.selectedEmoji;
|
|
491
|
-
activeCategory = this.state.activeCategory;
|
|
492
|
-
} else {
|
|
493
|
-
selectedEmoji = undefined; // Only enable categories for full emoji list (non-search)
|
|
494
|
-
|
|
495
|
-
activeCategory = undefined;
|
|
401
|
+
if (supportsUploadFeature(emojiProvider)) {
|
|
402
|
+
emojiProvider.isUploadSupported().then(onUploadSupported);
|
|
496
403
|
}
|
|
497
404
|
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
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
|
+
}
|
|
507
427
|
});
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
428
|
+
}, [filteredEmojis.length, fireAnalytics, query]);
|
|
429
|
+
useEffect(() => {
|
|
430
|
+
if (!frequentlyUsedEmojis.length || query) {
|
|
431
|
+
setFilteredEmojis(searchEmojis);
|
|
432
|
+
} else {
|
|
433
|
+
setFilteredEmojis([...searchEmojis, ...frequentlyUsedEmojis]);
|
|
513
434
|
}
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
uploading,
|
|
534
|
-
uploadErrorMessage,
|
|
535
|
-
uploadSupported,
|
|
536
|
-
isPreviewDisplayed
|
|
537
|
-
} = this.state;
|
|
538
|
-
const recordUsageOnSelection = createRecordSelectionDefault(emojiProvider, this.onSelectWrapper, analytic => this.fireAnalytics(analytic('picker')));
|
|
539
|
-
const formattedErrorMessage = uploadErrorMessage ? jsx(FormattedMessage, uploadErrorMessage) : null;
|
|
540
|
-
const emojiContextValue = {
|
|
541
|
-
emoji: {
|
|
542
|
-
emojiProvider: this.props.emojiProvider
|
|
543
|
-
}
|
|
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
|
+
});
|
|
544
454
|
};
|
|
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
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
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);
|