Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[IMPROVE] Emoji picker for large amount of custom emojis #27745

Merged
merged 10 commits into from
Jan 23, 2023
19 changes: 6 additions & 13 deletions apps/meteor/app/emoji-custom/client/lib/emojiCustom.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,18 @@ import { isSetNotNull } from './function-isSet';
import { RoomManager } from '../../../ui-utils/client';
import { emoji, EmojiPicker } from '../../../emoji/client';
import { CachedCollectionManager } from '../../../ui-cached-collection/client';
import { APIClient } from '../../../utils/client';
import { APIClient, getURL } from '../../../utils/client';

export const getEmojiUrlFromName = function (name, extension) {
Session.get;
if (name == null) {
return;
}

const key = `emoji_random_${name}`;

let random = 0;
if (isSetNotNull(() => Session.keys[key])) {
random = Session.keys[key];
}
const random = isSetNotNull(() => Session.keys[key]) ? Session.keys[key] : 0;

if (name == null) {
return;
}
const path = __meteor_runtime_config__.ROOT_URL_PATH_PREFIX || '';
return `${path}/emoji-custom/${encodeURIComponent(name)}.${extension}?_dc=${random}`;
return getURL(`/emoji-custom/${encodeURIComponent(name)}.${extension}?_dc=${random}`);
};

export const deleteEmojiCustom = function (emojiData) {
Expand Down Expand Up @@ -200,8 +195,6 @@ Meteor.startup(() =>
};
}
}

EmojiPicker.updateRecent('rocket');
} catch (e) {
console.error('Error getting custom emoji', e);
}
Expand Down
29 changes: 10 additions & 19 deletions apps/meteor/app/emoji-custom/server/startup/emoji-custom.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,36 +82,27 @@ Meteor.startup(function () {
return;
}

let fileUploadDate = undefined;
if (file.uploadDate != null) {
fileUploadDate = file.uploadDate.toUTCString();
}
const fileUploadDate = file.uploadDate != null ? file.uploadDate.toUTCString() : undefined;

const reqModifiedHeader = req.headers['if-modified-since'];
if (reqModifiedHeader != null) {
if (reqModifiedHeader === fileUploadDate) {
res.setHeader('Last-Modified', reqModifiedHeader);
res.writeHead(304);
res.end();
return;
}
if (reqModifiedHeader != null && reqModifiedHeader === fileUploadDate) {
res.setHeader('Last-Modified', reqModifiedHeader);
res.writeHead(304);
res.end();
return;
}

res.setHeader('Cache-Control', 'public, max-age=0');
res.setHeader('Expires', '-1');
if (fileUploadDate != null) {
res.setHeader('Last-Modified', fileUploadDate);
} else {
res.setHeader('Last-Modified', new Date().toUTCString());
}
res.setHeader('Cache-Control', 'public, max-age=31536000');
res.setHeader('Last-Modified', fileUploadDate || new Date().toUTCString());
res.setHeader('Content-Length', file.length);

if (/^svg$/i.test(params.emoji.split('.').pop())) {
res.setHeader('Content-Type', 'image/svg+xml');
} else if (/^png$/i.test(params.emoji.split('.').pop())) {
res.setHeader('Content-Type', 'image/png');
} else {
res.setHeader('Content-Type', 'image/jpeg');
}
res.setHeader('Content-Length', file.length);

file.readStream.pipe(res);
}),
Expand Down
66 changes: 53 additions & 13 deletions apps/meteor/app/emoji/client/emojiPicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,46 +17,66 @@ const emojiListByCategory = new ReactiveDict('emojiList');
const getEmojiElement = (emoji, image) =>
image && `<li class="emoji-${emoji} emoji-picker-item" data-emoji="${emoji}" title="${emoji}">${image}</li>`;

