diff --git a/assets/javascripts/discourse/components/babble-sidebar-component.js.es6 b/assets/javascripts/discourse/components/babble-sidebar-component.js.es6 index 9310261..e01574a 100644 --- a/assets/javascripts/discourse/components/babble-sidebar-component.js.es6 +++ b/assets/javascripts/discourse/components/babble-sidebar-component.js.es6 @@ -1,122 +1,151 @@ -import MountWidget from 'discourse/components/mount-widget' -import Babble from '../lib/babble' -import { on, observes } from 'discourse-common/utils/decorators' -import { debounce, throttle } from "@ember/runloop"; +import Babble from "../lib/babble"; +import MountWidget from "discourse/components/mount-widget"; +import { on, observes } from "discourse-common/utils/decorators"; +import debounce from "discourse/plugins/babble/lib/debounce"; +import { throttle } from "@ember/runloop"; -var whosOnline +var whosOnline; if (Discourse.SiteSettings.whos_online_enabled) { - whosOnline = Ember.inject.service('online-service') + whosOnline = Ember.inject.service("online-service"); } export default MountWidget.extend({ - widget: 'babble-sidebar', + widget: "babble-sidebar", availableTopics: [], availableUsers: [], whosOnline: whosOnline, buildArgs() { - if (Babble.disabled()) { return {} } - return { - topic: this.topic, - mobile: this.site.isMobileDevice, - initialized: this.initialized, - canInitialize: Discourse.SiteSettings.babble_enable_pms || Babble.summary.topicCount > 0, - availableTopics: Babble.availableTopics(), - availableUsers: Babble.availableUsers(), - visible: (this.initialized && this.visible), - csrf: this.session.get('csrfToken'), - isOnline: (userId) => { - if (!this.get('whosOnline')) { return } - return this.get('whosOnline').isUserOnline(userId) - } + if (Babble.disabled()) { + return {}; } + return { + topic: this.topic, + mobile: this.site.isMobileDevice, + initialized: this.initialized, + canInitialize: + Discourse.SiteSettings.babble_enable_pms || + Babble.summary.topicCount > 0, + availableTopics: Babble.availableTopics(), + availableUsers: Babble.availableUsers(), + visible: this.initialized && this.visible, + csrf: this.session.get("csrfToken"), + isOnline: (userId) => { + if (!this.get("whosOnline")) { + return; + } + return this.get("whosOnline").isUserOnline(userId); + }, + }; }, - @on('didInsertElement') + @on("didInsertElement") _initialize() { - if (Babble.disabled()) { return } + if (Babble.disabled()) { + return; + } - this.set('targetObject', this) + this.set("targetObject", this); - $(window).on('resize.babble-window-resize', () => debounce(this, this.triggerRerender, 250)) + $(window).on("resize.babble-window-resize", () => + debounce(this, this.triggerRerender, 250) + ); if (Discourse.SiteSettings.babble_adaptive_height) { - $(window).on('scroll.babble-scroll', () => throttle(this, this.triggerRerender, 250)) + $(window).on("scroll.babble-scroll", () => + throttle(this, this.triggerRerender, 250) + ); } this.appEvents.on("babble-toggle-chat", () => { - this.visible ? this.closeChat() : this.openChat() - }) + this.visible ? this.closeChat() : this.openChat(); + }); - this.appEvents.on('babble-rerender', () => { - this.dirtyKeys.forceAll() - this.rerenderWidget() - }) + this.appEvents.on("babble-rerender", () => { + this.dirtyKeys.forceAll(); + this.rerenderWidget(); + }); - this.appEvents.on('babble-has-topics', () => { - if (!Babble.openByDefault() || this.site.isMobileDevice) { return } - this.initialize() - }) + this.appEvents.on("babble-has-topics", () => { + if (!Babble.openByDefault() || this.site.isMobileDevice) { + return; + } + this.initialize(); + }); - Babble.subscribeToNotifications(this) - Babble.loadSummary(this) + Babble.subscribeToNotifications(this); + Babble.loadSummary(this); }, - @on('willDestroyElement') + @on("willDestroyElement") _teardown() { - $(window).off('resize.babble-window-resize') - $(window).off('scroll.babble-scroll') - this.appEvents.off('babble-toggle-chat') - this.appEvents.off('babble-rerender') + $(window).off("resize.babble-window-resize"); + $(window).off("scroll.babble-scroll"); + this.appEvents.off("babble-toggle-chat"); + this.appEvents.off("babble-rerender"); }, - @observes('visible') + @observes("visible") _updateOutletClass() { - const $outlet = $('#main-outlet') + const $outlet = $("#main-outlet"); if (this.visible) { - $outlet.addClass(`chat-active--${Discourse.SiteSettings.babble_position}`) + $outlet.addClass( + `chat-active--${Discourse.SiteSettings.babble_position}` + ); } else { - $outlet.removeClass(`chat-active--${Discourse.SiteSettings.babble_position}`) + $outlet.removeClass( + `chat-active--${Discourse.SiteSettings.babble_position}` + ); } - this.appEvents.trigger('babble-rerender') + this.appEvents.trigger("babble-rerender"); }, initialize(topic) { - if (this.initialized) { return } - this.set('initialized', true) + if (this.initialized) { + return; + } + this.set("initialized", true); - Babble.loadBoot(this).then(() => { this.openChat(topic) }) + Babble.loadBoot(this).then(() => { + this.openChat(topic); + }); }, - + triggerRerender() { - this.appEvents.trigger('babble-rerender'); + this.appEvents.trigger("babble-rerender"); }, openChat(topic) { - if (!this.initialized) { return this.initialize(topic) } + if (!this.initialized) { + return this.initialize(topic); + } if (!topic && Babble.singleChannel()) { - if (this.defaultLoaded) { return this.openChat(Babble.fetchDefault()) } + if (this.defaultLoaded) { + return this.openChat(Babble.fetchDefault()); + } Babble.loadTopic(Babble.summary.defaultId).then(() => { - this.set('defaultLoaded', true) - this.openChat() - }) + this.set("defaultLoaded", true); + this.openChat(); + }); } else { - if (this.visible) { this.closeChat() } - Babble.bind(this, topic) - this.set('topic', topic) - this.set('visible', true) + if (this.visible) { + this.closeChat(); + } + Babble.bind(this, topic); + this.set("topic", topic); + this.set("visible", true); } }, closeChat() { - this.set('visible', false) - Babble.unbind(this) + this.set("visible", false); + Babble.unbind(this); }, channelView() { - Babble.unbind(this) + Babble.unbind(this); }, -}) +}); diff --git a/assets/javascripts/discourse/lib/chat-element-utils.js.es6 b/assets/javascripts/discourse/lib/chat-element-utils.js.es6 index 1ceb75c..894782d 100644 --- a/assets/javascripts/discourse/lib/chat-element-utils.js.es6 +++ b/assets/javascripts/discourse/lib/chat-element-utils.js.es6 @@ -1,176 +1,235 @@ -import { forEachTopicContainer } from './chat-topic-iterators' -import userSearch from 'discourse/lib/user-search' -import { translations } from 'pretty-text/emoji/data' -import { emojiSearch } from 'pretty-text/emoji' -import { emojiUrlFor } from 'discourse/lib/text' -import { findRawTemplate } from 'discourse/lib/raw-templates' -import debounce from 'discourse/lib/debounce' -import autosize from 'discourse/lib/autosize' -import User from 'discourse/models/user' -import Site from 'discourse/models/site' -import Babble from '../lib/babble' -import { flatten } from './babble-utils'; - -let scrollToPost = function(topic, postNumber, speed = 400, offset = 60) { - Ember.run.scheduleOnce('afterRender', () => { - forEachTopicContainer(topic, function($container) { - if (!hasChatElements($container)) { return } - - let $scrollContainer = $container.find('.babble-list') - let $post = $container.find(`.babble-post[data-post-number=${postNumber}]`) - if (!$post.length || !$scrollContainer.length) { return } - - let postWidth = $post.find('.babble-post-content-wrapper').width() - $scrollContainer.find('.babble-post-content img[height]').toArray().map((img) => { - let fullHeight = parseInt(img.attributes.height.value) - let fullWidth = parseInt(img.attributes.width.value) - var viewHeight - var viewWidth - if (fullHeight <= postWidth && fullWidth <= postWidth) { - viewHeight = fullHeight - viewWidth = fullWidth - } else { - viewHeight = postWidth * fullHeight / fullWidth - viewWidth = postWidth - } - img.style.height = `${viewHeight}px` - img.style.width = `${viewWidth}px` - }) - - let animateTarget = $post.position().top + $scrollContainer.scrollTop() - offset - $scrollContainer.animate({ scrollTop: animateTarget }, speed) - }) - }) -} - -let setupScrollContainer = function(topic) { - forEachTopicContainer(topic, function($container) { - if (!hasChatElements($container)) { return } - - let $scrollContainer = $($container).find('.babble-list[scroll-container=inactive]') - if (!$scrollContainer.length) { console.warn("Babble scroll container already active or could not be found"); return } - - $($scrollContainer).on('scroll.discourse-babble-scroll', () => { - debounce(this, () => { - $container.find('.babble-post-actions-menu').hide() - Babble.ensureRead(topic, $container) - }, 500); +import { forEachTopicContainer } from "./chat-topic-iterators"; +import userSearch from "discourse/lib/user-search"; +import { translations } from "pretty-text/emoji/data"; +import { emojiSearch } from "pretty-text/emoji"; +import { emojiUrlFor } from "discourse/lib/text"; +import { findRawTemplate } from "discourse/lib/raw-templates"; +import debounce from "discourse/plugins/babble/lib/debounce"; +import autosize from "discourse/lib/autosize"; +import User from "discourse/models/user"; +import Site from "discourse/models/site"; +import Babble from "../lib/babble"; +import { flatten } from "./babble-utils"; + +let scrollToPost = function (topic, postNumber, speed = 400, offset = 60) { + Ember.run.scheduleOnce("afterRender", () => { + forEachTopicContainer(topic, function ($container) { + if (!hasChatElements($container)) { + return; + } + + let $scrollContainer = $container.find(".babble-list"); + let $post = $container.find( + `.babble-post[data-post-number=${postNumber}]` + ); + if (!$post.length || !$scrollContainer.length) { + return; + } + + let postWidth = $post.find(".babble-post-content-wrapper").width(); + $scrollContainer + .find(".babble-post-content img[height]") + .toArray() + .map((img) => { + let fullHeight = parseInt(img.attributes.height.value); + let fullWidth = parseInt(img.attributes.width.value); + var viewHeight; + var viewWidth; + if (fullHeight <= postWidth && fullWidth <= postWidth) { + viewHeight = fullHeight; + viewWidth = fullWidth; + } else { + viewHeight = (postWidth * fullHeight) / fullWidth; + viewWidth = postWidth; + } + img.style.height = `${viewHeight}px`; + img.style.width = `${viewWidth}px`; + }); + + let animateTarget = + $post.position().top + $scrollContainer.scrollTop() - offset; + $scrollContainer.animate({ scrollTop: animateTarget }, speed); }); - Babble.ensureRead(topic, $container) + }); +}; - // Mark scroll container as activated - $container.attr('scroll-container', 'active') - return $container - }) -} +let setupScrollContainer = function (topic) { + forEachTopicContainer(topic, function ($container) { + if (!hasChatElements($container)) { + return; + } + + let $scrollContainer = $($container).find( + ".babble-list[scroll-container=inactive]" + ); + if (!$scrollContainer.length) { + console.warn( + "Babble scroll container already active or could not be found" + ); + return; + } + + $($scrollContainer).on("scroll.discourse-babble-scroll", () => { + debounce( + this, + () => { + $container.find(".babble-post-actions-menu").hide(); + Babble.ensureRead(topic, $container); + }, + 500 + ); + }); + Babble.ensureRead(topic, $container); -let setupComposer = function(topic, opts = { emojis: true, mentions: true }) { - Ember.run.scheduleOnce('afterRender', () => { - forEachTopicContainer(topic, function($container) { - if (!hasChatElements($container)) { return } + // Mark scroll container as activated + $container.attr("scroll-container", "active"); + return $container; + }); +}; + +let setupComposer = function (topic, opts = { emojis: true, mentions: true }) { + Ember.run.scheduleOnce("afterRender", () => { + forEachTopicContainer(topic, function ($container) { + if (!hasChatElements($container)) { + return; + } - const $textarea = $($container).find('.babble-post-composer textarea[babble-composer=inactive]') - if (!$textarea.length) { console.warn("Babble composer already active or could not be found"); return } + const $textarea = $($container).find( + ".babble-post-composer textarea[babble-composer=inactive]" + ); + if (!$textarea.length) { + console.warn("Babble composer already active or could not be found"); + return; + } - autosize($textarea) + autosize($textarea); if (opts.emojis) { $textarea.autocomplete({ - template: findRawTemplate('emoji-selector-autocomplete'), + template: findRawTemplate("emoji-selector-autocomplete"), key: ":", transformComplete(v) { - if (!v.code) { return } - return `${v.code}:` + if (!v.code) { + return; + } + return `${v.code}:`; }, dataSource(term) { - return new Ember.RSVP.Promise(resolve => { - var options = (term === "" && ['smile', 'smiley', 'wink', 'sunny', 'blush']) || - translations[`:${term}`] || - emojiSearch(term, {maxResults: 5}) - return resolve(options) - }).then(list => flatten([list]).map(code => { - return {code, src: emojiUrlFor(code)}; - })) - } - }) + return new Ember.RSVP.Promise((resolve) => { + var options = + (term === "" && [ + "smile", + "smiley", + "wink", + "sunny", + "blush", + ]) || + translations[`:${term}`] || + emojiSearch(term, { maxResults: 5 }); + return resolve(options); + }).then((list) => + flatten([list]).map((code) => { + return { code, src: emojiUrlFor(code) }; + }) + ); + }, + }); } if (opts.mentions) { $textarea.autocomplete({ - template: findRawTemplate('user-selector-autocomplete'), - key: '@', + template: findRawTemplate("user-selector-autocomplete"), + key: "@", dataSource(term) { return userSearch({ term: term, topicId: topic.id, includeGroups: true, - exclude: [User.currentProp('username')] - }) + exclude: [User.currentProp("username")], + }); }, transformComplete(v) { - return v.username || v.name - } - }) + return v.username || v.name; + }, + }); - $textarea.attr('babble-composer', 'active') + $textarea.attr("babble-composer", "active"); if (!Site.current().isMobileDevice) { - $textarea.focus() + $textarea.focus(); } } - }) - }) -} + }); + }); +}; -let teardownComposer = function(topic) { - forEachTopicContainer(topic, function($container) { - if (!hasChatElements($container)) { return } - let $composer = $($container).find('.babble-post-composer textarea[babble-composer=active]')[0] +let teardownComposer = function (topic) { + forEachTopicContainer(topic, function ($container) { + if (!hasChatElements($container)) { + return; + } + let $composer = $($container).find( + ".babble-post-composer textarea[babble-composer=active]" + )[0]; - let event = document.createEvent('Event') - event.initEvent('autosize:update', true, false) - $composer.dispatchEvent(event) - }) -} + let event = document.createEvent("Event"); + event.initEvent("autosize:update", true, false); + $composer.dispatchEvent(event); + }); +}; // A component has chat elements if it renders a 'babble-chat' widget. // This won't be the case for navbar counters or other unread tracking components. -let hasChatElements = function(element) { - return $(element).find('.babble-chat').length -} - -let positionDropdown = function(e, menuSelector, dropdownWidth = 150, delay = 100) { - const rect = document.elementFromPoint(e.clientX, e.clientY).closest('.btn').getBoundingClientRect() +let hasChatElements = function (element) { + return $(element).find(".babble-chat").length; +}; + +let positionDropdown = function ( + e, + menuSelector, + dropdownWidth = 150, + delay = 100 +) { + const rect = document + .elementFromPoint(e.clientX, e.clientY) + .closest(".btn") + .getBoundingClientRect(); setTimeout(() => { - const menu = document.querySelector(menuSelector) - menu.style.top = `${rect.top}px` + const menu = document.querySelector(menuSelector); + menu.style.top = `${rect.top}px`; if (document.body.offsetWidth > rect.left + dropdownWidth) { - menu.style.left = `${rect.left}px` + menu.style.left = `${rect.left}px`; } else { - menu.style.right = `${document.body.offsetWidth - rect.right}px` + menu.style.right = `${document.body.offsetWidth - rect.right}px`; } - }, delay) -} + }, delay); +}; -let setupChannelAutocomplete = function(opts = {}) { +let setupChannelAutocomplete = function (opts = {}) { setTimeout(() => { - const $input = $(document.querySelector(`.babble-${opts.type}-autocomplete input`)) + const $input = $( + document.querySelector(`.babble-${opts.type}-autocomplete input`) + ); $input.autocomplete({ - template: findRawTemplate(opts.template), - onChangeItems: (items) => { opts.onSelect(items[0]) }, - dataSource: opts.source - }) - $input.focus() - }, 100) -} - -let playNotification = function() { - const $audio = $('audio#babble-notification')[0] - if (!$audio || !$audio.play) { return } - $audio.play() -} + template: findRawTemplate(opts.template), + onChangeItems: (items) => { + opts.onSelect(items[0]); + }, + dataSource: opts.source, + }); + $input.focus(); + }, 100); +}; + +let playNotification = function () { + const $audio = $("audio#babble-notification")[0]; + if (!$audio || !$audio.play) { + return; + } + $audio.play(); +}; export { scrollToPost, @@ -180,5 +239,5 @@ export { hasChatElements, positionDropdown, setupChannelAutocomplete, - playNotification -} + playNotification, +}; diff --git a/assets/javascripts/lib/debounce.js.es6 b/assets/javascripts/lib/debounce.js.es6 new file mode 100644 index 0000000..328848d --- /dev/null +++ b/assets/javascripts/lib/debounce.js.es6 @@ -0,0 +1,6 @@ +import discourseDebounce from "discourse-common/lib/debounce"; +import { debounce } from "@ember/runloop"; + +// TODO: Remove this file and use discouseDebounce after the 2.7 release. +const debounceFunction = discourseDebounce || debounce; +export default debounceFunction;