diff --git a/app/assets/javascripts/discourse/components/d-editor.js.es6 b/app/assets/javascripts/discourse/components/d-editor.js.es6 index ad5611cd3f1..8cd3c541bae 100644 --- a/app/assets/javascripts/discourse/components/d-editor.js.es6 +++ b/app/assets/javascripts/discourse/components/d-editor.js.es6 @@ -220,6 +220,7 @@ export default Ember.Component.extend({ _mouseTrap: null, showLink: true, emojiPickerIsActive: false, + emojiStore: Ember.inject.service("emoji-store"), @computed("placeholder") placeholderTranslated(placeholder) { @@ -422,6 +423,7 @@ export default Ember.Component.extend({ transformComplete: v => { if (v.code) { + this.emojiStore.track(v.code); return `${v.code}:`; } else { $editorInput.autocomplete({ cancel: true }); @@ -458,7 +460,17 @@ export default Ember.Component.extend({ } if (term === "") { - return resolve(["slight_smile", "smile", "wink", "sunny", "blush"]); + if (this.emojiStore.favorites.length) { + return resolve(this.emojiStore.favorites.slice(0, 5)); + } else { + return resolve([ + "slight_smile", + "smile", + "wink", + "sunny", + "blush" + ]); + } } if (translations[full]) { diff --git a/app/assets/javascripts/discourse/components/emoji-picker.js.es6 b/app/assets/javascripts/discourse/components/emoji-picker.js.es6 index 88185d9bd89..dfe6287c670 100644 --- a/app/assets/javascripts/discourse/components/emoji-picker.js.es6 +++ b/app/assets/javascripts/discourse/components/emoji-picker.js.es6 @@ -1,7 +1,7 @@ import { on, observes } from "ember-addons/ember-computed-decorators"; import { findRawTemplate } from "discourse/lib/raw-templates"; import { emojiUrlFor } from "discourse/lib/text"; -import KeyValueStore from "discourse/lib/key-value-store"; + import { extendedEmojiList, isSkinTonableEmoji, @@ -10,21 +10,14 @@ import { import { safariHacksDisabled } from "discourse/lib/utilities"; const { run } = Ember; -const keyValueStore = new KeyValueStore("discourse_emojis_"); -const EMOJI_USAGE = "emojiUsage"; -const EMOJI_SELECTED_DIVERSITY = "emojiSelectedDiversity"; const PER_ROW = 11; const customEmojis = _.keys(extendedEmojiList()).map(code => { return { code, src: emojiUrlFor(code) }; }); -export function resetCache() { - keyValueStore.setObject({ key: EMOJI_USAGE, value: [] }); - keyValueStore.setObject({ key: EMOJI_SELECTED_DIVERSITY, value: 1 }); -} - export default Ember.Component.extend({ automaticPositioning: true, + emojiStore: Ember.inject.service("emoji-store"), close() { this._unbindEvents(); @@ -46,11 +39,10 @@ export default Ember.Component.extend({ this.$results = this.$picker.find(".results"); this.$list = this.$picker.find(".list"); - this.set( - "selectedDiversity", - keyValueStore.getObject(EMOJI_SELECTED_DIVERSITY) || 1 - ); - this.set("recentEmojis", keyValueStore.getObject(EMOJI_USAGE) || []); + this.setProperties({ + selectedDiversity: this.emojiStore.diversity, + recentEmojis: this.emojiStore.favorites + }); run.scheduleOnce("afterRender", this, function() { this._bindEvents(); @@ -88,18 +80,7 @@ export default Ember.Component.extend({ _setup() { this.$picker = $(this.element.querySelector(".emoji-picker")); this.$modal = $(this.element.querySelector(".emoji-picker-modal")); - this.appEvents.on("emoji-picker:close", this, "_closeEmojiPicker"); - - if (!keyValueStore.getObject(EMOJI_USAGE)) { - keyValueStore.setObject({ key: EMOJI_USAGE, value: [] }); - } else if (_.isPlainObject(keyValueStore.getObject(EMOJI_USAGE))) { - // handle legacy format - keyValueStore.setObject({ - key: EMOJI_USAGE, - value: _.keys(keyValueStore.getObject(EMOJI_USAGE)) - }); - } }, @on("didUpdateAttrs") @@ -116,10 +97,7 @@ export default Ember.Component.extend({ @observes("selectedDiversity") selectedDiversityChanged() { - keyValueStore.setObject({ - key: EMOJI_SELECTED_DIVERSITY, - value: this.selectedDiversity - }); + this.emojiStore.diversity = this.selectedDiversity; $.each( this.$list.find(".emoji[data-loaded='1'].diversity"), @@ -326,7 +304,7 @@ export default Ember.Component.extend({ ".section[data-section='recent'] .clear-recent" ); $recent.on("click", () => { - keyValueStore.setObject({ key: EMOJI_USAGE, value: [] }); + this.emojiStore.favorites = []; this.set("recentEmojis", []); this._scrollTo(0); return false; @@ -608,12 +586,8 @@ export default Ember.Component.extend({ }, _trackEmojiUsage(code) { - let recent = keyValueStore.getObject(EMOJI_USAGE) || []; - recent = recent.filter(r => r !== code); - recent.unshift(code); - recent.length = Math.min(recent.length, PER_ROW); - keyValueStore.setObject({ key: EMOJI_USAGE, value: recent }); - this.set("recentEmojis", recent); + this.emojiStore.track(code); + this.set("recentEmojis", this.emojiStore.favorites.slice(0, PER_ROW)); }, _scrollTo(y) { diff --git a/app/assets/javascripts/discourse/services/emoji-store.js.es6 b/app/assets/javascripts/discourse/services/emoji-store.js.es6 new file mode 100644 index 00000000000..7c7a5d082a7 --- /dev/null +++ b/app/assets/javascripts/discourse/services/emoji-store.js.es6 @@ -0,0 +1,48 @@ +import KeyValueStore from "discourse/lib/key-value-store"; + +const EMOJI_USAGE = "emojiUsage"; +const EMOJI_SELECTED_DIVERSITY = "emojiSelectedDiversity"; +const TRACKED_EMOJIS = 15; +const STORE_NAMESPACE = "discourse_emojis_"; + +export default Ember.Service.extend({ + init() { + this._super(...arguments); + + this.store = new KeyValueStore(STORE_NAMESPACE); + + if (!this.store.getObject(EMOJI_USAGE)) { + this.favorites = []; + } + }, + + get diversity() { + return this.store.getObject(EMOJI_SELECTED_DIVERSITY) || 1; + }, + + set diversity(value) { + this.store.setObject({ key: EMOJI_SELECTED_DIVERSITY, value: value || 1 }); + }, + + get favorites() { + return this.store.getObject(EMOJI_USAGE) || []; + }, + + set favorites(value) { + this.store.setObject({ key: EMOJI_USAGE, value: value || [] }); + }, + + track(code) { + const normalizedCode = code.replace(/(^:)|(:$)/g, ""); + const recent = this.favorites.filter(r => r !== normalizedCode); + recent.unshift(normalizedCode); + recent.length = Math.min(recent.length, TRACKED_EMOJIS); + this.favorites = recent; + }, + + reset() { + const store = new KeyValueStore(STORE_NAMESPACE); + store.setObject({ key: EMOJI_USAGE, value: [] }); + store.setObject({ key: EMOJI_SELECTED_DIVERSITY, value: 1 }); + } +}); diff --git a/test/javascripts/acceptance/emoji-picker-test.js.es6 b/test/javascripts/acceptance/emoji-picker-test.js.es6 index 96c5baf37e9..f6b4046eaa4 100644 --- a/test/javascripts/acceptance/emoji-picker-test.js.es6 +++ b/test/javascripts/acceptance/emoji-picker-test.js.es6 @@ -1,11 +1,11 @@ import { acceptance } from "helpers/qunit-helpers"; import { IMAGE_VERSION as v } from "pretty-text/emoji/version"; -import { resetCache } from "discourse/components/emoji-picker"; acceptance("EmojiPicker", { loggedIn: true, beforeEach() { - resetCache(); + const store = Discourse.__container__.lookup("service:emojis-store"); + store.reset(); } }); diff --git a/test/javascripts/lib/emoji-store-test.js.es6 b/test/javascripts/lib/emoji-store-test.js.es6 new file mode 100644 index 00000000000..f0c521a58f5 --- /dev/null +++ b/test/javascripts/lib/emoji-store-test.js.es6 @@ -0,0 +1,33 @@ +QUnit.module("lib:emoji-store", { + afterEach() { + const store = Discourse.__container__.lookup("service:emoji-store"); + store.reset(); + } +}); + +QUnit.test("defaults", assert => { + const store = Discourse.__container__.lookup("service:emoji-store"); + assert.deepEqual(store.favorites, []); + assert.equal(store.diversity, 1); +}); + +QUnit.test("diversity", assert => { + const store = Discourse.__container__.lookup("service:emoji-store"); + store.diversity = 2; + assert.equal(store.diversity, 2); +}); + +QUnit.test("favorites", assert => { + const store = Discourse.__container__.lookup("service:emoji-store"); + store.favorites = ["smile"]; + assert.deepEqual(store.favorites, ["smile"]); +}); + +QUnit.test("track", assert => { + const store = Discourse.__container__.lookup("service:emoji-store"); + store.track("woman:t4"); + assert.deepEqual(store.favorites, ["woman:t4"]); + store.track("otter"); + store.track(":otter:"); + assert.deepEqual(store.favorites, ["otter", "woman:t4"]); +});