const createEmojiList = (category, actualTone) => {
// used as a function so `t` can use the user's language correctly when called
const loadMoreLink = () => `<li class="emoji-picker-load-more"><a href="#load-more">${t('Load_more')}</a></li>`;

let customItems = 90;

const createEmojiList = (category, actualTone, limit = null) => {
const html =
Object.values(emoji.packages)
.map((emojiPackage) => {
if (!emojiPackage.emojisByCategory || !emojiPackage.emojisByCategory[category]) {
return;
}

return emojiPackage.emojisByCategory[category]
.map((current) => {
const tone = actualTone > 0 && emojiPackage.toneList.hasOwnProperty(current) ? `_tone${actualTone}` : '';
return getEmojiElement(current, emojiPackage.renderPicker(`:${current}${tone}:`));
})
.join('');
const result = [];

const total = emojiPackage.emojisByCategory[category].length;

const listTotal = limit ? Math.min(limit, total) : total;

for (let i = 0; i < listTotal; i++) {
const current = emojiPackage.emojisByCategory[category][i];

const tone = actualTone > 0 && emojiPackage.toneList.hasOwnProperty(current) ? `_tone${actualTone}` : '';
result.push(getEmojiElement(current, emojiPackage.renderPicker(`:${current}${tone}:`)));
}

if (limit > 0 && total > limit) {
result.push(loadMoreLink());
}

return result.join('');
})
.join('') || `<li>${t('No_emojis_found')}</li>`;

return html;
};

export function updateRecentEmoji(category) {
emojiListByCategory.set(category, createEmojiList(category));
emojiListByCategory.set(category, createEmojiList(category, null, category === 'rocket' ? customItems : null));
}

const createPickerEmojis = (instance) => {
const categories = instance.categoriesList;
const actualTone = instance.tone;

categories.forEach((category) => emojiListByCategory.set(category.key, createEmojiList(category.key, actualTone)));
categories.forEach((category) =>
emojiListByCategory.set(category.key, createEmojiList(category.key, actualTone, category.key === 'rocket' ? customItems : null)),
);
};

function getEmojisBySearchTerm(searchTerm) {
function getEmojisBySearchTerm(searchTerm, limit) {
let html = '<ul class="emoji-list">';
const t = Template.instance();
const actualTone = t.tone;
const actualTone = Template.instance().tone;

EmojiPicker.currentCategory.set('');

const searchRegExp = new RegExp(escapeRegExp(searchTerm.replace(/:/g, '')), 'i');

let totalFound = 0;

for (let current in emoji.list) {
if (!emoji.list.hasOwnProperty(current)) {
continue;
Expand Down Expand Up @@ -89,8 +109,14 @@ function getEmojisBySearchTerm(searchTerm) {
if (emojiFound) {
const image = emoji.packages[emojiPackage].renderPicker(`:${current}${tone}:`);
html += getEmojiElement(current, image);
totalFound++;
}
}

if (totalFound >= limit) {
html += loadMoreLink();
break;
}
}
html += '</ul>';

Expand All @@ -116,7 +142,7 @@ Template.emojiPicker.helpers({
return Template.instance().currentSearchTerm.get().length > 0;
},
searchResults() {
return getEmojisBySearchTerm(Template.instance().currentSearchTerm.get());
return getEmojisBySearchTerm(Template.instance().currentSearchTerm.get(), Template.instance().searchTermItems.get());
},
emojiList(category) {
return emojiListByCategory.get(category);
Expand Down Expand Up @@ -190,6 +216,18 @@ Template.emojiPicker.events({

instance.$('.tone-selector').toggleClass('show');
},
'click .emoji-picker-load-more > a'(event, instance) {
event.stopPropagation();
event.preventDefault();

if (instance.currentSearchTerm.get().length > 0) {
instance.searchTermItems.set(instance.searchTermItems.get() + 90);
return;
}

customItems += 90;
emojiListByCategory.set('rocket', createEmojiList('rocket', 0, customItems));
},
'click .tone-selector .tone'(event, instance) {
event.stopPropagation();
event.preventDefault();
Expand Down Expand Up @@ -241,6 +279,7 @@ Template.emojiPicker.events({
input.val('');
}
instance.currentSearchTerm.set('');
instance.searchTermItems.set(90);

EmojiPicker.pickEmoji(_emoji + tone);
},
Expand All @@ -266,6 +305,7 @@ Template.emojiPicker.onCreated(function () {
const recent = EmojiPicker.getRecent();

this.currentSearchTerm = new ReactiveVar('');
this.searchTermItems = new ReactiveVar(90);

this.categoriesList = [];
for (const emojiPackage in emoji.packages) {
Expand Down
5 changes: 2 additions & 3 deletions apps/meteor/app/emoji/client/lib/EmojiPicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ReactiveVar } from 'meteor/reactive-var';
import { Tracker } from 'meteor/tracker';

import { emoji } from '../../lib/rocketchat';
import { updateRecentEmoji } from '../emojiPicker';

let updatePositions = true;

Expand Down Expand Up @@ -156,9 +157,7 @@ export const EmojiPicker = {
this.recent.splice(pos, 1);
Meteor._localStorage.setItem('emoji.recent', this.recent);
},
async updateRecent(category) {
const emojiPickerImport = await import('../emojiPicker');
const { updateRecentEmoji } = emojiPickerImport;
updateRecent(category) {
updateRecentEmoji(category);
},
calculateCategoryPositions() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@
}
}

& li.emoji-picker-load-more {
text-align: center;
}

&.visible {
display: block;
}
Expand Down