From 399865923658319d6d12a7719bc3b5554218bbad Mon Sep 17 00:00:00 2001 From: Harrison Healey Date: Fri, 4 Aug 2017 14:03:41 -0400 Subject: PLT-7267 Refactored tracking of recent emojis to hide deleted emojis (#7102) * Fixed local ESLint error * PLT-7267 Refactored tracking of recent emojis to hide deleted emojis --- webapp/actions/post_actions.jsx | 13 ++- .../components/create_comment/create_comment.jsx | 20 ++--- webapp/components/create_post.jsx | 9 --- .../components/emoji_picker_category.jsx | 3 +- webapp/components/emoji_picker/emoji_picker.jsx | 93 ++++++++++------------ webapp/stores/emoji_store.jsx | 65 ++++++++------- webapp/utils/emoticons.jsx | 4 +- 7 files changed, 95 insertions(+), 112 deletions(-) diff --git a/webapp/actions/post_actions.jsx b/webapp/actions/post_actions.jsx index e96e8306b..60913b171 100644 --- a/webapp/actions/post_actions.jsx +++ b/webapp/actions/post_actions.jsx @@ -11,8 +11,8 @@ import TeamStore from 'stores/team_store.jsx'; import {loadNewDMIfNeeded, loadNewGMIfNeeded} from 'actions/user_actions.jsx'; import {sendDesktopNotification} from 'actions/notification_actions.jsx'; -import Constants from 'utils/constants.jsx'; -const ActionTypes = Constants.ActionTypes; +import {ActionTypes, Constants} from 'utils/constants.jsx'; +import {EMOJI_PATTERN} from 'utils/emoticons.jsx'; import {browserHistory} from 'react-router/es6'; @@ -164,6 +164,15 @@ export function removeReaction(channelId, postId, emojiName) { } export function createPost(post, files, success) { + // parse message and emit emoji event + const emojis = post.message.match(EMOJI_PATTERN); + if (emojis) { + for (const emoji of emojis) { + const trimmed = emoji.substring(1, emoji.length - 1); + emitEmojiPosted(trimmed); + } + } + PostActions.createPost(post, files)(dispatch, getState).then(() => { if (post.root_id) { PostStore.storeCommentDraft(post.root_id, null); diff --git a/webapp/components/create_comment/create_comment.jsx b/webapp/components/create_comment/create_comment.jsx index 62eff9e28..9370d7b48 100644 --- a/webapp/components/create_comment/create_comment.jsx +++ b/webapp/components/create_comment/create_comment.jsx @@ -31,7 +31,7 @@ import {browserHistory} from 'react-router/es6'; const ActionTypes = Constants.ActionTypes; const KeyCodes = Constants.KeyCodes; -import {REACTION_PATTERN, EMOJI_PATTERN} from 'components/create_post.jsx'; +import {REACTION_PATTERN} from 'components/create_post.jsx'; import PropTypes from 'prop-types'; import React from 'react'; @@ -87,9 +87,13 @@ export default class CreateComment extends React.Component { if (this.state.message === '') { this.setState({message: ':' + emojiAlias + ': '}); } else { - //check whether there is already a blank at the end of the current message - const newMessage = (/\s+$/.test(this.state.message)) ? - this.state.message + ':' + emojiAlias + ': ' : this.state.message + ' :' + emojiAlias + ': '; + // Check whether there is already a blank at the end of the current message + let newMessage; + if ((/\s+$/).test(this.state.message)) { + newMessage = this.state.message + ':' + emojiAlias + ': '; + } else { + newMessage = this.state.message + ' :' + emojiAlias + ': '; + } this.setState({message: newMessage}); } @@ -230,14 +234,6 @@ export default class CreateComment extends React.Component { GlobalActions.emitUserCommentedEvent(post); - const emojiResult = post.message.match(EMOJI_PATTERN); - if (emojiResult) { - // parse message and emit emoji event - emojiResult.forEach((emoji) => { - PostActions.emitEmojiPosted(emoji); - }); - } - PostActions.createPost(post, this.state.fileInfos); this.setState({ diff --git a/webapp/components/create_post.jsx b/webapp/components/create_post.jsx index e5ead4e84..f822f46f4 100644 --- a/webapp/components/create_post.jsx +++ b/webapp/components/create_post.jsx @@ -41,7 +41,6 @@ import React from 'react'; import PropTypes from 'prop-types'; export const REACTION_PATTERN = /^(\+|-):([^:\s]+):\s*$/; -export const EMOJI_PATTERN = /:[A-Za-z-_0-9]*:/g; export default class CreatePost extends React.Component { constructor(props) { @@ -268,14 +267,6 @@ export default class CreatePost extends React.Component { GlobalActions.emitUserPostedEvent(post); - // parse message and emit emoji event - const emojiResult = post.message.match(EMOJI_PATTERN); - if (emojiResult) { - emojiResult.forEach((emoji) => { - PostActions.emitEmojiPosted(emoji); - }); - } - PostActions.createPost(post, this.state.fileInfos, () => GlobalActions.postListScrollChange(true), (err) => { diff --git a/webapp/components/emoji_picker/components/emoji_picker_category.jsx b/webapp/components/emoji_picker/components/emoji_picker_category.jsx index 579aaed28..66146106b 100644 --- a/webapp/components/emoji_picker/components/emoji_picker_category.jsx +++ b/webapp/components/emoji_picker/components/emoji_picker_category.jsx @@ -1,8 +1,7 @@ -import PropTypes from 'prop-types'; - // Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. +import PropTypes from 'prop-types'; import React from 'react'; export default class EmojiPickerCategory extends React.Component { diff --git a/webapp/components/emoji_picker/emoji_picker.jsx b/webapp/components/emoji_picker/emoji_picker.jsx index 8b5375000..49050a7d8 100644 --- a/webapp/components/emoji_picker/emoji_picker.jsx +++ b/webapp/components/emoji_picker/emoji_picker.jsx @@ -169,45 +169,57 @@ export default class EmojiPicker extends React.Component { } renderCategory(category, isLoaded, filter) { - const items = []; - let indices = []; - let recentEmojis = []; - + let emojis; if (category === 'recent') { - recentEmojis = EmojiStore.getRecentEmojis(); - indices = [...Array(recentEmojis.length).keys()]; + const recentEmojis = [...EmojiStore.getRecentEmojis()]; - // reverse indices so most recently added is first - indices.reverse(); + // Reverse so most recently added is first + recentEmojis.reverse(); + + emojis = recentEmojis.filter((name) => { + return EmojiStore.has(name); + }).map((name) => { + return EmojiStore.get(name); + }); } else { - indices = Emoji.EmojiIndicesByCategory.get(category) || []; + const indices = Emoji.EmojiIndicesByCategory.get(category) || []; + + emojis = indices.map((index) => Emoji.Emojis[index]); + + if (category === 'custom') { + emojis = emojis.concat([...EmojiStore.getCustomEmojiMap().values()]); + } } - for (const index of indices) { - let emoji = {}; - if (category === 'recent') { - emoji = recentEmojis[index]; - } else { - emoji = Emoji.Emojis[index]; + // Apply filter + emojis = emojis.filter((emoji) => { + if (emoji.name) { + return emoji.name.indexOf(filter) !== -1; } - if (filter) { - let matches = false; - - for (const alias of emoji.aliases || [...emoji.name]) { - if (alias.indexOf(filter) !== -1) { - matches = true; - break; - } - } - if (!matches) { - continue; + for (const alias of emoji.aliases) { + if (alias.indexOf(filter) !== -1) { + return true; } } - items.push( + return false; + }); + + const items = emojis.map((emoji) => { + const name = emoji.name || emoji.aliases[0]; + let key; + if (category === 'recent') { + key = 'system_recent_' + name; + } else if (category === 'custom' && emoji.name) { + key = 'custom_' + name; + } else { + key = 'system_' + name; + } + + return ( ); - } - - if (category === 'custom') { - const customEmojis = EmojiStore.getCustomEmojiMap().values(); - - for (const emoji of customEmojis) { - if (filter && emoji.name.indexOf(filter) === -1) { - continue; - } - - items.push( - - ); - } - } + }); // Only render the header if there's any visible items let header = null; diff --git a/webapp/stores/emoji_store.jsx b/webapp/stores/emoji_store.jsx index d8af75dbc..0ec3468ff 100644 --- a/webapp/stores/emoji_store.jsx +++ b/webapp/stores/emoji_store.jsx @@ -123,57 +123,54 @@ class EmojiStore extends EventEmitter { return this.map.get(name); } - removeRecentEmoji(id) { + addRecentEmoji(alias) { const recentEmojis = this.getRecentEmojis(); - for (let i = recentEmojis.length - 1; i >= 0; i--) { - if (recentEmojis[i].id === id) { - recentEmojis.splice(i, 1); - break; - } - } - localStorage.setItem(Constants.RECENT_EMOJI_KEY, JSON.stringify(recentEmojis)); - } - - addRecentEmoji(rawAlias) { - const recentEmojis = this.getRecentEmojis(); - - const alias = rawAlias.split(':').join(''); - - let emoji = this.getCustomEmojiMap().get(alias); + let name; + const emoji = this.get(alias); if (!emoji) { - const emojiIndex = Emoji.EmojiIndicesByAlias.get(alias); - emoji = Emoji.Emojis[emojiIndex]; - } - - if (!emoji) { - // something is wrong, so we return return; + } else if (emoji.name) { + name = emoji.name; + } else { + name = emoji.aliases[0]; } - // odd workaround to the lack of array.findLastIndex - reverse looping & splice - for (let i = recentEmojis.length - 1; i >= 0; i--) { - if ((emoji.name && recentEmojis[i].name === emoji.name) || - (emoji.filename && recentEmojis[i].filename === emoji.filename)) { - recentEmojis.splice(i, 1); - break; - } + const index = recentEmojis.indexOf(name); + if (index !== -1) { + recentEmojis.splice(index, 1); } - recentEmojis.push(emoji); - // cut off the _top_ if it's over length (since new are added to end) + recentEmojis.push(name); + if (recentEmojis.length > MAXIMUM_RECENT_EMOJI) { recentEmojis.splice(0, recentEmojis.length - MAXIMUM_RECENT_EMOJI); } + localStorage.setItem(Constants.RECENT_EMOJI_KEY, JSON.stringify(recentEmojis)); } getRecentEmojis() { - const result = JSON.parse(localStorage.getItem(Constants.RECENT_EMOJI_KEY)); - if (!result) { + let recentEmojis; + try { + recentEmojis = JSON.parse(localStorage.getItem(Constants.RECENT_EMOJI_KEY)); + } catch (e) { + // Errors are handled below + } + + if (!recentEmojis) { return []; } - return result; + + if (recentEmojis.length > 0 && typeof recentEmojis[0] === 'object') { + // Prior to PLT-7267, recent emojis were stored with the entire object for the emoji, but this + // has been changed to store only the names of the emojis, so we need to change that + recentEmojis = recentEmojis.map((emoji) => { + return emoji.name || emoji.aliases[0]; + }); + } + + return recentEmojis; } hasUnicode(codepoint) { diff --git a/webapp/utils/emoticons.jsx b/webapp/utils/emoticons.jsx index b13b50a77..8a10e254e 100644 --- a/webapp/utils/emoticons.jsx +++ b/webapp/utils/emoticons.jsx @@ -26,6 +26,8 @@ export const emoticonPatterns = { thumbsdown: /(^|\s)(:-1:)(?=$|\s)/g // :-1: }; +export const EMOJI_PATTERN = /(:([a-zA-Z0-9_-]+):)/g; + export function handleEmoticons(text, tokens, emojis) { let output = text; @@ -49,7 +51,7 @@ export function handleEmoticons(text, tokens, emojis) { } // match named emoticons like :goat: - output = output.replace(/(:([a-zA-Z0-9_-]+):)/g, (fullMatch, matchText, name) => replaceEmoticonWithToken(fullMatch, '', matchText, name)); + output = output.replace(EMOJI_PATTERN, (fullMatch, matchText, name) => replaceEmoticonWithToken(fullMatch, '', matchText, name)); // match text smilies like :D for (const name of Object.keys(emoticonPatterns)) { -- cgit v1.2.3-1-g7c22