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
Этот коммит содержится в:
коммит произвёл
Christopher Speller
родитель
2c8a5ffd97
Коммит
3998659236
@@ -11,8 +11,8 @@ import TeamStore from 'stores/team_store.jsx';
|
|||||||
import {loadNewDMIfNeeded, loadNewGMIfNeeded} from 'actions/user_actions.jsx';
|
import {loadNewDMIfNeeded, loadNewGMIfNeeded} from 'actions/user_actions.jsx';
|
||||||
import {sendDesktopNotification} from 'actions/notification_actions.jsx';
|
import {sendDesktopNotification} from 'actions/notification_actions.jsx';
|
||||||
|
|
||||||
import Constants from 'utils/constants.jsx';
|
import {ActionTypes, Constants} from 'utils/constants.jsx';
|
||||||
const ActionTypes = Constants.ActionTypes;
|
import {EMOJI_PATTERN} from 'utils/emoticons.jsx';
|
||||||
|
|
||||||
import {browserHistory} from 'react-router/es6';
|
import {browserHistory} from 'react-router/es6';
|
||||||
|
|
||||||
@@ -164,6 +164,15 @@ export function removeReaction(channelId, postId, emojiName) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function createPost(post, files, success) {
|
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(() => {
|
PostActions.createPost(post, files)(dispatch, getState).then(() => {
|
||||||
if (post.root_id) {
|
if (post.root_id) {
|
||||||
PostStore.storeCommentDraft(post.root_id, null);
|
PostStore.storeCommentDraft(post.root_id, null);
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ import {browserHistory} from 'react-router/es6';
|
|||||||
const ActionTypes = Constants.ActionTypes;
|
const ActionTypes = Constants.ActionTypes;
|
||||||
const KeyCodes = Constants.KeyCodes;
|
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 PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
@@ -87,9 +87,13 @@ export default class CreateComment extends React.Component {
|
|||||||
if (this.state.message === '') {
|
if (this.state.message === '') {
|
||||||
this.setState({message: ':' + emojiAlias + ': '});
|
this.setState({message: ':' + emojiAlias + ': '});
|
||||||
} else {
|
} else {
|
||||||
//check whether there is already a blank at the end of the current message
|
// Check whether there is already a blank at the end of the current message
|
||||||
const newMessage = (/\s+$/.test(this.state.message)) ?
|
let newMessage;
|
||||||
this.state.message + ':' + emojiAlias + ': ' : this.state.message + ' :' + emojiAlias + ': ';
|
if ((/\s+$/).test(this.state.message)) {
|
||||||
|
newMessage = this.state.message + ':' + emojiAlias + ': ';
|
||||||
|
} else {
|
||||||
|
newMessage = this.state.message + ' :' + emojiAlias + ': ';
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({message: newMessage});
|
this.setState({message: newMessage});
|
||||||
}
|
}
|
||||||
@@ -230,14 +234,6 @@ export default class CreateComment extends React.Component {
|
|||||||
|
|
||||||
GlobalActions.emitUserCommentedEvent(post);
|
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);
|
PostActions.createPost(post, this.state.fileInfos);
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ import React from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
export const REACTION_PATTERN = /^(\+|-):([^:\s]+):\s*$/;
|
export const REACTION_PATTERN = /^(\+|-):([^:\s]+):\s*$/;
|
||||||
export const EMOJI_PATTERN = /:[A-Za-z-_0-9]*:/g;
|
|
||||||
|
|
||||||
export default class CreatePost extends React.Component {
|
export default class CreatePost extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@@ -268,14 +267,6 @@ export default class CreatePost extends React.Component {
|
|||||||
|
|
||||||
GlobalActions.emitUserPostedEvent(post);
|
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,
|
PostActions.createPost(post, this.state.fileInfos,
|
||||||
() => GlobalActions.postListScrollChange(true),
|
() => GlobalActions.postListScrollChange(true),
|
||||||
(err) => {
|
(err) => {
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See License.txt for license information.
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
export default class EmojiPickerCategory extends React.Component {
|
export default class EmojiPickerCategory extends React.Component {
|
||||||
|
|||||||
@@ -169,45 +169,57 @@ export default class EmojiPicker extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderCategory(category, isLoaded, filter) {
|
renderCategory(category, isLoaded, filter) {
|
||||||
const items = [];
|
let emojis;
|
||||||
let indices = [];
|
|
||||||
let recentEmojis = [];
|
|
||||||
|
|
||||||
if (category === 'recent') {
|
if (category === 'recent') {
|
||||||
recentEmojis = EmojiStore.getRecentEmojis();
|
const recentEmojis = [...EmojiStore.getRecentEmojis()];
|
||||||
indices = [...Array(recentEmojis.length).keys()];
|
|
||||||
|
|
||||||
// reverse indices so most recently added is first
|
// Reverse so most recently added is first
|
||||||
indices.reverse();
|
recentEmojis.reverse();
|
||||||
|
|
||||||
|
emojis = recentEmojis.filter((name) => {
|
||||||
|
return EmojiStore.has(name);
|
||||||
|
}).map((name) => {
|
||||||
|
return EmojiStore.get(name);
|
||||||
|
});
|
||||||
} else {
|
} 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) {
|
// Apply filter
|
||||||
let emoji = {};
|
emojis = emojis.filter((emoji) => {
|
||||||
|
if (emoji.name) {
|
||||||
|
return emoji.name.indexOf(filter) !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const alias of emoji.aliases) {
|
||||||
|
if (alias.indexOf(filter) !== -1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
const items = emojis.map((emoji) => {
|
||||||
|
const name = emoji.name || emoji.aliases[0];
|
||||||
|
let key;
|
||||||
if (category === 'recent') {
|
if (category === 'recent') {
|
||||||
emoji = recentEmojis[index];
|
key = 'system_recent_' + name;
|
||||||
|
} else if (category === 'custom' && emoji.name) {
|
||||||
|
key = 'custom_' + name;
|
||||||
} else {
|
} else {
|
||||||
emoji = Emoji.Emojis[index];
|
key = 'system_' + name;
|
||||||
}
|
|
||||||
if (filter) {
|
|
||||||
let matches = false;
|
|
||||||
|
|
||||||
for (const alias of emoji.aliases || [...emoji.name]) {
|
|
||||||
if (alias.indexOf(filter) !== -1) {
|
|
||||||
matches = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!matches) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
items.push(
|
return (
|
||||||
<EmojiPickerItem
|
<EmojiPickerItem
|
||||||
key={'system_' + (category === 'recent' ? 'recent_' : '') + (emoji.name || emoji.aliases[0])}
|
key={key}
|
||||||
emoji={emoji}
|
emoji={emoji}
|
||||||
category={category}
|
category={category}
|
||||||
isLoaded={isLoaded}
|
isLoaded={isLoaded}
|
||||||
@@ -217,30 +229,7 @@ export default class EmojiPicker extends React.Component {
|
|||||||
onItemUnmount={this.handleItemUnmount}
|
onItemUnmount={this.handleItemUnmount}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
|
|
||||||
if (category === 'custom') {
|
|
||||||
const customEmojis = EmojiStore.getCustomEmojiMap().values();
|
|
||||||
|
|
||||||
for (const emoji of customEmojis) {
|
|
||||||
if (filter && emoji.name.indexOf(filter) === -1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
items.push(
|
|
||||||
<EmojiPickerItem
|
|
||||||
key={'custom_' + emoji.name}
|
|
||||||
emoji={emoji}
|
|
||||||
category={category}
|
|
||||||
onItemOver={this.handleItemOver}
|
|
||||||
onItemOut={this.handleItemOut}
|
|
||||||
onItemClick={this.handleItemClick}
|
|
||||||
onItemUnmount={this.handleItemUnmount}
|
|
||||||
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only render the header if there's any visible items
|
// Only render the header if there's any visible items
|
||||||
let header = null;
|
let header = null;
|
||||||
|
|||||||
@@ -123,57 +123,54 @@ class EmojiStore extends EventEmitter {
|
|||||||
return this.map.get(name);
|
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 recentEmojis = this.getRecentEmojis();
|
||||||
|
|
||||||
const alias = rawAlias.split(':').join('');
|
let name;
|
||||||
|
const emoji = this.get(alias);
|
||||||
let emoji = this.getCustomEmojiMap().get(alias);
|
|
||||||
|
|
||||||
if (!emoji) {
|
if (!emoji) {
|
||||||
const emojiIndex = Emoji.EmojiIndicesByAlias.get(alias);
|
|
||||||
emoji = Emoji.Emojis[emojiIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!emoji) {
|
|
||||||
// something is wrong, so we return
|
|
||||||
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
|
const index = recentEmojis.indexOf(name);
|
||||||
for (let i = recentEmojis.length - 1; i >= 0; i--) {
|
if (index !== -1) {
|
||||||
if ((emoji.name && recentEmojis[i].name === emoji.name) ||
|
recentEmojis.splice(index, 1);
|
||||||
(emoji.filename && recentEmojis[i].filename === emoji.filename)) {
|
|
||||||
recentEmojis.splice(i, 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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) {
|
if (recentEmojis.length > MAXIMUM_RECENT_EMOJI) {
|
||||||
recentEmojis.splice(0, recentEmojis.length - MAXIMUM_RECENT_EMOJI);
|
recentEmojis.splice(0, recentEmojis.length - MAXIMUM_RECENT_EMOJI);
|
||||||
}
|
}
|
||||||
|
|
||||||
localStorage.setItem(Constants.RECENT_EMOJI_KEY, JSON.stringify(recentEmojis));
|
localStorage.setItem(Constants.RECENT_EMOJI_KEY, JSON.stringify(recentEmojis));
|
||||||
}
|
}
|
||||||
|
|
||||||
getRecentEmojis() {
|
getRecentEmojis() {
|
||||||
const result = JSON.parse(localStorage.getItem(Constants.RECENT_EMOJI_KEY));
|
let recentEmojis;
|
||||||
if (!result) {
|
try {
|
||||||
|
recentEmojis = JSON.parse(localStorage.getItem(Constants.RECENT_EMOJI_KEY));
|
||||||
|
} catch (e) {
|
||||||
|
// Errors are handled below
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!recentEmojis) {
|
||||||
return [];
|
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) {
|
hasUnicode(codepoint) {
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ export const emoticonPatterns = {
|
|||||||
thumbsdown: /(^|\s)(:-1:)(?=$|\s)/g // :-1:
|
thumbsdown: /(^|\s)(:-1:)(?=$|\s)/g // :-1:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const EMOJI_PATTERN = /(:([a-zA-Z0-9_-]+):)/g;
|
||||||
|
|
||||||
export function handleEmoticons(text, tokens, emojis) {
|
export function handleEmoticons(text, tokens, emojis) {
|
||||||
let output = text;
|
let output = text;
|
||||||
|
|
||||||
@@ -49,7 +51,7 @@ export function handleEmoticons(text, tokens, emojis) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// match named emoticons like :goat:
|
// 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
|
// match text smilies like :D
|
||||||
for (const name of Object.keys(emoticonPatterns)) {
|
for (const name of Object.keys(emoticonPatterns)) {
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user