Commit d07919de authored by Tim Zallmann's avatar Tim Zallmann Committed by Phil Hughes

Moved all emojis to the public folder

Created new emojis map in public folder

Renamed folder to emojis

Loading now the emojis from Localstorage or from Server

Moved all emojis to the public folder

Loading the emojis.json file now through AJAX

Loads now the map in the web element instead when building the emoji tag

Updated the custom Element Setup to modern method

Fixed Emoji Karma Specs for async loading

Loading now the emojis from Localstorage or from Server

Loads now the map in the web element instead when building the emoji tag

Fixed problem with FIXTURE_PATH for emojis fixtures

Fixes Linting Error in gemojione.rake

Fixed Emoji Karma Specs

Fix static type check in gemojione and check if already registered

Testing if the Emoji Support Check is failing Rspec

Change of CLass Name, returning true on check to test

Fixes failing Emoji RSpec Tests

Moved Emojis into public/-/emojis/1/

Fixed Linting Errors in gl_emoji

Fix to fixtures creation for emojis

Fixed path spec for new subdirectory -/emojis

Optimized emojis.json output

Fix for Emoji Spec failure due to unicode dataset

Better catch handling for emojis
parent b7998f60

Too many changes to show.

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

...@@ -615,10 +615,18 @@ export class AwardsHandler { ...@@ -615,10 +615,18 @@ export class AwardsHandler {
let awardsHandlerPromise = null; let awardsHandlerPromise = null;
export default function loadAwardsHandler(reload = false) { export default function loadAwardsHandler(reload = false) {
if (!awardsHandlerPromise || reload) { if (!awardsHandlerPromise || reload) {
awardsHandlerPromise = import(/* webpackChunkName: 'emoji' */ './emoji').then(Emoji => { awardsHandlerPromise = new Promise((resolve, reject) => {
const awardsHandler = new AwardsHandler(Emoji); import(/* webpackChunkName: 'emoji' */ './emoji')
awardsHandler.bindEvents(); .then(Emoji => {
return awardsHandler; Emoji.initEmojiMap()
.then(() => {
const awardsHandler = new AwardsHandler(Emoji);
awardsHandler.bindEvents();
resolve(awardsHandler);
})
.catch(() => reject);
})
.catch(() => reject);
}); });
} }
return awardsHandlerPromise; return awardsHandlerPromise;
......
import 'document-register-element'; import 'document-register-element';
import isEmojiUnicodeSupported from '../emoji/support'; import isEmojiUnicodeSupported from '../emoji/support';
import { initEmojiMap, getEmojiInfo, emojiFallbackImageSrc, emojiImageTag } from '../emoji';
class GlEmoji extends HTMLElement { class GlEmoji extends HTMLElement {
constructor() { constructor() {
super(); super();
const emojiUnicode = this.textContent.trim(); let emojiUnicode = this.textContent.trim();
const { name, unicodeVersion, fallbackSrc, fallbackSpriteClass } = this.dataset; const { fallbackSpriteClass, fallbackSrc, forceFallback } = this.dataset;
let { name, unicodeVersion } = this.dataset;
const isEmojiUnicode =
this.childNodes && initEmojiMap()
Array.prototype.every.call(this.childNodes, childNode => childNode.nodeType === 3); .then(() => {
const hasImageFallback = fallbackSrc && fallbackSrc.length > 0; if (!unicodeVersion) {
const hasCssSpriteFalback = fallbackSpriteClass && fallbackSpriteClass.length > 0; const emojiInfo = getEmojiInfo(name);
if (emojiUnicode && isEmojiUnicode && !isEmojiUnicodeSupported(emojiUnicode, unicodeVersion)) { if (emojiInfo) {
// CSS sprite fallback takes precedence over image fallback if (name !== emojiInfo.name) {
if (hasCssSpriteFalback) { ({ name } = emojiInfo);
if (!gon.emoji_sprites_css_added && gon.emoji_sprites_css_path) { this.dataset.name = emojiInfo.name;
const emojiSpriteLinkTag = document.createElement('link'); }
emojiSpriteLinkTag.setAttribute('rel', 'stylesheet'); unicodeVersion = emojiInfo.u;
emojiSpriteLinkTag.setAttribute('href', gon.emoji_sprites_css_path); this.dataset.uni = unicodeVersion;
document.head.appendChild(emojiSpriteLinkTag);
gon.emoji_sprites_css_added = true; if (forceFallback === 'true' && !fallbackSpriteClass) {
} this.innerHTML = emojiImageTag(name, emojiFallbackImageSrc(name));
// IE 11 doesn't like adding multiple at once :(
this.classList.add('emoji-icon');
this.classList.add(fallbackSpriteClass);
} else {
import(/* webpackChunkName: 'emoji' */ '../emoji')
.then(({ emojiImageTag, emojiFallbackImageSrc }) => {
if (hasImageFallback) {
this.innerHTML = emojiImageTag(name, fallbackSrc);
} else { } else {
const src = emojiFallbackImageSrc(name); emojiUnicode = emojiInfo.e;
this.innerHTML = emojiImageTag(name, src); this.innerHTML = emojiInfo.e;
} }
})
.catch(() => { this.title = emojiInfo.d;
// do nothing }
}); }
}
} const isEmojiUnicode =
this.childNodes &&
Array.prototype.every.call(this.childNodes, childNode => childNode.nodeType === 3);
const hasImageFallback = fallbackSrc && fallbackSrc.length > 0;
const hasCssSpriteFalback = fallbackSpriteClass && fallbackSpriteClass.length > 0;
if (
emojiUnicode &&
isEmojiUnicode &&
!isEmojiUnicodeSupported(emojiUnicode, unicodeVersion)
) {
// CSS sprite fallback takes precedence over image fallback
if (hasCssSpriteFalback) {
if (!gon.emoji_sprites_css_added && gon.emoji_sprites_css_path) {
const emojiSpriteLinkTag = document.createElement('link');
emojiSpriteLinkTag.setAttribute('rel', 'stylesheet');
emojiSpriteLinkTag.setAttribute('href', gon.emoji_sprites_css_path);
document.head.appendChild(emojiSpriteLinkTag);
gon.emoji_sprites_css_added = true;
}
// IE 11 doesn't like adding multiple at once :(
this.classList.add('emoji-icon');
this.classList.add(fallbackSpriteClass);
} else if (hasImageFallback) {
this.innerHTML = emojiImageTag(name, fallbackSrc);
} else {
const src = emojiFallbackImageSrc(name);
this.innerHTML = emojiImageTag(name, src);
}
}
})
.catch(error => {
// Only reject is already handled in initEmojiMap
throw error;
});
} }
} }
......
import _ from 'underscore'; import _ from 'underscore';
import emojiMap from 'emojis/digests.json'; import createFlash from '~/flash';
import { s__ } from '~/locale';
import emojiAliases from 'emojis/aliases.json'; import emojiAliases from 'emojis/aliases.json';
import axios from '../lib/utils/axios_utils';
export const validEmojiNames = [...Object.keys(emojiMap), ...Object.keys(emojiAliases)]; import AccessorUtilities from '../lib/utils/accessor';
let emojiMap = null;
let validEmojiNames = null;
export const EMOJI_VERSION = '1';
const EMOJI_VERSION_LOCALSTORAGE = `EMOJIS_${EMOJI_VERSION}`;
const isLocalStorageAvailable = AccessorUtilities.isLocalStorageAccessSafe();
export function initEmojiMap() {
return new Promise((resolve, reject) => {
if (emojiMap) {
resolve(emojiMap);
} else if (isLocalStorageAvailable && window.localStorage.getItem(EMOJI_VERSION_LOCALSTORAGE)) {
emojiMap = JSON.parse(window.localStorage.getItem(EMOJI_VERSION_LOCALSTORAGE));
validEmojiNames = [...Object.keys(emojiMap), ...Object.keys(emojiAliases)];
resolve(emojiMap);
} else {
// We load the JSON from server
axios
.get(
`${gon.asset_host || ''}${gon.relative_url_root ||
''}/-/emojis/${EMOJI_VERSION}/emojis.json`,
)
.then(({ data }) => {
emojiMap = data;
validEmojiNames = [...Object.keys(emojiMap), ...Object.keys(emojiAliases)];
resolve(emojiMap);
if (isLocalStorageAvailable) {
window.localStorage.setItem(EMOJI_VERSION_LOCALSTORAGE, JSON.stringify(emojiMap));
}
})
.catch(err => {
createFlash(s__('Emojis|Something went wrong while loading emojis.'));
reject(err);
});
}
});
}
export function normalizeEmojiName(name) { export function normalizeEmojiName(name) {
return Object.prototype.hasOwnProperty.call(emojiAliases, name) ? emojiAliases[name] : name; return Object.prototype.hasOwnProperty.call(emojiAliases, name) ? emojiAliases[name] : name;
} }
export function getValidEmojiNames() {
return validEmojiNames;
}
export function isEmojiNameValid(name) { export function isEmojiNameValid(name) {
return validEmojiNames.indexOf(name) >= 0; return validEmojiNames.indexOf(name) >= 0;
} }
...@@ -36,8 +81,8 @@ export function getEmojiCategoryMap() { ...@@ -36,8 +81,8 @@ export function getEmojiCategoryMap() {
}; };
Object.keys(emojiMap).forEach(name => { Object.keys(emojiMap).forEach(name => {
const emoji = emojiMap[name]; const emoji = emojiMap[name];
if (emojiCategoryMap[emoji.category]) { if (emojiCategoryMap[emoji.c]) {
emojiCategoryMap[emoji.category].push(name); emojiCategoryMap[emoji.c].push(name);
} }
}); });
} }
...@@ -58,8 +103,9 @@ export function getEmojiInfo(query) { ...@@ -58,8 +103,9 @@ export function getEmojiInfo(query) {
} }
export function emojiFallbackImageSrc(inputName) { export function emojiFallbackImageSrc(inputName) {
const { name, digest } = getEmojiInfo(inputName); const { name } = getEmojiInfo(inputName);
return `${gon.asset_host || ''}${gon.relative_url_root || ''}/assets/emoji/${name}-${digest}.png`; return `${gon.asset_host || ''}${gon.relative_url_root ||
''}/-/emojis/${EMOJI_VERSION}/${name}.png`;
} }
export function emojiImageTag(name, src) { export function emojiImageTag(name, src) {
...@@ -68,9 +114,8 @@ export function emojiImageTag(name, src) { ...@@ -68,9 +114,8 @@ export function emojiImageTag(name, src) {
export function glEmojiTag(inputName, options) { export function glEmojiTag(inputName, options) {
const opts = { sprite: false, forceFallback: false, ...options }; const opts = { sprite: false, forceFallback: false, ...options };
const { name, ...emojiInfo } = getEmojiInfo(inputName); const name = normalizeEmojiName(inputName);
const fallbackImageSrc = emojiFallbackImageSrc(name);
const fallbackSpriteClass = `emoji-${name}`; const fallbackSpriteClass = `emoji-${name}`;
const classList = []; const classList = [];
...@@ -79,24 +124,19 @@ export function glEmojiTag(inputName, options) { ...@@ -79,24 +124,19 @@ export function glEmojiTag(inputName, options) {
classList.push(fallbackSpriteClass); classList.push(fallbackSpriteClass);
} }
const classAttribute = classList.length > 0 ? `class="${classList.join(' ')}"` : ''; const classAttribute = classList.length > 0 ? `class="${classList.join(' ')}"` : '';
const fallbackSpriteAttribute = opts.sprite const fallbackSpriteAttribute = opts.sprite
? `data-fallback-sprite-class="${fallbackSpriteClass}"` ? `data-fallback-sprite-class="${fallbackSpriteClass}"`
: ''; : '';
let contents = emojiInfo.moji; const forceFallbackAttribute = opts.forceFallback ? 'data-force-fallback="true"' : '';
if (opts.forceFallback && !opts.sprite) {
contents = emojiImageTag(name, fallbackImageSrc);
}
return ` return `
<gl-emoji <gl-emoji
${classAttribute} ${classAttribute}
data-name="${name}" data-name="${name}"
data-fallback-src="${fallbackImageSrc}"
${fallbackSpriteAttribute} ${fallbackSpriteAttribute}
data-unicode-version="${emojiInfo.unicodeVersion}" ${forceFallbackAttribute}
title="${emojiInfo.description}"
> >
${contents}
</gl-emoji> </gl-emoji>
`; `;
} }
...@@ -5,6 +5,9 @@ import getUnicodeSupportMap from './unicode_support_map'; ...@@ -5,6 +5,9 @@ import getUnicodeSupportMap from './unicode_support_map';
let browserUnicodeSupportMap; let browserUnicodeSupportMap;
export default function isEmojiUnicodeSupportedByBrowser(emojiUnicode, unicodeVersion) { export default function isEmojiUnicodeSupportedByBrowser(emojiUnicode, unicodeVersion) {
// Our Spec browser would fail producing emoji maps
if (/\bHeadlessChrome\//.test(navigator.userAgent)) return true;
browserUnicodeSupportMap = browserUnicodeSupportMap || getUnicodeSupportMap(); browserUnicodeSupportMap = browserUnicodeSupportMap || getUnicodeSupportMap();
return isEmojiUnicodeSupported(browserUnicodeSupportMap, emojiUnicode, unicodeVersion); return isEmojiUnicodeSupported(browserUnicodeSupportMap, emojiUnicode, unicodeVersion);
} }
...@@ -102,15 +102,24 @@ export default class VisualTokenValue { ...@@ -102,15 +102,24 @@ export default class VisualTokenValue {
return ( return (
import(/* webpackChunkName: 'emoji' */ '../emoji') import(/* webpackChunkName: 'emoji' */ '../emoji')
.then(Emoji => { .then(Emoji => {
if (!Emoji.isEmojiNameValid(value)) { Emoji.initEmojiMap()
return; .then(() => {
} if (!Emoji.isEmojiNameValid(value)) {
return;
container.dataset.originalValue = value; }
element.innerHTML = Emoji.glEmojiTag(value);
container.dataset.originalValue = value;
element.innerHTML = Emoji.glEmojiTag(value);
})
// ignore error and leave emoji name in the search bar
.catch(err => {
throw err;
});
}) })
// ignore error and leave emoji name in the search bar // ignore error and leave emoji name in the search bar
.catch(() => {}) .catch(importError => {
throw importError;
})
); );
} }
} }
...@@ -487,9 +487,15 @@ class GfmAutoComplete { ...@@ -487,9 +487,15 @@ class GfmAutoComplete {
this.loadData($input, at, this.cachedData[at]); this.loadData($input, at, this.cachedData[at]);
} else if (GfmAutoComplete.atTypeMap[at] === 'emojis') { } else if (GfmAutoComplete.atTypeMap[at] === 'emojis') {
import(/* webpackChunkName: 'emoji' */ './emoji') import(/* webpackChunkName: 'emoji' */ './emoji')
.then(({ validEmojiNames, glEmojiTag }) => { .then(({ initEmojiMap, getValidEmojiNames, glEmojiTag }) => {
this.loadData($input, at, validEmojiNames); initEmojiMap()
GfmAutoComplete.glEmojiTag = glEmojiTag; .then(() => {
this.loadData($input, at, getValidEmojiNames());
GfmAutoComplete.glEmojiTag = glEmojiTag;
})
.catch(() => {
this.isLoadingData[at] = false;
});
}) })
.catch(() => { .catch(() => {
this.isLoadingData[at] = false; this.isLoadingData[at] = false;
......
...@@ -56,30 +56,34 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -56,30 +56,34 @@ document.addEventListener('DOMContentLoaded', () => {
import(/* webpackChunkName: 'emoji' */ '~/emoji') import(/* webpackChunkName: 'emoji' */ '~/emoji')
.then(Emoji => { .then(Emoji => {
const emojiMenu = new EmojiMenu( Emoji.initEmojiMap()
Emoji, .then(() => {
toggleEmojiMenuButtonSelector, const emojiMenu = new EmojiMenu(
'js-status-emoji-menu', Emoji,
selectEmojiCallback, toggleEmojiMenuButtonSelector,
); 'js-status-emoji-menu',
emojiMenu.bindEvents(); selectEmojiCallback,
);
emojiMenu.bindEvents();
const defaultEmojiTag = Emoji.glEmojiTag(defaultStatusEmoji); const defaultEmojiTag = Emoji.glEmojiTag(defaultStatusEmoji);
statusMessageField.addEventListener('input', () => { statusMessageField.addEventListener('input', () => {
const hasStatusMessage = statusMessageField.value.trim() !== ''; const hasStatusMessage = statusMessageField.value.trim() !== '';
const statusEmoji = findStatusEmoji(); const statusEmoji = findStatusEmoji();
if (hasStatusMessage && statusEmoji) { if (hasStatusMessage && statusEmoji) {
return; return;
} }
if (hasStatusMessage) { if (hasStatusMessage) {
toggleNoEmojiPlaceholder(false); toggleNoEmojiPlaceholder(false);
toggleEmojiMenuButton.innerHTML += defaultEmojiTag; toggleEmojiMenuButton.innerHTML += defaultEmojiTag;
} else if (statusEmoji.dataset.name === defaultStatusEmoji) { } else if (statusEmoji.dataset.name === defaultStatusEmoji) {
toggleNoEmojiPlaceholder(true); toggleNoEmojiPlaceholder(true);
removeStatusEmoji(); removeStatusEmoji();
} }
}); });
})
.catch(() => createFlash('Failed to load emoji list.'));
}) })
.catch(() => createFlash('Failed to load emoji list.')); .catch(() => createFlash('Failed to load emoji list.'));
}); });
...@@ -66,19 +66,23 @@ export default { ...@@ -66,19 +66,23 @@ export default {
import(/* webpackChunkName: 'emoji' */ '~/emoji') import(/* webpackChunkName: 'emoji' */ '~/emoji')
.then(Emoji => { .then(Emoji => {
if (this.emoji) { Emoji.initEmojiMap()
this.emojiTag = Emoji.glEmojiTag(this.emoji); .then(() => {
} if (this.emoji) {
this.noEmoji = this.emoji === ''; this.emojiTag = Emoji.glEmojiTag(this.emoji);
this.defaultEmojiTag = Emoji.glEmojiTag('speech_balloon'); }
this.noEmoji = this.emoji === '';
this.defaultEmojiTag = Emoji.glEmojiTag('speech_balloon');
this.emojiMenu = new EmojiMenuInModal( this.emojiMenu = new EmojiMenuInModal(
Emoji, Emoji,
toggleEmojiMenuButtonSelector, toggleEmojiMenuButtonSelector,
emojiMenuClass, emojiMenuClass,
this.setEmoji, this.setEmoji,
this.$refs.userStatusForm, this.$refs.userStatusForm,
); );
})
.catch(() => createFlash(__('Failed to load emoji list.')));
}) })
.catch(() => createFlash(__('Failed to load emoji list.'))); .catch(() => createFlash(__('Failed to load emoji list.')));
}, },
......
...@@ -30,33 +30,28 @@ namespace :gemojione do ...@@ -30,33 +30,28 @@ namespace :gemojione do
# We don't have `node_modules` available in built versions of GitLab # We don't have `node_modules` available in built versions of GitLab
FileUtils.cp_r(Rails.root.join('node_modules', 'emoji-unicode-version', 'emoji-unicode-version-map.json'), File.join(Rails.root, 'fixtures', 'emojis')) FileUtils.cp_r(Rails.root.join('node_modules', 'emoji-unicode-version', 'emoji-unicode-version-map.json'), File.join(Rails.root, 'fixtures', 'emojis'))
dir = Gemojione.images_path
resultant_emoji_map = {} resultant_emoji_map = {}
Gitlab::Emoji.emojis.each do |name, emoji_hash| Gitlab::Emoji.emojis.each do |name, emoji_hash|
# Ignore aliases # Ignore aliases
unless Gitlab::Emoji.emojis_aliases.key?(name) unless Gitlab::Emoji.emojis_aliases.key?(name)
fpath = File.join(dir, "#{emoji_hash['unicode']}.png")
hash_digest = Digest::SHA256.file(fpath).hexdigest
category = emoji_hash['category'] category = emoji_hash['category']
if name == 'gay_pride_flag' if name == 'gay_pride_flag'
category = 'flags' category = 'flags'
end end
entry = { entry = {
category: category, c: category,
moji: emoji_hash['moji'], e: emoji_hash['moji'],
description: emoji_hash['description'], d: emoji_hash['description'],
unicodeVersion: Gitlab::Emoji.emoji_unicode_version(name), u: Gitlab::Emoji.emoji_unicode_version(name)
digest: hash_digest
} }
resultant_emoji_map[name] = entry resultant_emoji_map[name] = entry
end end
end end
out = File.join(Rails.root, 'fixtures', 'emojis', 'digests.json') out = File.join(Rails.root, 'public', '-', 'emojis', '1', 'emojis.json')
File.open(out, 'w') do |handle| File.open(out, 'w') do |handle|
handle.write(JSON.pretty_generate(resultant_emoji_map)) handle.write(JSON.pretty_generate(resultant_emoji_map))
end end
......
...@@ -2968,6 +2968,9 @@ msgstr "" ...@@ -2968,6 +2968,9 @@ msgstr ""
msgid "Embed" msgid "Embed"
msgstr "" msgstr ""
msgid "Emojis|Something went wrong while loading emojis."
msgstr ""
msgid "Empty file" msgid "Empty file"
msgstr "" msgstr ""
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment