From 995d253030a2ed7d1e1c45bb85b31113c39436c2 Mon Sep 17 00:00:00 2001 From: Joffrey JAFFEUX Date: Mon, 24 Jul 2017 12:41:39 +0200 Subject: [PATCH] FIX: improves efficiency --- .../discourse/components/emoji-picker.js.es6 | 140 ++++++++---------- 1 file changed, 63 insertions(+), 77 deletions(-) diff --git a/app/assets/javascripts/discourse/components/emoji-picker.js.es6 b/app/assets/javascripts/discourse/components/emoji-picker.js.es6 index ad2e703b8eb..bd745becd65 100644 --- a/app/assets/javascripts/discourse/components/emoji-picker.js.es6 +++ b/app/assets/javascripts/discourse/components/emoji-picker.js.es6 @@ -8,19 +8,17 @@ import { extendedEmojiList, isSkinTonableEmoji } from "pretty-text/emoji"; const keyValueStore = new KeyValueStore("discourse_emojis_"); const EMOJI_USAGE = "emojiUsage"; const EMOJI_SELECTED_DIVERSITY = "emojiSelectedDiversity"; -const EMOJI_CACHED_SECTIONS = "emojiCachedSections"; const PER_ROW = 11; const customEmojis = _.map(_.keys(extendedEmojiList()), code => { return { code, src: emojiUrlFor(code) }; }); export function resetCache() { - keyValueStore.setObject({ key: EMOJI_CACHED_SECTIONS, value: [] }); keyValueStore.setObject({ key: EMOJI_USAGE, value: [] }); keyValueStore.setObject({ key: EMOJI_SELECTED_DIVERSITY, value: 1 }); } -let $picker, $filter, $results, $list, scrollPosition; +let $picker, $filter, $results, $list, scrollPosition, $visibleSections; export default Ember.Component.extend({ willDestroyElement() { @@ -30,12 +28,6 @@ export default Ember.Component.extend({ this.appEvents.off("emoji-picker:close"); }, - didDestroyElement() { - this._super(); - - $picker = null; - }, - didInsertElement() { this._super(); @@ -43,10 +35,6 @@ export default Ember.Component.extend({ $picker = this.$(".emoji-picker"); - if (!keyValueStore.getObject(EMOJI_CACHED_SECTIONS)) { - keyValueStore.setObject({ key: EMOJI_CACHED_SECTIONS, value: [] }); - } - if (!keyValueStore.getObject(EMOJI_USAGE)) { keyValueStore.setObject({ key: EMOJI_USAGE, value: [] }); } else if(_.isPlainObject(keyValueStore.getObject(EMOJI_USAGE))) { @@ -77,20 +65,28 @@ export default Ember.Component.extend({ selectedDiversityChanged() { keyValueStore.setObject({key: EMOJI_SELECTED_DIVERSITY, value: this.get("selectedDiversity")}); - $.each($list.find(".emoji.diversity"), (_, button) => this._setButtonBackground(button, true) ); + $.each($list.find(".emoji[data-loaded='1'].diversity"), (_, button) => this._setButtonBackground(button, true) ); if(this.get("filter") !== "") { $.each($results.find(".emoji.diversity"), (_, button) => this._setButtonBackground(button, true) ); } + + $picker + .find(".diversity-picker .diversity-scale") + .removeClass("selected"); + + $picker + .find(`.diversity-picker .diversity-scale[data-level="${this.get("selectedDiversity")}"]`) + .addClass("selected"); }, @observes("recentEmojis") recentEmojisChanged() { - const previousScrollTop = $list.scrollTop(); + const previousScrollTop = scrollPosition; const $recentSection = $list.find(".section[data-section='recent']"); const $recentSectionGroup = $recentSection.find(".section-group"); const $recentCategory = $picker.find(".category-icon button[data-section='recent']").parent(); - let persistScrollPosition = !$recentCategory.is(':visible') ? true : false; + let persistScrollPosition = !$recentCategory.is(":visible") ? true : false; // we set height to 0 to avoid it being taken into account for scroll position if(_.isEmpty(this.get("recentEmojis"))) { @@ -122,10 +118,12 @@ export default Ember.Component.extend({ this.$().find(".emoji-picker-modal").remove(); this._unbindEvents(); + + this._unbindSectionLoadingCheck(); }, show() { - const template = findRawTemplate("emoji-picker")({ customEmojis }); + const template = findRawTemplate("emoji-picker")({customEmojis}); $picker.html(template); this.$().append("
"); @@ -140,12 +138,24 @@ export default Ember.Component.extend({ Ember.run.scheduleOnce("afterRender", this, function() { this._loadCategoriesEmojis(); - this._setDiversity(); this._positionPicker(); this._scrollTo(); + this._sectionLoadingCheck(); }); }, + _unbindSectionLoadingCheck() { + Ember.run.cancel(this.get("nextSectionloadingCheck")); + }, + + _sectionLoadingCheck() { + const nextSectionloadingCheck = Ember.run.later(this, function() { + Ember.run.throttle(this, this._checkVisibleSection, 100); + this._sectionLoadingCheck(); + }, 500); + this.set("nextSectionloadingCheck", nextSectionloadingCheck); + }, + _loadCategoriesEmojis() { $.each($picker.find(".categories-column button.emoji"), (_, button) => { const $button = $(button); @@ -313,8 +323,8 @@ export default Ember.Component.extend({ _bindSectionsScroll() { $list.on("scroll", () => { - Ember.run.debounce(this, this._checkVisibleSection, 150); scrollPosition = $list.scrollTop(); + Ember.run.throttle(this, this._checkVisibleSection, 150); }); }, @@ -325,68 +335,60 @@ export default Ember.Component.extend({ } const $sections = $list.find(".section"); - const sections = []; - let cumulatedHeight = 0; + const listHeight = $list.innerHeight(); + let $selectedSection; - $.each($sections, (_, section) => { + $visibleSections = _.filter($sections, section => { const $section = $(section); - sections.push({$section, cumulatedHeight}); - cumulatedHeight += $section.innerHeight(); + const sectionTop = $section.position().top; + return sectionTop + $section.height() > 0 && sectionTop < listHeight; }); - let selectedSection; - const currentScrollTop = $list.scrollTop(); - if (!_.isEmpty(this.get("recentEmojis")) && currentScrollTop === 0) { - selectedSection = _.first(sections); - } else if (!_.isEmpty(customEmojis) && - currentScrollTop === $list[0].scrollHeight - $list.innerHeight()) - { - selectedSection = _.last(sections); + if (!_.isEmpty(this.get("recentEmojis")) && scrollPosition === 0) { + $selectedSection = $(_.first($visibleSections)); } else { - selectedSection = _.last(_.reject(sections, (section) => { - return section.cumulatedHeight > currentScrollTop; - })); + $selectedSection = $(_.last($visibleSections)); } - if(selectedSection) { - const sectionTitle = selectedSection.$section.data("section"); + if($selectedSection) { $picker.find(".category-icon").removeClass("current"); - $picker.find(`.category-icon button[data-section='${sectionTitle}']`) + $picker.find(`.category-icon button[data-section='${$selectedSection.data("section")}']`) .parent() .addClass("current"); - if(!selectedSection.$section.hasClass("loaded")) { - selectedSection.$section.addClass("loaded"); - this._loadSection(selectedSection.$section); - } - - //preload surrounding sections - const selectedSectionIndex = sections.indexOf(selectedSection); - const preloadedSection = sections[selectedSectionIndex + 1] || sections[selectedSectionIndex - 1]; - if(preloadedSection && !preloadedSection.$section.hasClass("loaded")) { - preloadedSection.$section.addClass("loaded"); - this._loadSection(preloadedSection.$section); - } + this._loadVisibleSections(); } }, + _loadVisibleSections() { + if(!$visibleSections) { + return; + } + + const listHeight = $list.innerHeight(); + $visibleSections.forEach(visibleSection => { + const $unloadedEmojis = $(visibleSection).find("button.emoji[data-loaded!='1']"); + $.each($unloadedEmojis, (_, button) => { + const $button = $(button); + const buttonTop = $button.position().top; + const buttonHeight = $button.height(); + + if(buttonTop + buttonHeight > 0 && buttonTop - buttonHeight < listHeight) { + this._setButtonBackground($button); + } + }); + }); + }, + _bindDiversityClick() { const $diversityScales = $picker.find(".diversity-picker .diversity-scale"); $diversityScales.on("click", (event) => { const $selectedDiversity = $(event.currentTarget); - $diversityScales.removeClass("selected"); - $selectedDiversity.addClass("selected"); this.set("selectedDiversity", parseInt($selectedDiversity.data("level"))); return false; }); }, - _setDiversity() { - $picker - .find(`.diversity-picker .diversity-scale[data-level="${this.get("selectedDiversity")}"]`) - .addClass("selected"); - }, - _isReplyControlExpanded() { const verticalSpace = this.$(window).height() - Ember.$(".d-header").height() - @@ -490,24 +492,6 @@ export default Ember.Component.extend({ $picker.find(".info").css("max-width", infoMaxWidth); }, - _loadSection($section) { - const sectionName = $section.data("section"); - if(keyValueStore.getObject(EMOJI_CACHED_SECTIONS).indexOf(sectionName) > -1) { - $.each($section.find(".emoji"), (_, button) => this._setButtonBackground(button) ); - } else { - Ember.run.later( - this, () => { - keyValueStore.setObject({ - key: EMOJI_CACHED_SECTIONS, - value: keyValueStore.getObject(EMOJI_CACHED_SECTIONS).concat(sectionName) - }); - $.each($section.find(".emoji"), (_, button) => this._setButtonBackground(button) ); - }, - 1500 - ); - } - }, - _codeWithDiversity(code, diversity) { if(diversity && this.get("selectedDiversity") !== 1) { return `${code}:t${this.get("selectedDiversity")}`; @@ -553,6 +537,8 @@ export default Ember.Component.extend({ $button.css("background-image", ""); } - $button.css("background-image", `url("${emojiUrlFor(code)}")`); + $button + .attr("data-loaded", 1) + .css("background-image", `url("${emojiUrlFor(code)}")`); }, });