From dc2f2a800105b77e665ec2a00c6290f35b1a2ba3 Mon Sep 17 00:00:00 2001 From: Harrison Healey Date: Tue, 5 Jul 2016 11:58:18 -0400 Subject: PLT-3145 Custom Emojis (#3381) * Reorganized Backstage code to use a view controller and separated it from integrations code * Renamed InstalledIntegrations component to BackstageList * Added EmojiList page * Added AddEmoji page * Added custom emoji to autocomplete and text formatter * Moved system emoji to EmojiStore * Stopped trying to get emoji before logging in * Rerender posts when emojis change * Fixed submit handler on backstage pages to properly support enter * Removed debugging code * Updated javascript driver * Fixed unit tests * Fixed backstage routes * Added clientside validation to prevent users from creating an emoji with the same name as a system one * Fixed AddEmoji page to properly redirect when an emoji is created successfully * Fixed updating emoji list when an emoji is deleted * Added type prop to BackstageList to properly support using a table for the list * Added help text to EmojiList * Fixed backstage on smaller screen sizes * Disable custom emoji by default * Improved restrictions on creating emojis * Fixed non-admin users seeing the option to delete each other's emojis * Fixing gofmt * Fixed emoji unit tests * Fixed trying to get emoji from the server when it's disabled --- webapp/stores/emoji_store.jsx | 135 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 webapp/stores/emoji_store.jsx (limited to 'webapp/stores/emoji_store.jsx') diff --git a/webapp/stores/emoji_store.jsx b/webapp/stores/emoji_store.jsx new file mode 100644 index 000000000..5e1d81dd3 --- /dev/null +++ b/webapp/stores/emoji_store.jsx @@ -0,0 +1,135 @@ +// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import AppDispatcher from '../dispatcher/app_dispatcher.jsx'; +import Constants from 'utils/constants.jsx'; +import EventEmitter from 'events'; + +import EmojiJson from 'utils/emoji.json'; + +const ActionTypes = Constants.ActionTypes; + +const CHANGE_EVENT = 'changed'; + +class EmojiStore extends EventEmitter { + constructor() { + super(); + + this.dispatchToken = AppDispatcher.register(this.handleEventPayload.bind(this)); + + this.emojis = new Map(EmojiJson); + this.systemEmojis = new Map(EmojiJson); + + this.unicodeEmojis = new Map(); + for (const [, emoji] of this.systemEmojis) { + if (emoji.unicode) { + this.unicodeEmojis.set(emoji.unicode, emoji); + } + } + + this.receivedCustomEmojis = false; + this.customEmojis = new Map(); + } + + addChangeListener(callback) { + this.on(CHANGE_EVENT, callback); + } + + removeChangeListener(callback) { + this.removeListener(CHANGE_EVENT, callback); + } + + emitChange() { + this.emit(CHANGE_EVENT); + } + + hasReceivedCustomEmojis() { + return this.receivedCustomEmojis; + } + + setCustomEmojis(customEmojis) { + this.customEmojis = new Map(); + + for (const emoji of customEmojis) { + this.addCustomEmoji(emoji); + } + + // add custom emojis to the map first so that they can't override system ones + this.emojis = new Map([...this.customEmojis, ...this.systemEmojis]); + } + + addCustomEmoji(emoji) { + this.customEmojis.set(emoji.name, emoji); + } + + removeCustomEmoji(id) { + for (const [name, emoji] of this.customEmojis) { + if (emoji.id === id) { + this.customEmojis.delete(name); + break; + } + } + } + + getSystemEmojis() { + return this.systemEmojis; + } + + getCustomEmojiMap() { + return this.customEmojis; + } + + getEmojis() { + return this.emojis; + } + + has(name) { + return this.emojis.has(name); + } + + get(name) { + // prioritize system emojis so that custom ones can't override them + return this.emojis.get(name); + } + + hasUnicode(codepoint) { + return this.unicodeEmojis.has(codepoint); + } + + getUnicode(codepoint) { + return this.unicodeEmojis.get(codepoint); + } + + getEmojiImageUrl(emoji) { + if (emoji.id) { + // must match Client.getCustomEmojiImageUrl + return `/api/v3/emoji/${emoji.id}`; + } + + const filename = emoji.unicode || emoji.filename || emoji.name; + + return Constants.EMOJI_PATH + '/' + filename + '.png'; + } + + handleEventPayload(payload) { + const action = payload.action; + + switch (action.type) { + case ActionTypes.RECEIVED_CUSTOM_EMOJIS: + this.setCustomEmojis(action.emojis); + this.receivedCustomEmojis = true; + this.emitChange(); + break; + case ActionTypes.RECEIVED_CUSTOM_EMOJI: + this.addCustomEmoji(action.emoji); + this.emitChange(); + break; + case ActionTypes.REMOVED_CUSTOM_EMOJI: + this.removeCustomEmoji(action.id); + this.emitChange(); + break; + } + } +} + +export default new EmojiStore(); -- cgit v1.2.3-1-g7c22