From 5fd3f2547c91d0d74b91c6afab3274326bb13c96 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Mon, 8 Dec 2014 12:59:04 -0500 Subject: [PATCH] FIX: Fix rerendering issues on some components. --- .../discourse-action-history.js.es6 | 17 +++---- .../discourse/components/global-notice.js.es6 | 7 +-- .../components/navigation-item.js.es6 | 14 ++---- .../discourse/components/post-gutter.js.es6 | 12 ++--- .../discourse/components/top-title.js.es6 | 10 +++-- .../components/topic-post-badges.js.es6 | 7 +-- .../discourse/components/topic-status.js.es6 | 20 +++------ .../discourse/helpers/i18n_helpers.js | 7 ++- .../discourse/views/activity-filter.js.es6 | 8 ++-- .../javascripts/discourse/views/button.js.es6 | 6 ++- .../discourse/views/dropdown-button.js.es6 | 44 ++++++++++--------- .../discourse/views/input-tip.js.es6 | 14 ++---- .../discourse/views/post-menu.js.es6 | 10 +++-- .../discourse/views/star-button.js.es6 | 2 +- .../discourse/views/topic-closing.js.es6 | 21 ++++----- .../views/topic-map-container.js.es6 | 5 ++- .../javascripts/discourse/views/view.js | 10 +---- test/javascripts/views/view-test.js.es6 | 44 ------------------- 18 files changed, 96 insertions(+), 162 deletions(-) diff --git a/app/assets/javascripts/discourse/components/discourse-action-history.js.es6 b/app/assets/javascripts/discourse/components/discourse-action-history.js.es6 index d941bca62..7dba3c582 100644 --- a/app/assets/javascripts/discourse/components/discourse-action-history.js.es6 +++ b/app/assets/javascripts/discourse/components/discourse-action-history.js.es6 @@ -1,23 +1,16 @@ -/** - This component handles rendering of what actions have been taken on a post. It uses - buffer rendering for performance rather than a template. +import StringBuffer from 'discourse/mixins/string-buffer'; - @class ActionsHistoryComponent - @extends Em.Component - @namespace Discourse - @module Discourse -**/ -export default Em.Component.extend({ +export default Em.Component.extend(StringBuffer, { tagName: 'section', classNameBindings: [':post-actions', 'hidden'], actionsHistory: Em.computed.alias('post.actionsHistory'), emptyHistory: Em.computed.empty('actionsHistory'), hidden: Em.computed.and('emptyHistory', 'post.notDeleted'), - shouldRerender: Discourse.View.renderIfChanged('actionsHistory.@each', 'actionsHistory.users.length', 'post.deleted'), + + rerenderTriggers: ['actionsHistory.@each', 'actionsHistory.users.length', 'post.deleted'], // This was creating way too many bound ifs and subviews in the handlebars version. - render: function(buffer) { - + renderString: function(buffer) { if (!this.get('emptyHistory')) { this.get('actionsHistory').forEach(function(c) { buffer.push("
"); diff --git a/app/assets/javascripts/discourse/components/global-notice.js.es6 b/app/assets/javascripts/discourse/components/global-notice.js.es6 index 7dca11a0c..0f6f60f67 100644 --- a/app/assets/javascripts/discourse/components/global-notice.js.es6 +++ b/app/assets/javascripts/discourse/components/global-notice.js.es6 @@ -1,8 +1,9 @@ -export default Ember.Component.extend({ +import StringBuffer from 'discourse/mixins/string-buffer'; - shouldRerender: Discourse.View.renderIfChanged("site.isReadOnly"), +export default Ember.Component.extend(StringBuffer, { + rerenderTriggers: ['site.isReadOnly'], - render: function(buffer) { + renderString: function(buffer) { var notices = []; if (this.site.get("isReadOnly")) { diff --git a/app/assets/javascripts/discourse/components/navigation-item.js.es6 b/app/assets/javascripts/discourse/components/navigation-item.js.es6 index ff5b01acd..e66fdd276 100644 --- a/app/assets/javascripts/discourse/components/navigation-item.js.es6 +++ b/app/assets/javascripts/discourse/components/navigation-item.js.es6 @@ -1,17 +1,11 @@ -/** - This view handles rendering of a navigation item +import StringBuffer from 'discourse/mixins/string-buffer'; - @class NavigationItemComponent - @extends Ember.Component - @namespace Discourse - @module Discourse -**/ -export default Ember.Component.extend({ +export default Ember.Component.extend(StringBuffer, { tagName: 'li', classNameBindings: ['active', 'content.hasIcon:has-icon'], attributeBindings: ['title'], hidden: Em.computed.not('content.visible'), - shouldRerender: Discourse.View.renderIfChanged('content.count'), + rerenderTriggers: ['content.count'], title: function() { var categoryName = this.get('content.categoryName'), @@ -42,7 +36,7 @@ export default Ember.Component.extend({ return I18n.t("filters." + name + ".title", extra); }.property('content.count'), - render: function(buffer) { + renderString: function(buffer) { var content = this.get('content'); buffer.push(""); if (content.get('hasIcon')) { diff --git a/app/assets/javascripts/discourse/components/post-gutter.js.es6 b/app/assets/javascripts/discourse/components/post-gutter.js.es6 index 6f263277d..925434b50 100644 --- a/app/assets/javascripts/discourse/components/post-gutter.js.es6 +++ b/app/assets/javascripts/discourse/components/post-gutter.js.es6 @@ -1,8 +1,12 @@ var MAX_SHOWN = 5; -export default Em.Component.extend({ +import StringBuffer from 'discourse/mixins/string-buffer'; + +export default Em.Component.extend(StringBuffer, { classNameBindings: [':gutter'], + rerenderTriggers: ['expanded'], + // Roll up links to avoid duplicates collapsed: function() { var seen = {}, @@ -21,7 +25,7 @@ export default Em.Component.extend({ return result; }.property('links'), - render: function(buffer) { + renderString: function(buffer) { var links = this.get('collapsed'), toRender = links, collapsed = !this.get('expanded'); @@ -62,10 +66,6 @@ export default Em.Component.extend({ } }, - _rerenderIfNeeded: function() { - this.rerender(); - }.observes('expanded'), - click: function(e) { var $target = $(e.target); if ($target.hasClass('toggle-more')) { diff --git a/app/assets/javascripts/discourse/components/top-title.js.es6 b/app/assets/javascripts/discourse/components/top-title.js.es6 index 31c03461d..cfa065429 100644 --- a/app/assets/javascripts/discourse/components/top-title.js.es6 +++ b/app/assets/javascripts/discourse/components/top-title.js.es6 @@ -1,8 +1,10 @@ -export default Ember.Component.extend({ - tagName: 'h2', +import StringBuffer from 'discourse/mixins/string-buffer'; - _shouldRerender: Discourse.View.renderIfChanged('period.title'), - render: function(buffer) { +export default Ember.Component.extend(StringBuffer, { + tagName: 'h2', + rerenderTriggers: ['period.title'], + + renderString: function(buffer) { buffer.push(" " + this.get('period.title')); } }); diff --git a/app/assets/javascripts/discourse/components/topic-post-badges.js.es6 b/app/assets/javascripts/discourse/components/topic-post-badges.js.es6 index cf21baa2a..ff9b0131e 100644 --- a/app/assets/javascripts/discourse/components/topic-post-badges.js.es6 +++ b/app/assets/javascripts/discourse/components/topic-post-badges.js.es6 @@ -1,3 +1,4 @@ +import StringBuffer from 'discourse/mixins/string-buffer'; // Creates a link function link(buffer, prop, url, cssClass, i18nKey, text) { @@ -7,12 +8,12 @@ function link(buffer, prop, url, cssClass, i18nKey, text) { buffer.push("" + (text || prop) + "\n"); } -export default Ember.Component.extend({ +export default Ember.Component.extend(StringBuffer, { tagName: 'span', classNameBindings: [':topic-post-badges'], - _shouldRerender: Discourse.View.renderIfChanged('url', 'unread', 'newPosts', 'unseen'), + rerenderTriggers: ['url', 'unread', 'newPosts', 'unseen'], - render: function(buffer) { + renderString: function(buffer) { var url = this.get('url'); link(buffer, this.get('unread'), url, 'unread', 'unread_posts'); diff --git a/app/assets/javascripts/discourse/components/topic-status.js.es6 b/app/assets/javascripts/discourse/components/topic-status.js.es6 index 1d0883f2e..dd8b0f8c0 100644 --- a/app/assets/javascripts/discourse/components/topic-status.js.es6 +++ b/app/assets/javascripts/discourse/components/topic-status.js.es6 @@ -1,18 +1,12 @@ -/** - This view is for rendering an icon representing the status of a topic +import StringBuffer from 'discourse/mixins/string-buffer'; - @class TopicStatusComponent - @extends Ember.Component - @namespace Discourse - @module Discourse -**/ -export default Ember.Component.extend({ +export default Ember.Component.extend(StringBuffer, { classNames: ['topic-statuses'], hasDisplayableStatus: Em.computed.or('topic.archived','topic.closed', 'topic.pinned', 'topic.unpinned', 'topic.invisible', 'topic.archetypeObject.notDefault', 'topic.is_warning'), - shouldRerender: Discourse.View.renderIfChanged('topic.archived', 'topic.closed', 'topic.pinned', 'topic.visible', 'topic.unpinned', 'topic.is_warning'), + rerenderTriggers: ['topic.archived', 'topic.closed', 'topic.pinned', 'topic.visible', 'topic.unpinned', 'topic.is_warning'], - didInsertElement: function(){ + watchClick: function(){ var self = this; this.$('a').click(function(){ @@ -27,13 +21,13 @@ export default Ember.Component.extend({ return false; }); - }, + }.on('didInsertElement'), canAct: function() { return Discourse.User.current() && !this.get('disableActions'); }.property('disableActions'), - render: function(buffer) { + renderString: function(buffer) { if (!this.get('hasDisplayableStatus')) { return; } var self = this; @@ -41,7 +35,7 @@ export default Ember.Component.extend({ var renderIconIf = function(conditionProp, name, key, actionable) { if (!self.get(conditionProp)) { return; } var title = Handlebars.Utils.escapeExpression(I18n.t("topic_statuses." + key + ".help")); - var startTag = actionable ? "a href='#'" : "span"; + var startTag = actionable ? "a href" : "span"; var endTag = actionable ? "a" : "span"; buffer.push("<" + startTag + " title='" + title + "' class='topic-status'>"); diff --git a/app/assets/javascripts/discourse/helpers/i18n_helpers.js b/app/assets/javascripts/discourse/helpers/i18n_helpers.js index 324229c97..fb9e8c7c8 100644 --- a/app/assets/javascripts/discourse/helpers/i18n_helpers.js +++ b/app/assets/javascripts/discourse/helpers/i18n_helpers.js @@ -49,14 +49,13 @@ Ember.Handlebars.registerBoundHelper("boundI18n", function(property, options) { @for Handlebars **/ Ember.Handlebars.registerHelper('countI18n', function(key, options) { - var view = Discourse.View.extend({ + var view = Discourse.View.extend(Discourse.StringBuffer, { tagName: 'span', - shouldRerender: Discourse.View.renderIfChanged('count', 'suffix'), + rerenderTriggers: ['count', 'suffix'], - render: function(buffer) { + renderString: function(buffer) { buffer.push(I18n.t(key + (this.get('suffix') || ''), { count: this.get('count') })); } - }); return Ember.Handlebars.helpers.view.call(this, view, options); }); diff --git a/app/assets/javascripts/discourse/views/activity-filter.js.es6 b/app/assets/javascripts/discourse/views/activity-filter.js.es6 index b29821fd5..24445239d 100644 --- a/app/assets/javascripts/discourse/views/activity-filter.js.es6 +++ b/app/assets/javascripts/discourse/views/activity-filter.js.es6 @@ -1,8 +1,10 @@ -export default Ember.Component.extend({ +import StringBuffer from 'discourse/mixins/string-buffer'; + +export default Ember.Component.extend(StringBuffer, { tagName: 'li', classNameBindings: ['active', 'noGlyph'], - shouldRerender: Discourse.View.renderIfChanged('content.count', 'count'), + rerenderTriggers: ['content.count', 'count'], noGlyph: Em.computed.empty('icon'), active: function() { @@ -37,7 +39,7 @@ export default Ember.Component.extend({ return this.get('content.description') || I18n.t("user.filters.all"); }.property('content.description'), - render: function(buffer) { + renderString: function(buffer) { buffer.push(""); var icon = this.get('icon'); if (icon) { diff --git a/app/assets/javascripts/discourse/views/button.js.es6 b/app/assets/javascripts/discourse/views/button.js.es6 index b06713cb0..0f62c7204 100644 --- a/app/assets/javascripts/discourse/views/button.js.es6 +++ b/app/assets/javascripts/discourse/views/button.js.es6 @@ -1,4 +1,6 @@ -export default Discourse.View.extend({ +import StringBuffer from 'discourse/mixins/string-buffer'; + +export default Discourse.View.extend(StringBuffer, { tagName: 'button', classNameBindings: [':btn', ':standard', 'dropDownToggle'], attributeBindings: ['title', 'data-toggle', 'data-share-url'], @@ -12,7 +14,7 @@ export default Discourse.View.extend({ return I18n.t(this.get('textKey')); }.property('textKey'), - render: function(buffer) { + renderString: function(buffer) { if (this.renderIcon) { this.renderIcon(buffer); } diff --git a/app/assets/javascripts/discourse/views/dropdown-button.js.es6 b/app/assets/javascripts/discourse/views/dropdown-button.js.es6 index 307183ea7..b10d8ad7a 100644 --- a/app/assets/javascripts/discourse/views/dropdown-button.js.es6 +++ b/app/assets/javascripts/discourse/views/dropdown-button.js.es6 @@ -1,41 +1,45 @@ -export default Discourse.View.extend({ - classNameBindings: [':btn-group', 'hidden'], - shouldRerender: Discourse.View.renderIfChanged('text', 'longDescription'), +import StringBuffer from 'discourse/mixins/string-buffer'; - didInsertElement: function() { - var self = this; +export default Discourse.View.extend(StringBuffer, { + classNameBindings: [':btn-group', 'hidden'], + rerenderTriggers: ['text', 'longDescription'], + + _bindClick: function() { // If there's a click handler, call it - if (self.clicked) { - self.$('ul li').on('click.dropdown-button', function(e) { + if (this.clicked) { + var self = this; + this.$().on('click.dropdown-button', 'ul li', function(e) { e.preventDefault(); - if ($(e.currentTarget).data('id') !== self.get('activeItem')) + if ($(e.currentTarget).data('id') !== self.get('activeItem')) { self.clicked($(e.currentTarget).data('id')); + } + self.$('.dropdown-toggle').dropdown('toggle'); return false; }); } - }, + }.on('didInsertElement'), - willDestroyElement: function() { - this.$('ul li').off('click.dropdown-button'); - }, + _unbindClick: function() { + this.$().off('click.dropdown-button', 'ul li'); + }.on('willDestroyElement'), - render: function(buffer) { - var self = this; + renderString: function(buffer) { - buffer.push("

" + self.get('title') + "

"); + buffer.push("

" + this.get('title') + "

"); buffer.push(""); buffer.push("
"); - var desc = self.get('longDescription'); + var desc = this.get('longDescription'); if (desc) { buffer.push("

"); buffer.push(desc); diff --git a/app/assets/javascripts/discourse/views/input-tip.js.es6 b/app/assets/javascripts/discourse/views/input-tip.js.es6 index b2d0709c9..6caf2dad6 100644 --- a/app/assets/javascripts/discourse/views/input-tip.js.es6 +++ b/app/assets/javascripts/discourse/views/input-tip.js.es6 @@ -1,19 +1,13 @@ -/** - This view handles rendering a tip when a field on a form is invalid +import StringBuffer from 'discourse/mixins/string-buffer'; - @class InputTipView - @extends Discourse.View - @namespace Discourse - @module Discourse -**/ -export default Discourse.View.extend({ +export default Discourse.View.extend(StringBuffer, { classNameBindings: [':tip', 'good', 'bad'], + rerenderTriggers: ['validation'], - shouldRerender: Discourse.View.renderIfChanged('validation'), bad: Em.computed.alias('validation.failed'), good: Em.computed.not('bad'), - render: function(buffer) { + renderString: function(buffer) { var reason = this.get('validation.reason'); if (reason) { var icon = this.get('good') ? 'fa-check' : 'fa-times'; diff --git a/app/assets/javascripts/discourse/views/post-menu.js.es6 b/app/assets/javascripts/discourse/views/post-menu.js.es6 index 4925a38c0..1c44a6ffb 100644 --- a/app/assets/javascripts/discourse/views/post-menu.js.es6 +++ b/app/assets/javascripts/discourse/views/post-menu.js.es6 @@ -1,3 +1,5 @@ +import StringBuffer from 'discourse/mixins/string-buffer'; + // Helper class for rendering a button export var Button = function(action, label, icon, opts) { this.action = action; @@ -28,11 +30,11 @@ Button.prototype.render = function(buffer) { var hiddenButtons; -export default Discourse.View.extend({ +export default Discourse.View.extend(StringBuffer, { tagName: 'section', classNames: ['post-menu-area', 'clearfix'], - shouldRerender: Discourse.View.renderIfChanged( + rerenderTriggers: [ 'post.deleted_at', 'post.flagsAvailable.@each', 'post.reply_count', @@ -43,13 +45,13 @@ export default Discourse.View.extend({ 'post.topic.deleted_at', 'post.replies.length', 'post.wiki', - 'collapsed'), + 'collapsed'], _collapsedByDefault: function() { this.set('collapsed', true); }.on('init'), - render: function(buffer) { + renderString: function(buffer) { var post = this.get('post'); buffer.push("