From 7c9fb5d3fca8c00d712dd1fa74325e57884ce6bc Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Wed, 11 Mar 2015 13:38:36 -0400 Subject: [PATCH] Refactored SiteMap/Header to support more dynamic flag counts Also fixes deprecation in `plugin-outlet` --- .../discourse/controllers/header.js.es6 | 47 +++++++++++--- .../discourse/controllers/site-map.js.es6 | 3 +- .../discourse/helpers/plugin-outlet.js.es6 | 44 ++++++------- .../discourse/lib/plugin-api.js.es6 | 6 +- .../discourse/templates/header.hbs | 6 +- .../discourse/templates/site-map.hbs | 6 +- .../javascripts/discourse/views/header.js.es6 | 63 ++++++++----------- .../controllers/site-map-test.js.es6 | 12 +--- 8 files changed, 98 insertions(+), 89 deletions(-) diff --git a/app/assets/javascripts/discourse/controllers/header.js.es6 b/app/assets/javascripts/discourse/controllers/header.js.es6 index b40ac3efdad..74e2e133ce5 100644 --- a/app/assets/javascripts/discourse/controllers/header.js.es6 +++ b/app/assets/javascripts/discourse/controllers/header.js.es6 @@ -1,6 +1,6 @@ import DiscourseController from 'discourse/controllers/controller'; -export default DiscourseController.extend({ +const HeaderController = DiscourseController.extend({ topic: null, showExtraInfo: null, notifications: null, @@ -18,9 +18,9 @@ export default DiscourseController.extend({ return Discourse.User.current() && !this.get('topic.isPrivateMessage'); }.property('topic.isPrivateMessage'), - _resetCachedNotifications: function(){ + _resetCachedNotifications: function() { // a bit hacky, but if we have no focus, hide notifications first - var visible = $("#notifications-dropdown").is(":visible"); + const visible = $("#notifications-dropdown").is(":visible"); if(!Discourse.get("hasFocus")) { if(visible){ @@ -37,7 +37,7 @@ export default DiscourseController.extend({ }.observes("currentUser.lastNotificationChange"), refreshNotifications: function(){ - var self = this; + const self = this; if (self.get("loadingNotifications")) { return; } self.set("loadingNotifications", true); @@ -56,14 +56,14 @@ export default DiscourseController.extend({ }, actions: { - toggleStar: function() { - var topic = this.get('topic'); + toggleStar() { + const topic = this.get('topic'); if (topic) topic.toggleStar(); return false; }, - showNotifications: function(headerView) { - var self = this; + showNotifications(headerView) { + const self = this; if (self.get('currentUser.unread_notifications') || self.get('currentUser.unread_private_messages') || !self.get('notifications')) { self.refreshNotifications(); @@ -71,5 +71,34 @@ export default DiscourseController.extend({ headerView.showDropdownBySelector("#user-notifications"); } } - }); + +// Allow plugins to add to the sum of "flags" above the site map +const _flagProperties = []; +function addFlagProperty(prop) { + _flagProperties.pushObject(prop); +} + +let _appliedFlagProps = false; +HeaderController.reopenClass({ + create() { + // We only want to change the class the first time it's created + if (!_appliedFlagProps && _flagProperties.length) { + _appliedFlagProps = true; + + const args = _flagProperties.slice(); + args.push(function() { + let sum = 0; + _flagProperties.forEach((fp) => sum += (this.get(fp) || 0)); + return sum; + }); + HeaderController.reopen({ flaggedPostsCount: Ember.computed.apply(this, args) }); + } + return this._super.apply(this, Array.prototype.slice.call(arguments)); + } +}); + +addFlagProperty('currentUser.site_flagged_posts_count'); + +export { addFlagProperty }; +export default HeaderController; diff --git a/app/assets/javascripts/discourse/controllers/site-map.js.es6 b/app/assets/javascripts/discourse/controllers/site-map.js.es6 index 9ee464c789c..6ae95cda30e 100644 --- a/app/assets/javascripts/discourse/controllers/site-map.js.es6 +++ b/app/assets/javascripts/discourse/controllers/site-map.js.es6 @@ -1,9 +1,8 @@ export default Ember.ArrayController.extend({ - needs: ['application'], + needs: ['application', 'header'], showBadgesLink: function(){return Discourse.SiteSettings.enable_badges;}.property(), showAdminLinks: Em.computed.alias('currentUser.staff'), - flaggedPostsCount: Em.computed.alias("currentUser.site_flagged_posts_count"), faqUrl: function() { return Discourse.SiteSettings.faq_url ? Discourse.SiteSettings.faq_url : Discourse.getURL('/faq'); diff --git a/app/assets/javascripts/discourse/helpers/plugin-outlet.js.es6 b/app/assets/javascripts/discourse/helpers/plugin-outlet.js.es6 index a209513aefd..c4d0e04c801 100644 --- a/app/assets/javascripts/discourse/helpers/plugin-outlet.js.es6 +++ b/app/assets/javascripts/discourse/helpers/plugin-outlet.js.es6 @@ -47,25 +47,24 @@ **/ -var _connectorCache; +let _connectorCache; function findOutlets(collection, callback) { - var disabledPlugins = Discourse.Site.currentProp('disabled_plugins') || []; + const disabledPlugins = Discourse.Site.currentProp('disabled_plugins') || []; Ember.keys(collection).forEach(function(res) { if (res.indexOf("/connectors/") !== -1) { // Skip any disabled plugins - for (var i=0; i 1) { - viewClass = Ember.ContainerView.extend({ - childViews: childViews - }); - } else { - viewClass = childViews[0]; - } + const viewClass = (childViews.length > 1) ? Ember.ContainerView : childViews[0]; delete options.fn; // we don't need the default template since we have a connector - return Ember.Handlebars.helpers.view.call(this, viewClass, options); + Ember.Handlebars.helpers.view.call(this, viewClass, options); + + const cvs = options.data.view._childViews; + if (childViews.length > 1 && cvs && cvs.length) { + const inserted = cvs[cvs.length-1]; + if (inserted) { + childViews.forEach(function(cv) { + inserted.pushObject(cv.create()); + }); + } + } } else if (options.fn) { // If a block is passed, render its content. return Ember.Handlebars.helpers.view.call(this, diff --git a/app/assets/javascripts/discourse/lib/plugin-api.js.es6 b/app/assets/javascripts/discourse/lib/plugin-api.js.es6 index 32a9965a6fb..621c4bdefe6 100644 --- a/app/assets/javascripts/discourse/lib/plugin-api.js.es6 +++ b/app/assets/javascripts/discourse/lib/plugin-api.js.es6 @@ -1,13 +1,13 @@ -var _decorateId = 0; +let _decorateId = 0; function decorate(klass, evt, cb) { - var mixin = {}; + const mixin = {}; mixin["_decorate_" + (_decorateId++)] = function($elem) { cb($elem); }.on(evt); klass.reopen(mixin); } export function decorateCooked(container, cb) { - var postView = container.lookupFactory('view:post'); + const postView = container.lookupFactory('view:post'); decorate(postView, 'postViewInserted', cb); decorate(postView, 'postViewUpdated', cb); decorate(container.lookupFactory('view:composer'), 'previewRefreshed', cb); diff --git a/app/assets/javascripts/discourse/templates/header.hbs b/app/assets/javascripts/discourse/templates/header.hbs index 0299c69e931..02748e28da7 100644 --- a/app/assets/javascripts/discourse/templates/header.hbs +++ b/app/assets/javascripts/discourse/templates/header.hbs @@ -52,14 +52,14 @@ {{fa-icon "bars" label="site_map"}} {{/if}} - {{#if currentUser.site_flagged_posts_count}} - {{currentUser.site_flagged_posts_count}} + {{#if flaggedPostsCount}} + {{flaggedPostsCount}} {{/if}} {{#if currentUser}} diff --git a/app/assets/javascripts/discourse/templates/site-map.hbs b/app/assets/javascripts/discourse/templates/site-map.hbs index a1efed37ab2..34660f8f636 100644 --- a/app/assets/javascripts/discourse/templates/site-map.hbs +++ b/app/assets/javascripts/discourse/templates/site-map.hbs @@ -6,9 +6,9 @@
  • - {{i18n 'flags_title'}} - {{#if flaggedPostsCount}} - {{flaggedPostsCount}} + {{fa-icon "flag"}} {{i18n 'flags_title'}} + {{#if currentUser.site_flagged_posts_count}} + {{currentUser.site_flagged_posts_count}} {{/if}}
  • diff --git a/app/assets/javascripts/discourse/views/header.js.es6 b/app/assets/javascripts/discourse/views/header.js.es6 index f256b802eff..0271f6a501d 100644 --- a/app/assets/javascripts/discourse/views/header.js.es6 +++ b/app/assets/javascripts/discourse/views/header.js.es6 @@ -1,13 +1,4 @@ -/** - This view handles rendering of the header of the site - - @class HeaderView - @extends Discourse.View - @namespace Discourse - @module Discourse -**/ - -var originalZIndex; +let originalZIndex; export default Discourse.View.extend({ tagName: 'header', @@ -19,7 +10,7 @@ export default Discourse.View.extend({ showDropdown: function($target) { var self = this; - if(!this.get("renderDropdowns")){ + if (!this.get("renderDropdowns")) { this.set("renderDropdowns", true); Em.run.next(function(){ self.showDropdown($target); @@ -27,30 +18,29 @@ export default Discourse.View.extend({ return; } - var elementId = $target.data('dropdown') || $target.data('notifications'), - $dropdown = $("#" + elementId), - $li = $target.closest('li'), - $ul = $target.closest('ul'), - $html = $('html'), - $header = $('header'), - replyZIndex = parseInt($('#reply-control').css('z-index'), 10); - + const elementId = $target.data('dropdown') || $target.data('notifications'), + $dropdown = $("#" + elementId), + $li = $target.closest('li'), + $ul = $target.closest('ul'), + $html = $('html'), + $header = $('header'), + replyZIndex = parseInt($('#reply-control').css('z-index'), 10); originalZIndex = originalZIndex || $('header').css('z-index'); - if(replyZIndex > 0) { + if (replyZIndex > 0) { $header.css("z-index", replyZIndex + 1); } - var controller = self.get('controller'); - if(controller && !controller.isDestroyed){ + const controller = self.get('controller'); + if (controller && !controller.isDestroyed) { controller.set('visibleDropdown', elementId); } // we need to ensure we are rendered, // this optimises the speed of the initial render - var render = $target.data('render'); - if(render){ - if(!this.get(render)){ + const render = $target.data('render'); + if (render){ + if (!this.get(render)){ this.set(render, true); Em.run.next(this, function(){ this.showDropdown.apply(self, [$target]); @@ -59,20 +49,21 @@ export default Discourse.View.extend({ } } - var hideDropdown = function() { + const hideDropdown = function() { $header.css("z-index", originalZIndex); $dropdown.fadeOut('fast'); $li.removeClass('active'); $html.data('hide-dropdown', null); - var controller = self.get('controller'); - if(controller && !controller.isDestroyed){ + + const controller = self.get('controller'); + if (controller && !controller.isDestroyed){ controller.set('visibleDropdown', null); } - return $html.off('click.d-dropdown'); + $html.off('click.d-dropdown'); }; // if a dropdown is active and the user clicks on it, close it - if($li.hasClass('active')) { return hideDropdown(); } + if ($li.hasClass('active')) { return hideDropdown(); } // otherwhise, mark it as active $li.addClass('active'); // hide the other dropdowns @@ -129,16 +120,16 @@ export default Discourse.View.extend({ }, - willDestroyElement: function() { + _tearDown: function() { $(window).unbind('scroll.discourse-dock'); $(document).unbind('touchmove.discourse-dock'); this.$('a.unread-private-messages, a.unread-notifications, a[data-notifications]').off('click.notifications'); this.$('a[data-dropdown]').off('click.dropdown'); - }, + }.on('willDestroyElement'), - didInsertElement: function() { + _setup: function() { - var self = this; + const self = this; this.$('a[data-dropdown]').on('click.dropdown', function(e) { self.showDropdown.apply(self, [$(e.currentTarget)]); @@ -172,7 +163,5 @@ export default Discourse.View.extend({ } } }); - } + }.on('didInsertElement') }); - - diff --git a/test/javascripts/controllers/site-map-test.js.es6 b/test/javascripts/controllers/site-map-test.js.es6 index f328d421379..419ed58ccc3 100644 --- a/test/javascripts/controllers/site-map-test.js.es6 +++ b/test/javascripts/controllers/site-map-test.js.es6 @@ -1,7 +1,7 @@ var oldMobileView; moduleFor("controller:site-map", "controller:site-map", { - needs: ['controller:application'], + needs: ['controller:application', 'controller:header'], setup: function() { oldMobileView = Discourse.Mobile.mobileView; @@ -21,16 +21,6 @@ test("showAdminLinks", function() { equal(controller.get("showAdminLinks"), false, "is false when current user is not a staff member"); }); -test("flaggedPostsCount", function() { - const currentUser = Ember.Object.create({ site_flagged_posts_count: 5 }); - const controller = this.subject({ currentUser }); - - equal(controller.get("flaggedPostsCount"), 5, "returns current user's flagged posts count"); - - currentUser.set("site_flagged_posts_count", 0); - equal(controller.get("flaggedPostsCount"), 0, "is bound (reacts to change of current user's flagged posts count)"); -}); - test("faqUrl returns faq url configured in site settings if it is set", function() { Discourse.SiteSettings.faq_url = "faq-url"; var controller = this.subject();