/* eslint-disable class-methods-use-this */
/* global Flash */
import _ from 'underscore';
import Cookies from 'js-cookie';
const animationEndEventString = 'animationend webkitAnimationEnd MSAnimationEnd oAnimationEnd';
const transitionEndEventString = 'transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd';
const requestAnimationFrame = window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.setTimeout;
const FROM_SENTENCE_REGEX = /(?:, and | and |, )/; // For separating lists produced by ruby's Array#toSentence
const categoryLabelMap = {
activity: 'Activity',
people: 'People',
nature: 'Nature',
food: 'Food',
travel: 'Travel',
objects: 'Objects',
symbols: 'Symbols',
flags: 'Flags',
};
class AwardsHandler {
constructor(emoji) {
this.emoji = emoji;
this.eventListeners = [];
// If the user shows intent let's pre-build the menu
this.registerEventListener('one', $(document), 'mouseenter focus', '.js-add-award', 'mouseenter focus', () => {
const $menu = $('.emoji-menu');
if ($menu.length === 0) {
requestAnimationFrame(() => {
this.createEmojiMenu();
});
}
});
this.registerEventListener('on', $(document), 'click', '.js-add-award', (e) => {
e.stopPropagation();
e.preventDefault();
this.showEmojiMenu($(e.currentTarget));
});
this.registerEventListener('on', $('html'), 'click', (e) => {
const $target = $(e.target);
if (!$target.closest('.emoji-menu-content').length) {
$('.js-awards-block.current').removeClass('current');
}
if (!$target.closest('.emoji-menu').length) {
if ($('.emoji-menu').is(':visible')) {
$('.js-add-award.is-active').removeClass('is-active');
$('.emoji-menu').removeClass('is-visible');
}
}
});
this.registerEventListener('on', $(document), 'click', '.js-emoji-btn', (e) => {
e.preventDefault();
const $target = $(e.currentTarget);
const $glEmojiElement = $target.find('gl-emoji');
const $spriteIconElement = $target.find('.icon');
const emojiName = ($glEmojiElement.length ? $glEmojiElement : $spriteIconElement).data('name');
$target.closest('.js-awards-block').addClass('current');
this.addAward(this.getVotesBlock(), this.getAwardUrl(), emojiName);
});
}
registerEventListener(method = 'on', element, ...args) {
element[method].call(element, ...args);
this.eventListeners.push({
element,
args,
});
}
showEmojiMenu($addBtn) {
if ($addBtn.hasClass('js-note-emoji')) {
$addBtn.closest('.note').find('.js-awards-block').addClass('current');
} else {
$addBtn.closest('.js-awards-block').addClass('current');
}
const $menu = $('.emoji-menu');
const $thumbsBtn = $menu.find('[data-name="thumbsup"], [data-name="thumbsdown"]').parent();
const $userAuthored = this.isUserAuthored($addBtn);
if ($menu.length) {
if ($menu.is('.is-visible')) {
$addBtn.removeClass('is-active');
$menu.removeClass('is-visible');
$('.js-emoji-menu-search').blur();
} else {
$addBtn.addClass('is-active');
this.positionMenu($menu, $addBtn);
$menu.addClass('is-visible');
$('.js-emoji-menu-search').focus();
}
} else {
$addBtn.addClass('is-loading is-active');
this.createEmojiMenu(() => {
const $createdMenu = $('.emoji-menu');
$addBtn.removeClass('is-loading');
this.positionMenu($createdMenu, $addBtn);
return setTimeout(() => {
$createdMenu.addClass('is-visible');
$('.js-emoji-menu-search').focus();
}, 200);
});
}
$thumbsBtn.toggleClass('disabled', $userAuthored);
}
// Create the emoji menu with the first category of emojis.
// Then render the remaining categories of emojis one by one to avoid jank.
createEmojiMenu(callback) {
if (this.isCreatingEmojiMenu) {
return;
}
this.isCreatingEmojiMenu = true;
// Render the first category
const categoryMap = this.emoji.getEmojiCategoryMap();
const categoryNameKey = Object.keys(categoryMap)[0];
const emojisInCategory = categoryMap[categoryNameKey];
const firstCategory = this.renderCategory(categoryLabelMap[categoryNameKey], emojisInCategory);
// Render the frequently used
const frequentlyUsedEmojis = this.getFrequentlyUsedEmojis();
let frequentlyUsedCatgegory = '';
if (frequentlyUsedEmojis.length > 0) {
frequentlyUsedCatgegory = this.renderCategory('Frequently used', frequentlyUsedEmojis, {
menuListClass: 'frequent-emojis',
});
}
const emojiMenuMarkup = `
${frequentlyUsedCatgegory}
${firstCategory}
`;
document.body.insertAdjacentHTML('beforeend', emojiMenuMarkup);
this.addRemainingEmojiMenuCategories();
this.setupSearch();
if (callback) {
callback();
}
}
addRemainingEmojiMenuCategories() {
if (this.isAddingRemainingEmojiMenuCategories) {
return;
}
this.isAddingRemainingEmojiMenuCategories = true;
const categoryMap = this.emoji.getEmojiCategoryMap();
// Avoid the jank and render the remaining categories separately
// This will take more time, but makes UI more responsive
const menu = document.querySelector('.emoji-menu');
const emojiContentElement = menu.querySelector('.emoji-menu-content');
const remainingCategories = Object.keys(categoryMap).slice(1);
const allCategoriesAddedPromise = remainingCategories.reduce(
(promiseChain, categoryNameKey) =>
promiseChain.then(() =>
new Promise((resolve) => {
const emojisInCategory = categoryMap[categoryNameKey];
const categoryMarkup = this.renderCategory(
categoryLabelMap[categoryNameKey],
emojisInCategory,
);
requestAnimationFrame(() => {
emojiContentElement.insertAdjacentHTML('beforeend', categoryMarkup);
resolve();
});
}),
),
Promise.resolve(),
);
allCategoriesAddedPromise.then(() => {
// Used for tests
// We check for the menu in case it was destroyed in the meantime
if (menu) {
menu.dispatchEvent(new CustomEvent('build-emoji-menu-finish'));
}
}).catch((err) => {
emojiContentElement.insertAdjacentHTML('beforeend', '
We encountered an error while adding the remaining categories
');
throw new Error(`Error occurred in addRemainingEmojiMenuCategories: ${err.message}`);
});
}
renderCategory(name, emojiList, opts = {}) {
return `