mirror of
https://github.com/discourse/discourse.git
synced 2025-09-06 10:50:21 +08:00
FIX: Fix rerendering issues on some components.
This commit is contained in:
parent
7069ee16e2
commit
5fd3f2547c
18 changed files with 96 additions and 162 deletions
|
@ -1,23 +1,16 @@
|
||||||
/**
|
import StringBuffer from 'discourse/mixins/string-buffer';
|
||||||
This component handles rendering of what actions have been taken on a post. It uses
|
|
||||||
buffer rendering for performance rather than a template.
|
|
||||||
|
|
||||||
@class ActionsHistoryComponent
|
export default Em.Component.extend(StringBuffer, {
|
||||||
@extends Em.Component
|
|
||||||
@namespace Discourse
|
|
||||||
@module Discourse
|
|
||||||
**/
|
|
||||||
export default Em.Component.extend({
|
|
||||||
tagName: 'section',
|
tagName: 'section',
|
||||||
classNameBindings: [':post-actions', 'hidden'],
|
classNameBindings: [':post-actions', 'hidden'],
|
||||||
actionsHistory: Em.computed.alias('post.actionsHistory'),
|
actionsHistory: Em.computed.alias('post.actionsHistory'),
|
||||||
emptyHistory: Em.computed.empty('actionsHistory'),
|
emptyHistory: Em.computed.empty('actionsHistory'),
|
||||||
hidden: Em.computed.and('emptyHistory', 'post.notDeleted'),
|
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.
|
// This was creating way too many bound ifs and subviews in the handlebars version.
|
||||||
render: function(buffer) {
|
renderString: function(buffer) {
|
||||||
|
|
||||||
if (!this.get('emptyHistory')) {
|
if (!this.get('emptyHistory')) {
|
||||||
this.get('actionsHistory').forEach(function(c) {
|
this.get('actionsHistory').forEach(function(c) {
|
||||||
buffer.push("<div class='post-action'>");
|
buffer.push("<div class='post-action'>");
|
||||||
|
|
|
@ -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 = [];
|
var notices = [];
|
||||||
|
|
||||||
if (this.site.get("isReadOnly")) {
|
if (this.site.get("isReadOnly")) {
|
||||||
|
|
|
@ -1,17 +1,11 @@
|
||||||
/**
|
import StringBuffer from 'discourse/mixins/string-buffer';
|
||||||
This view handles rendering of a navigation item
|
|
||||||
|
|
||||||
@class NavigationItemComponent
|
export default Ember.Component.extend(StringBuffer, {
|
||||||
@extends Ember.Component
|
|
||||||
@namespace Discourse
|
|
||||||
@module Discourse
|
|
||||||
**/
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
tagName: 'li',
|
tagName: 'li',
|
||||||
classNameBindings: ['active', 'content.hasIcon:has-icon'],
|
classNameBindings: ['active', 'content.hasIcon:has-icon'],
|
||||||
attributeBindings: ['title'],
|
attributeBindings: ['title'],
|
||||||
hidden: Em.computed.not('content.visible'),
|
hidden: Em.computed.not('content.visible'),
|
||||||
shouldRerender: Discourse.View.renderIfChanged('content.count'),
|
rerenderTriggers: ['content.count'],
|
||||||
|
|
||||||
title: function() {
|
title: function() {
|
||||||
var categoryName = this.get('content.categoryName'),
|
var categoryName = this.get('content.categoryName'),
|
||||||
|
@ -42,7 +36,7 @@ export default Ember.Component.extend({
|
||||||
return I18n.t("filters." + name + ".title", extra);
|
return I18n.t("filters." + name + ".title", extra);
|
||||||
}.property('content.count'),
|
}.property('content.count'),
|
||||||
|
|
||||||
render: function(buffer) {
|
renderString: function(buffer) {
|
||||||
var content = this.get('content');
|
var content = this.get('content');
|
||||||
buffer.push("<a href='" + content.get('href') + "'>");
|
buffer.push("<a href='" + content.get('href') + "'>");
|
||||||
if (content.get('hasIcon')) {
|
if (content.get('hasIcon')) {
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
var MAX_SHOWN = 5;
|
var MAX_SHOWN = 5;
|
||||||
|
|
||||||
export default Em.Component.extend({
|
import StringBuffer from 'discourse/mixins/string-buffer';
|
||||||
|
|
||||||
|
export default Em.Component.extend(StringBuffer, {
|
||||||
classNameBindings: [':gutter'],
|
classNameBindings: [':gutter'],
|
||||||
|
|
||||||
|
rerenderTriggers: ['expanded'],
|
||||||
|
|
||||||
// Roll up links to avoid duplicates
|
// Roll up links to avoid duplicates
|
||||||
collapsed: function() {
|
collapsed: function() {
|
||||||
var seen = {},
|
var seen = {},
|
||||||
|
@ -21,7 +25,7 @@ export default Em.Component.extend({
|
||||||
return result;
|
return result;
|
||||||
}.property('links'),
|
}.property('links'),
|
||||||
|
|
||||||
render: function(buffer) {
|
renderString: function(buffer) {
|
||||||
var links = this.get('collapsed'),
|
var links = this.get('collapsed'),
|
||||||
toRender = links,
|
toRender = links,
|
||||||
collapsed = !this.get('expanded');
|
collapsed = !this.get('expanded');
|
||||||
|
@ -62,10 +66,6 @@ export default Em.Component.extend({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_rerenderIfNeeded: function() {
|
|
||||||
this.rerender();
|
|
||||||
}.observes('expanded'),
|
|
||||||
|
|
||||||
click: function(e) {
|
click: function(e) {
|
||||||
var $target = $(e.target);
|
var $target = $(e.target);
|
||||||
if ($target.hasClass('toggle-more')) {
|
if ($target.hasClass('toggle-more')) {
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
export default Ember.Component.extend({
|
import StringBuffer from 'discourse/mixins/string-buffer';
|
||||||
tagName: 'h2',
|
|
||||||
|
|
||||||
_shouldRerender: Discourse.View.renderIfChanged('period.title'),
|
export default Ember.Component.extend(StringBuffer, {
|
||||||
render: function(buffer) {
|
tagName: 'h2',
|
||||||
|
rerenderTriggers: ['period.title'],
|
||||||
|
|
||||||
|
renderString: function(buffer) {
|
||||||
buffer.push("<i class='fa fa-calendar-o'></i> " + this.get('period.title'));
|
buffer.push("<i class='fa fa-calendar-o'></i> " + this.get('period.title'));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import StringBuffer from 'discourse/mixins/string-buffer';
|
||||||
|
|
||||||
// Creates a link
|
// Creates a link
|
||||||
function link(buffer, prop, url, cssClass, i18nKey, text) {
|
function link(buffer, prop, url, cssClass, i18nKey, text) {
|
||||||
|
@ -7,12 +8,12 @@ function link(buffer, prop, url, cssClass, i18nKey, text) {
|
||||||
buffer.push("<a href='" + url + "' class='badge " + cssClass + " badge-notification' title='" + title + "'>" + (text || prop) + "</a>\n");
|
buffer.push("<a href='" + url + "' class='badge " + cssClass + " badge-notification' title='" + title + "'>" + (text || prop) + "</a>\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Ember.Component.extend(StringBuffer, {
|
||||||
tagName: 'span',
|
tagName: 'span',
|
||||||
classNameBindings: [':topic-post-badges'],
|
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');
|
var url = this.get('url');
|
||||||
|
|
||||||
link(buffer, this.get('unread'), url, 'unread', 'unread_posts');
|
link(buffer, this.get('unread'), url, 'unread', 'unread_posts');
|
||||||
|
|
|
@ -1,18 +1,12 @@
|
||||||
/**
|
import StringBuffer from 'discourse/mixins/string-buffer';
|
||||||
This view is for rendering an icon representing the status of a topic
|
|
||||||
|
|
||||||
@class TopicStatusComponent
|
export default Ember.Component.extend(StringBuffer, {
|
||||||
@extends Ember.Component
|
|
||||||
@namespace Discourse
|
|
||||||
@module Discourse
|
|
||||||
**/
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
classNames: ['topic-statuses'],
|
classNames: ['topic-statuses'],
|
||||||
|
|
||||||
hasDisplayableStatus: Em.computed.or('topic.archived','topic.closed', 'topic.pinned', 'topic.unpinned', 'topic.invisible', 'topic.archetypeObject.notDefault', 'topic.is_warning'),
|
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;
|
var self = this;
|
||||||
|
|
||||||
this.$('a').click(function(){
|
this.$('a').click(function(){
|
||||||
|
@ -27,13 +21,13 @@ export default Ember.Component.extend({
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
},
|
}.on('didInsertElement'),
|
||||||
|
|
||||||
canAct: function() {
|
canAct: function() {
|
||||||
return Discourse.User.current() && !this.get('disableActions');
|
return Discourse.User.current() && !this.get('disableActions');
|
||||||
}.property('disableActions'),
|
}.property('disableActions'),
|
||||||
|
|
||||||
render: function(buffer) {
|
renderString: function(buffer) {
|
||||||
if (!this.get('hasDisplayableStatus')) { return; }
|
if (!this.get('hasDisplayableStatus')) { return; }
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
@ -41,7 +35,7 @@ export default Ember.Component.extend({
|
||||||
var renderIconIf = function(conditionProp, name, key, actionable) {
|
var renderIconIf = function(conditionProp, name, key, actionable) {
|
||||||
if (!self.get(conditionProp)) { return; }
|
if (!self.get(conditionProp)) { return; }
|
||||||
var title = Handlebars.Utils.escapeExpression(I18n.t("topic_statuses." + key + ".help"));
|
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";
|
var endTag = actionable ? "a" : "span";
|
||||||
|
|
||||||
buffer.push("<" + startTag + " title='" + title + "' class='topic-status'><i class='fa fa-" + name + "'></i></" + endTag + ">");
|
buffer.push("<" + startTag + " title='" + title + "' class='topic-status'><i class='fa fa-" + name + "'></i></" + endTag + ">");
|
||||||
|
|
|
@ -49,14 +49,13 @@ Ember.Handlebars.registerBoundHelper("boundI18n", function(property, options) {
|
||||||
@for Handlebars
|
@for Handlebars
|
||||||
**/
|
**/
|
||||||
Ember.Handlebars.registerHelper('countI18n', function(key, options) {
|
Ember.Handlebars.registerHelper('countI18n', function(key, options) {
|
||||||
var view = Discourse.View.extend({
|
var view = Discourse.View.extend(Discourse.StringBuffer, {
|
||||||
tagName: 'span',
|
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') }));
|
buffer.push(I18n.t(key + (this.get('suffix') || ''), { count: this.get('count') }));
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
return Ember.Handlebars.helpers.view.call(this, view, options);
|
return Ember.Handlebars.helpers.view.call(this, view, options);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
export default Ember.Component.extend({
|
import StringBuffer from 'discourse/mixins/string-buffer';
|
||||||
|
|
||||||
|
export default Ember.Component.extend(StringBuffer, {
|
||||||
tagName: 'li',
|
tagName: 'li',
|
||||||
classNameBindings: ['active', 'noGlyph'],
|
classNameBindings: ['active', 'noGlyph'],
|
||||||
|
|
||||||
shouldRerender: Discourse.View.renderIfChanged('content.count', 'count'),
|
rerenderTriggers: ['content.count', 'count'],
|
||||||
noGlyph: Em.computed.empty('icon'),
|
noGlyph: Em.computed.empty('icon'),
|
||||||
|
|
||||||
active: function() {
|
active: function() {
|
||||||
|
@ -37,7 +39,7 @@ export default Ember.Component.extend({
|
||||||
return this.get('content.description') || I18n.t("user.filters.all");
|
return this.get('content.description') || I18n.t("user.filters.all");
|
||||||
}.property('content.description'),
|
}.property('content.description'),
|
||||||
|
|
||||||
render: function(buffer) {
|
renderString: function(buffer) {
|
||||||
buffer.push("<a href='" + this.get('url') + "'>");
|
buffer.push("<a href='" + this.get('url') + "'>");
|
||||||
var icon = this.get('icon');
|
var icon = this.get('icon');
|
||||||
if (icon) {
|
if (icon) {
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
export default Discourse.View.extend({
|
import StringBuffer from 'discourse/mixins/string-buffer';
|
||||||
|
|
||||||
|
export default Discourse.View.extend(StringBuffer, {
|
||||||
tagName: 'button',
|
tagName: 'button',
|
||||||
classNameBindings: [':btn', ':standard', 'dropDownToggle'],
|
classNameBindings: [':btn', ':standard', 'dropDownToggle'],
|
||||||
attributeBindings: ['title', 'data-toggle', 'data-share-url'],
|
attributeBindings: ['title', 'data-toggle', 'data-share-url'],
|
||||||
|
@ -12,7 +14,7 @@ export default Discourse.View.extend({
|
||||||
return I18n.t(this.get('textKey'));
|
return I18n.t(this.get('textKey'));
|
||||||
}.property('textKey'),
|
}.property('textKey'),
|
||||||
|
|
||||||
render: function(buffer) {
|
renderString: function(buffer) {
|
||||||
if (this.renderIcon) {
|
if (this.renderIcon) {
|
||||||
this.renderIcon(buffer);
|
this.renderIcon(buffer);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,41 +1,45 @@
|
||||||
export default Discourse.View.extend({
|
import StringBuffer from 'discourse/mixins/string-buffer';
|
||||||
classNameBindings: [':btn-group', 'hidden'],
|
|
||||||
shouldRerender: Discourse.View.renderIfChanged('text', 'longDescription'),
|
|
||||||
|
|
||||||
didInsertElement: function() {
|
export default Discourse.View.extend(StringBuffer, {
|
||||||
var self = this;
|
classNameBindings: [':btn-group', 'hidden'],
|
||||||
|
rerenderTriggers: ['text', 'longDescription'],
|
||||||
|
|
||||||
|
_bindClick: function() {
|
||||||
// If there's a click handler, call it
|
// If there's a click handler, call it
|
||||||
if (self.clicked) {
|
if (this.clicked) {
|
||||||
self.$('ul li').on('click.dropdown-button', function(e) {
|
var self = this;
|
||||||
|
this.$().on('click.dropdown-button', 'ul li', function(e) {
|
||||||
e.preventDefault();
|
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.clicked($(e.currentTarget).data('id'));
|
||||||
|
}
|
||||||
|
self.$('.dropdown-toggle').dropdown('toggle');
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}.on('didInsertElement'),
|
||||||
|
|
||||||
willDestroyElement: function() {
|
_unbindClick: function() {
|
||||||
this.$('ul li').off('click.dropdown-button');
|
this.$().off('click.dropdown-button', 'ul li');
|
||||||
},
|
}.on('willDestroyElement'),
|
||||||
|
|
||||||
render: function(buffer) {
|
renderString: function(buffer) {
|
||||||
var self = this;
|
|
||||||
|
|
||||||
buffer.push("<h4 class='title'>" + self.get('title') + "</h4>");
|
buffer.push("<h4 class='title'>" + this.get('title') + "</h4>");
|
||||||
buffer.push("<button class='btn standard dropdown-toggle' data-toggle='dropdown'>");
|
buffer.push("<button class='btn standard dropdown-toggle' data-toggle='dropdown'>");
|
||||||
buffer.push(self.get('text'));
|
buffer.push(this.get('text'));
|
||||||
buffer.push("</button>");
|
buffer.push("</button>");
|
||||||
buffer.push("<ul class='dropdown-menu'>");
|
buffer.push("<ul class='dropdown-menu'>");
|
||||||
|
|
||||||
_.each(self.get('dropDownContent'), function(row) {
|
var self = this;
|
||||||
|
this.get('dropDownContent').forEach(function(row) {
|
||||||
var id = row.id,
|
var id = row.id,
|
||||||
title = row.title,
|
title = row.title,
|
||||||
iconClass = row.styleClasses,
|
iconClass = row.styleClasses,
|
||||||
description = row.description,
|
description = row.description,
|
||||||
className = (self.get('activeItem') === id? 'disabled': '');
|
className = (self.get('activeItem') === id ? 'disabled': '');
|
||||||
|
|
||||||
buffer.push("<li data-id=\"" + id + "\" class=\"" + className + "\"><a href='#'>");
|
buffer.push("<li data-id=\"" + id + "\" class=\"" + className + "\"><a href>");
|
||||||
buffer.push("<span class='icon " + iconClass + "'></span>");
|
buffer.push("<span class='icon " + iconClass + "'></span>");
|
||||||
buffer.push("<div><span class='title'>" + title + "</span>");
|
buffer.push("<div><span class='title'>" + title + "</span>");
|
||||||
buffer.push("<span>" + description + "</span></div>");
|
buffer.push("<span>" + description + "</span></div>");
|
||||||
|
@ -44,7 +48,7 @@ export default Discourse.View.extend({
|
||||||
|
|
||||||
buffer.push("</ul>");
|
buffer.push("</ul>");
|
||||||
|
|
||||||
var desc = self.get('longDescription');
|
var desc = this.get('longDescription');
|
||||||
if (desc) {
|
if (desc) {
|
||||||
buffer.push("<p>");
|
buffer.push("<p>");
|
||||||
buffer.push(desc);
|
buffer.push(desc);
|
||||||
|
|
|
@ -1,19 +1,13 @@
|
||||||
/**
|
import StringBuffer from 'discourse/mixins/string-buffer';
|
||||||
This view handles rendering a tip when a field on a form is invalid
|
|
||||||
|
|
||||||
@class InputTipView
|
export default Discourse.View.extend(StringBuffer, {
|
||||||
@extends Discourse.View
|
|
||||||
@namespace Discourse
|
|
||||||
@module Discourse
|
|
||||||
**/
|
|
||||||
export default Discourse.View.extend({
|
|
||||||
classNameBindings: [':tip', 'good', 'bad'],
|
classNameBindings: [':tip', 'good', 'bad'],
|
||||||
|
rerenderTriggers: ['validation'],
|
||||||
|
|
||||||
shouldRerender: Discourse.View.renderIfChanged('validation'),
|
|
||||||
bad: Em.computed.alias('validation.failed'),
|
bad: Em.computed.alias('validation.failed'),
|
||||||
good: Em.computed.not('bad'),
|
good: Em.computed.not('bad'),
|
||||||
|
|
||||||
render: function(buffer) {
|
renderString: function(buffer) {
|
||||||
var reason = this.get('validation.reason');
|
var reason = this.get('validation.reason');
|
||||||
if (reason) {
|
if (reason) {
|
||||||
var icon = this.get('good') ? 'fa-check' : 'fa-times';
|
var icon = this.get('good') ? 'fa-check' : 'fa-times';
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import StringBuffer from 'discourse/mixins/string-buffer';
|
||||||
|
|
||||||
// Helper class for rendering a button
|
// Helper class for rendering a button
|
||||||
export var Button = function(action, label, icon, opts) {
|
export var Button = function(action, label, icon, opts) {
|
||||||
this.action = action;
|
this.action = action;
|
||||||
|
@ -28,11 +30,11 @@ Button.prototype.render = function(buffer) {
|
||||||
|
|
||||||
var hiddenButtons;
|
var hiddenButtons;
|
||||||
|
|
||||||
export default Discourse.View.extend({
|
export default Discourse.View.extend(StringBuffer, {
|
||||||
tagName: 'section',
|
tagName: 'section',
|
||||||
classNames: ['post-menu-area', 'clearfix'],
|
classNames: ['post-menu-area', 'clearfix'],
|
||||||
|
|
||||||
shouldRerender: Discourse.View.renderIfChanged(
|
rerenderTriggers: [
|
||||||
'post.deleted_at',
|
'post.deleted_at',
|
||||||
'post.flagsAvailable.@each',
|
'post.flagsAvailable.@each',
|
||||||
'post.reply_count',
|
'post.reply_count',
|
||||||
|
@ -43,13 +45,13 @@ export default Discourse.View.extend({
|
||||||
'post.topic.deleted_at',
|
'post.topic.deleted_at',
|
||||||
'post.replies.length',
|
'post.replies.length',
|
||||||
'post.wiki',
|
'post.wiki',
|
||||||
'collapsed'),
|
'collapsed'],
|
||||||
|
|
||||||
_collapsedByDefault: function() {
|
_collapsedByDefault: function() {
|
||||||
this.set('collapsed', true);
|
this.set('collapsed', true);
|
||||||
}.on('init'),
|
}.on('init'),
|
||||||
|
|
||||||
render: function(buffer) {
|
renderString: function(buffer) {
|
||||||
var post = this.get('post');
|
var post = this.get('post');
|
||||||
|
|
||||||
buffer.push("<nav class='post-controls'>");
|
buffer.push("<nav class='post-controls'>");
|
||||||
|
|
|
@ -6,7 +6,7 @@ export default ButtonView.extend({
|
||||||
helpKeyBinding: 'controller.starTooltipKey',
|
helpKeyBinding: 'controller.starTooltipKey',
|
||||||
attributeBindings: ['disabled'],
|
attributeBindings: ['disabled'],
|
||||||
|
|
||||||
shouldRerender: Discourse.View.renderIfChanged('controller.starred'),
|
rerenderTriggers: ['controller.starred'],
|
||||||
|
|
||||||
click: function() {
|
click: function() {
|
||||||
this.get('controller').send('toggleStar');
|
this.get('controller').send('toggleStar');
|
||||||
|
|
|
@ -1,19 +1,15 @@
|
||||||
/**
|
import StringBuffer from 'discourse/mixins/string-buffer';
|
||||||
This view is used for rendering the notification that a topic will
|
|
||||||
automatically close.
|
|
||||||
|
|
||||||
@class TopicClosingView
|
export default Discourse.View.extend(StringBuffer, {
|
||||||
@extends Discourse.View
|
|
||||||
@namespace Discourse
|
|
||||||
@module Discourse
|
|
||||||
**/
|
|
||||||
export default Discourse.View.extend({
|
|
||||||
elementId: 'topic-closing-info',
|
elementId: 'topic-closing-info',
|
||||||
delayedRerender: null,
|
delayedRerender: null,
|
||||||
|
|
||||||
shouldRerender: Discourse.View.renderIfChanged('topic.closed', 'topic.details.{auto_close_at,auto_close_based_on_last_post,auto_close_hours}'),
|
rerenderTriggers: ['topic.closed',
|
||||||
|
'topic.details.auto_close_at',
|
||||||
|
'topic.details.auto_close_based_on_last_post',
|
||||||
|
'topic.details.auto_close_hours'],
|
||||||
|
|
||||||
render: function(buffer) {
|
renderString: function(buffer) {
|
||||||
if (!this.present('topic.details.auto_close_at')) return;
|
if (!this.present('topic.details.auto_close_at')) return;
|
||||||
if (this.get("topic.closed")) return;
|
if (this.get("topic.closed")) return;
|
||||||
|
|
||||||
|
@ -36,8 +32,7 @@ export default Discourse.View.extend({
|
||||||
}
|
}
|
||||||
|
|
||||||
var basedOnLastPost = this.get("topic.details.auto_close_based_on_last_post");
|
var basedOnLastPost = this.get("topic.details.auto_close_based_on_last_post");
|
||||||
var key = basedOnLastPost ? 'topic.auto_close_notice_based_on_last_post' : 'topic.auto_close_notice'
|
var key = basedOnLastPost ? 'topic.auto_close_notice_based_on_last_post' : 'topic.auto_close_notice';
|
||||||
|
|
||||||
var autoCloseHours = this.get("topic.details.auto_close_hours") || 0;
|
var autoCloseHours = this.get("topic.details.auto_close_hours") || 0;
|
||||||
|
|
||||||
buffer.push('<h3><i class="fa fa-clock-o"></i> ');
|
buffer.push('<h3><i class="fa fa-clock-o"></i> ');
|
||||||
|
|
|
@ -6,7 +6,10 @@ import DiscourseContainerView from 'discourse/views/container';
|
||||||
|
|
||||||
export default DiscourseContainerView.extend({
|
export default DiscourseContainerView.extend({
|
||||||
classNameBindings: ['hidden', ':topic-map'],
|
classNameBindings: ['hidden', ':topic-map'],
|
||||||
shouldRerender: Discourse.View.renderIfChanged('topic.posts_count'),
|
|
||||||
|
_postsChanged: function() {
|
||||||
|
Ember.run.once(this, 'rerender');
|
||||||
|
}.observes('topic.posts_count'),
|
||||||
|
|
||||||
hidden: function() {
|
hidden: function() {
|
||||||
if (!this.get('post.firstPost')) return true;
|
if (!this.get('post.firstPost')) return true;
|
||||||
|
|
|
@ -40,16 +40,8 @@ Discourse.View.reopenClass({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
Returns an observer that will re-render if properties change. This is useful for
|
|
||||||
views where rendering is done to a buffer manually and need to know when to trigger
|
|
||||||
a new render call.
|
|
||||||
|
|
||||||
@method renderIfChanged
|
|
||||||
@params {String} propertyNames*
|
|
||||||
@return {Function} observer
|
|
||||||
**/
|
|
||||||
renderIfChanged: function() {
|
renderIfChanged: function() {
|
||||||
|
Em.warn("`rerenderIfChanged` is deprecated. Use the `StringBuffer` mixin with `rerenderTriggers` instead.");
|
||||||
var args = Array.prototype.slice.call(arguments, 0);
|
var args = Array.prototype.slice.call(arguments, 0);
|
||||||
args.unshift(function () {
|
args.unshift(function () {
|
||||||
Ember.run.once(this, 'rerender');
|
Ember.run.once(this, 'rerender');
|
||||||
|
|
|
@ -30,47 +30,3 @@ test("registerHelper: enables embedding a child view in a parent view via dedica
|
||||||
equal(parentView.$("#child").length, 1, "child view registered as helper is appended to the parent view");
|
equal(parentView.$("#child").length, 1, "child view registered as helper is appended to the parent view");
|
||||||
equal(parentView.$("#child").text(), "foo", "child view registered as helper gets parameters provided during helper invocation in parent's template");
|
equal(parentView.$("#child").text(), "foo", "child view registered as helper gets parameters provided during helper invocation in parent's template");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("renderIfChanged: rerenders the whole view template when one of registered view fields changes", function() {
|
|
||||||
var view, rerenderSpy;
|
|
||||||
|
|
||||||
var viewRerendersOnceWhen = function(message, changeCallback) {
|
|
||||||
rerenderSpy.reset();
|
|
||||||
Ember.run(function() { changeCallback(); });
|
|
||||||
ok(rerenderSpy.calledOnce, "view rerenders when " + message);
|
|
||||||
};
|
|
||||||
|
|
||||||
var viewDoesNotRerenderWhen = function(message, changeCallback) {
|
|
||||||
rerenderSpy.reset();
|
|
||||||
Ember.run(function() { changeCallback(); });
|
|
||||||
ok(!rerenderSpy.called, "view does not rerender when " + message);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
view = Ember.View.extend({
|
|
||||||
shouldRerender: Discourse.View.renderIfChanged("simple", "complex.@each.nested")
|
|
||||||
}).create({
|
|
||||||
simple: "initial value",
|
|
||||||
complex: [Ember.Object.create({nested: "initial value"})],
|
|
||||||
unregistered: "initial value"
|
|
||||||
});
|
|
||||||
|
|
||||||
rerenderSpy = sinon.spy(view, "rerender");
|
|
||||||
|
|
||||||
Ember.run(function() {
|
|
||||||
view.appendTo("#qunit-fixture");
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
viewRerendersOnceWhen("a simple field (holding a string) changes", function() {
|
|
||||||
view.set("simple", "updated value");
|
|
||||||
});
|
|
||||||
|
|
||||||
viewRerendersOnceWhen("a nested sub-field of a complex field (holding an array of objects) changes", function() {
|
|
||||||
view.get("complex").objectAt(0).set("nested", "updated value");
|
|
||||||
});
|
|
||||||
|
|
||||||
viewDoesNotRerenderWhen("unregistered field changes", function() {
|
|
||||||
view.set("unregistered", "updated value");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue