Commit 2ed966e7 authored by Chris's avatar Chris Committed by Harrison Healey

Refresh the emoji library (#7001)

* refresh the emoji library

* fix img_trans.gif

* compress sprite sheet

* remove note on compression - webpack seems to handle compression decently

* better emoji sheet preloading

* requested changes
parent 3d1cb1a4

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.

.PHONY: build test run clean stop check-style run-unit
.PHONY: build test run clean stop check-style run-unit emojis
BUILD_SERVER_DIR = ..
......@@ -54,3 +54,6 @@ clean:
rm -rf dist
rm -rf node_modules
rm -f .yarninstall
emojis:
./make-emojis
......@@ -17,6 +17,7 @@ import MsgTyping from './msg_typing.jsx';
import FileUpload from './file_upload.jsx';
import FilePreview from './file_preview.jsx';
import EmojiPickerOverlay from 'components/emoji_picker/emoji_picker_overlay.jsx';
import * as EmojiPicker from 'components/emoji_picker/emoji_picker.jsx';
import * as Utils from 'utils/utils.jsx';
import * as UserAgent from 'utils/user_agent.jsx';
import * as GlobalActions from 'actions/global_actions.jsx';
......@@ -588,6 +589,7 @@ export default class CreateComment extends React.Component {
<span
className={'fa fa-smile-o icon--emoji-picker emoji-rhs'}
onClick={this.toggleEmojiPicker}
onMouseOver={EmojiPicker.beginPreloading}
/>
</span>
);
......
......@@ -9,6 +9,7 @@ import FilePreview from './file_preview.jsx';
import PostDeletedModal from './post_deleted_modal.jsx';
import TutorialTip from './tutorial/tutorial_tip.jsx';
import EmojiPickerOverlay from 'components/emoji_picker/emoji_picker_overlay.jsx';
import * as EmojiPicker from 'components/emoji_picker/emoji_picker.jsx';
import AppDispatcher from 'dispatcher/app_dispatcher.jsx';
import * as GlobalActions from 'actions/global_actions.jsx';
......@@ -746,6 +747,7 @@ export default class CreatePost extends React.Component {
<span
className={'fa fa-smile-o icon--emoji-picker emoji-main'}
onClick={this.toggleEmojiPicker}
onMouseOver={EmojiPicker.beginPreloading}
/>
</span>
);
......
......@@ -14,7 +14,8 @@ export default class EmojiPickerItem extends React.Component {
onItemOut: PropTypes.func.isRequired,
onItemClick: PropTypes.func.isRequired,
onItemUnmount: PropTypes.func.isRequired,
category: PropTypes.string.isRequired
category: PropTypes.string.isRequired,
isLoaded: PropTypes.bool.isRequired
}
constructor(props) {
......@@ -61,8 +62,8 @@ export default class EmojiPickerItem extends React.Component {
item =
(<div >
<img
src='/static/emoji/img_trans.gif'
className={' emojisprite emoji-' + this.props.emoji.filename + ' '}
src='/static/images/img_trans.gif'
className={' emojisprite' + (this.props.isLoaded ? '' : '-loading') + ' emoji-' + this.props.emoji.filename + ' '}
onMouseOver={this.handleMouseOver}
onMouseOut={this.handleMouseOut}
onClick={this.handleClick}
......
......@@ -27,7 +27,7 @@ export default class EmojiPickerPreview extends React.Component {
name = emoji.aliases[0];
aliases = emoji.aliases;
previewImage = (<span className='sprite-preview'><img
src='/static/emoji/img_trans.gif'
src='/static/images/img_trans.gif'
className={' emojisprite-preview emoji-' + emoji.filename + ' '}
/></span>);
} else {
......
......@@ -14,14 +14,23 @@ import EmojiPickerCategory from './components/emoji_picker_category.jsx';
import EmojiPickerItem from './components/emoji_picker_item.jsx';
import EmojiPickerPreview from './components/emoji_picker_preview.jsx';
import PeopleSpriteSheet from 'images/emoji-sheets/people.png';
import NatureSpriteSheet from 'images/emoji-sheets/nature.png';
import FoodsSpriteSheet from 'images/emoji-sheets/foods.png';
import ActivitySpriteSheet from 'images/emoji-sheets/activity.png';
import PlacesSpriteSheet from 'images/emoji-sheets/places.png';
import ObjectsSpriteSheet from 'images/emoji-sheets/objects.png';
import SymbolsSpriteSheet from 'images/emoji-sheets/symbols.png';
import FlagsSpriteSheet from 'images/emoji-sheets/flags.png';
// This should include all the categories available in Emoji.CategoryNames
const CATEGORIES = [
'recent',
'people',
'nature',
'food',
'foods',
'activity',
'travel',
'places',
'objects',
'symbols',
'flags',
......@@ -49,6 +58,7 @@ export default class EmojiPicker extends React.Component {
// All props are primitives or treated as immutable
this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
this.handlePreload = this.handlePreload.bind(this);
this.handleCategoryClick = this.handleCategoryClick.bind(this);
this.handleFilterChange = this.handleFilterChange.bind(this);
this.handleItemOver = this.handleItemOver.bind(this);
......@@ -61,7 +71,8 @@ export default class EmojiPicker extends React.Component {
this.state = {
category: 'recent',
filter: '',
selected: null
selected: null,
preloaded: []
};
}
......@@ -71,6 +82,23 @@ export default class EmojiPicker extends React.Component {
requestAnimationFrame(() => {
this.searchInput.focus();
});
beginPreloading();
subscribeToPreloads(this.handlePreload);
this.handlePreload();
}
componentWillUnmount() {
unsubscribeFromPreloads(this.handlePreload);
}
handlePreload() {
const preloaded = [];
for (const category of CATEGORIES) {
if (didPreloadCategory(category)) {
preloaded.push(category);
}
}
this.setState({preloaded});
}
handleCategoryClick(category) {
......@@ -139,7 +167,8 @@ export default class EmojiPicker extends React.Component {
}
}
}
renderCategory(category, filter) {
renderCategory(category, isLoaded, filter) {
const items = [];
let indices = [];
let recentEmojis = [];
......@@ -181,6 +210,7 @@ export default class EmojiPicker extends React.Component {
key={'system_' + (category === 'recent' ? 'recent_' : '') + (emoji.name || emoji.aliases[0])}
emoji={emoji}
category={category}
isLoaded={isLoaded}
onItemOver={this.handleItemOver}
onItemOut={this.handleItemOut}
onItemClick={this.handleItemClick}
......@@ -261,7 +291,7 @@ export default class EmojiPicker extends React.Component {
previewImage = (
<span>
<img
src='/static/emoji/img_trans.gif'
src='/static/images/img_trans.gif'
className={' emojisprite-preview emoji-' + selected.filename + ' '}
align='absmiddle'
/>
......@@ -293,9 +323,9 @@ export default class EmojiPicker extends React.Component {
for (const category of CATEGORIES) {
if (category === 'custom') {
items.push(this.renderCategory('custom', this.state.filter, this.props.customEmojis));
items.push(this.renderCategory('custom', true, this.state.filter, this.props.customEmojis));
} else {
items.push(this.renderCategory(category, this.state.filter));
items.push(this.renderCategory(category, category === 'recent' || this.state.preloaded.indexOf(category) >= 0, this.state.filter));
}
}
......@@ -357,15 +387,15 @@ export default class EmojiPicker extends React.Component {
selected={this.state.category === 'nature'}
/>
<EmojiPickerCategory
category='food'
category='foods'
icon={
<i
className='fa fa-cutlery'
title={Utils.localizeMessage('emoji_picker.food', 'Food')}
title={Utils.localizeMessage('emoji_picker.foods', 'Foods')}
/>
}
onCategoryClick={this.handleCategoryClick}
selected={this.state.category === 'food'}
selected={this.state.category === 'foods'}
/>
<EmojiPickerCategory
category='activity'
......@@ -379,15 +409,15 @@ export default class EmojiPicker extends React.Component {
selected={this.state.category === 'activity'}
/>
<EmojiPickerCategory
category='travel'
category='places'
icon={
<i
className='fa fa-plane'
title={Utils.localizeMessage('emoji_picker.travel', 'Travel')}
title={Utils.localizeMessage('emoji_picker.places', 'Places')}
/>
}
onCategoryClick={this.handleCategoryClick}
selected={this.state.category === 'travel'}
selected={this.state.category === 'places'}
/>
<EmojiPickerCategory
category='objects'
......@@ -459,3 +489,87 @@ export default class EmojiPicker extends React.Component {
);
}
}
var preloads = {
people: {
src: PeopleSpriteSheet,
didPreload: false
},
nature: {
src: NatureSpriteSheet,
didPreload: false
},
foods: {
src: FoodsSpriteSheet,
didPreload: false
},
activity: {
src: ActivitySpriteSheet,
didPreload: false
},
places: {
src: PlacesSpriteSheet,
didPreload: false
},
objects: {
src: ObjectsSpriteSheet,
didPreload: false
},
symbols: {
src: SymbolsSpriteSheet,
didPreload: false
},
flags: {
src: FlagsSpriteSheet,
didPreload: false
}
};
var didBeginPreloading = false;
var preloadCallback = null;
export function beginPreloading() {
if (didBeginPreloading) {
return;
}
didBeginPreloading = true;
preloadNextCategory();
}
function preloadNextCategory() {
let sheet = null;
for (const category of CATEGORIES) {
const preload = preloads[category];
if (preload && !preload.didPreload) {
sheet = preload;
break;
}