From dc2f2a800105b77e665ec2a00c6290f35b1a2ba3 Mon Sep 17 00:00:00 2001
From: Harrison Healey <harrisonmhealey@gmail.com>
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

