Files
zulip/static/js/emoji.js
Gittenburg 45e19dd6b9 emoji: Rename :slight_smile: to 😄.
Zulip converts :) to the 1F642 Unicode emoji and promotes the same emoji
in the popular section of the emoji picker.

Previously Zulip has labeled 1F642 as "slight smile". While that name
conforms to the Unicode standard (which describes the code point as
SLIGHTLY SMILING FACE), it didn't match our use case of the emoji.

If a user types :) or selects the first smile in the emoji picker they
probably mean to express a regular "smile" and not a "slight smile",
which raises the question why they are only smiling slightly.

This commit relabels 1F642 as 😄 and our previous 😄 263A as
:smiling_face:. Note that 263A looks different in our three supported
emoji sets, so it is not suited to be our "default smile".

This change does not require a migration since our emoji system stores
both unicode points and names and handles name changes transparently.
2020-07-21 16:49:54 -07:00

183 lines
6.1 KiB
JavaScript

const util = require("./util");
const emoji_codes = require("../generated/emoji/emoji_codes.json");
// `emojis_by_name` is the central data source that is supposed to be
// used by every widget in the webapp for gathering data for displaying
// emojis. Emoji picker uses this data to derive data for its own use.
exports.emojis_by_name = new Map();
exports.all_realm_emojis = new Map();
exports.active_realm_emojis = new Map();
exports.default_emoji_aliases = new Map();
const emoticon_translations = (() => {
/*
Build a data structure that looks like something
like this:
[
{ regex: /(\:\))/g, replacement_text: ':smile:' },
{ regex: /(\(\:)/g, replacement_text: ':smile:' },
{ regex: /(\:\/)/g, replacement_text: ':confused:' },
{ regex: /(<3)/g, replacement_text: ':heart:' },
{ regex: /(\:\()/g, replacement_text: ':frown:' },
{ regex: /(\:\|)/g, replacement_text: ':expressionless:' }
]
We build up this list of ~6 emoticon translations
even if page_params.translate_emoticons is false, since
that setting can be flipped via live update events.
On the other hand, we assume that emoticon_conversions
won't change until the next reload, which is fine for
now (and we want to avoid creating new regexes on
every new message).
*/
const translations = [];
for (const [emoticon, replacement_text] of Object.entries(emoji_codes.emoticon_conversions)) {
const regex = new RegExp("(" + util.escape_regexp(emoticon) + ")", "g");
translations.push({
regex,
replacement_text,
});
}
return translations;
})();
const zulip_emoji = {
id: "zulip",
emoji_name: "zulip",
emoji_url: "/static/generated/emoji/images/emoji/unicode/zulip.png",
is_realm_emoji: true,
deactivated: false,
};
exports.get_emoji_name = (codepoint) => {
// get_emoji_name('1f384') === 'holiday_tree'
if (Object.prototype.hasOwnProperty.call(emoji_codes.codepoint_to_name, codepoint)) {
return emoji_codes.codepoint_to_name[codepoint];
}
};
exports.get_emoji_codepoint = (emoji_name) => {
// get_emoji_codepoint('avocado') === '1f951'
if (Object.prototype.hasOwnProperty.call(emoji_codes.name_to_codepoint, emoji_name)) {
return emoji_codes.name_to_codepoint[emoji_name];
}
};
exports.get_realm_emoji_url = (emoji_name) => {
// If the emoji name is a realm emoji, returns the URL for it.
// Returns undefined for unicode emoji.
// get_realm_emoji_url('shrug') === '/user_avatars/2/emoji/images/31.png'
const data = exports.active_realm_emojis.get(emoji_name);
if (!data) {
// Not all emojis have urls, plus the user
// may have hand-typed an invalid emoji.
// The caller can check the result for falsiness
// and then try alternate ways of parsing the
// emoji (in the case of markdown) or just do
// whatever makes sense for the caller.
return;
}
return data.emoji_url;
};
exports.update_emojis = function (realm_emojis) {
// exports.all_realm_emojis is emptied before adding the realm-specific emoji
// to it. This makes sure that in case of deletion, the deleted realm_emojis
// don't persist in exports.active_realm_emojis.
exports.all_realm_emojis.clear();
exports.active_realm_emojis.clear();
for (const data of Object.values(realm_emojis)) {
exports.all_realm_emojis.set(data.id, {
id: data.id,
emoji_name: data.name,
emoji_url: data.source_url,
deactivated: data.deactivated,
});
if (data.deactivated !== true) {
exports.active_realm_emojis.set(data.name, {
id: data.id,
emoji_name: data.name,
emoji_url: data.source_url,
});
}
}
// Add the Zulip emoji to the realm emojis list
exports.all_realm_emojis.set("zulip", zulip_emoji);
exports.active_realm_emojis.set("zulip", zulip_emoji);
exports.build_emoji_data(exports.active_realm_emojis);
};
exports.initialize = function initialize() {
for (const value of emoji_codes.names) {
const base_name = exports.get_emoji_codepoint(value);
if (exports.default_emoji_aliases.has(base_name)) {
exports.default_emoji_aliases.get(base_name).push(value);
} else {
exports.default_emoji_aliases.set(base_name, [value]);
}
}
exports.update_emojis(page_params.realm_emoji);
};
exports.build_emoji_data = function (realm_emojis) {
exports.emojis_by_name.clear();
for (const [realm_emoji_name, realm_emoji] of realm_emojis) {
const emoji_dict = {
name: realm_emoji_name,
display_name: realm_emoji_name,
aliases: [realm_emoji_name],
is_realm_emoji: true,
url: realm_emoji.emoji_url,
has_reacted: false,
};
exports.emojis_by_name.set(realm_emoji_name, emoji_dict);
}
for (const codepoints of Object.values(emoji_codes.emoji_catalog)) {
for (const codepoint of codepoints) {
const emoji_name = exports.get_emoji_name(codepoint);
if (emoji_name !== undefined && !exports.emojis_by_name.has(emoji_name)) {
const emoji_dict = {
name: emoji_name,
display_name: emoji_name,
aliases: exports.default_emoji_aliases.get(codepoint),
is_realm_emoji: false,
emoji_code: codepoint,
has_reacted: false,
};
exports.emojis_by_name.set(emoji_name, emoji_dict);
}
}
}
};
exports.get_canonical_name = function (emoji_name) {
if (exports.active_realm_emojis.has(emoji_name)) {
return emoji_name;
}
const codepoint = exports.get_emoji_codepoint(emoji_name);
if (codepoint === undefined) {
blueslip.error("Invalid emoji name: " + emoji_name);
return;
}
return exports.get_emoji_name(codepoint);
};
exports.get_emoticon_translations = () => emoticon_translations;
window.emoji = exports;