Added some macros to simplify code

This commit is contained in:
Robin Ward 2013-07-10 14:56:00 -04:00
parent 1fef617818
commit 8888ae4b40
17 changed files with 82 additions and 82 deletions

View file

@ -0,0 +1,17 @@
Discourse.computed = {
/**
Returns whether two properties are equal to each other.
@method propertyEqual
@params {String} p1 the first property
@params {String} p2 the second property
@return {Function} computedProperty function
**/
propertyEqual: function(p1, p2) {
return Ember.computed(function() {
return this.get(p1) === this.get(p2);
}).property(p1, p2);
}
}

View file

@ -37,14 +37,11 @@ Ember.Handlebars.registerHelper('i18n', function(property, options) {
Ember.Handlebars.registerHelper('countI18n', function(key, options) {
var view = Discourse.View.extend({
tagName: 'span',
shouldRerender: Discourse.View.renderIfChanged('countChanged'),
render: function(buffer) {
buffer.push(I18n.t(key, { count: this.get('count') }));
},
countChanged: function() {
this.rerender();
}.observes('count')
}
});
return Ember.Handlebars.helpers.view.call(this, view, options);

View file

@ -60,7 +60,7 @@
<div class='span5 gutter'>
{{collection contentBinding="internalLinks" itemViewClass="Discourse.PostLinkView" tagName="ul" classNames="post-links"}}
{{#if controller.details.can_reply_as_new_topic}}
{{#if topic.details.can_reply_as_new_topic}}
<a href='#' class='reply-new' {{action replyAsNewTopic this}}><i class='icon icon-plus'></i>{{i18n post.reply_as_new_topic}}</a>
{{/if}}
</div>

View file

@ -11,18 +11,14 @@ Discourse.ActionsHistoryView = Discourse.View.extend({
tagName: 'section',
classNameBindings: [':post-actions', 'hidden'],
hidden: (function() {
return this.blank('content');
}).property('content.@each'),
hidden: Em.computed.empty('content'),
usersChanged: (function() {
return this.rerender();
}).observes('content.@each', 'content.users.@each'),
shouldRerender: Discourse.View.renderIfChanged('content.@each', 'content.users.@each'),
// This was creating way too many bound ifs and subviews in the handlebars version.
render: function(buffer) {
if (!this.present('content')) return;
return this.get('content').forEach(function(c) {
var actionString, iconsHtml;
buffer.push("<div class='post-action'>");

View file

@ -10,6 +10,8 @@ Discourse.DropdownButtonView = Discourse.View.extend({
classNames: ['btn-group'],
attributeBindings: ['data-not-implemented'],
shouldRerender: Discourse.View.renderIfChanged('text', 'longDescription'),
didInsertElement: function(e) {
// If there's a click handler, call it
if (this.clicked) {
@ -26,10 +28,6 @@ Discourse.DropdownButtonView = Discourse.View.extend({
this.$('ul li').off('click.dropdown-button');
},
textChanged: function() {
this.rerender();
}.observes('text', 'longDescription'),
render: function(buffer) {
buffer.push("<h4 class='title'>" + this.get('title') + "</h4>");
buffer.push("<button class='btn standard dropdown-toggle' data-toggle='dropdown'>");

View file

@ -10,9 +10,7 @@ Discourse.FavoriteButton = Discourse.ButtonView.extend({
textKey: 'favorite.title',
helpKeyBinding: 'controller.content.favoriteTooltipKey',
favoriteChanged: function() {
this.rerender();
}.observes('controller.content.starred'),
shouldRerender: Discourse.View.renderIfChanged('controller.content.starred'),
click: function() {
this.get('controller').toggleStar();

View file

@ -10,6 +10,8 @@
Discourse.HotnessView = Discourse.View.extend({
classNames: ['hotness-control'],
shouldRerender: Discourse.View.renderIfChanged('hotness'),
render: function(buffer) {
// Our scale goes to 11!
for (var i=1; i<12; i++) {
@ -21,15 +23,6 @@ Discourse.HotnessView = Discourse.View.extend({
}
},
/**
Trigger a re-render whenever the hotness changes
@observer hotnessChanged
**/
hotnessChanged: function() {
this.rerender();
}.observes('hotness'),
/**
When the user clicks on a hotness value button, change it.

View file

@ -9,17 +9,9 @@
Discourse.InputTipView = Discourse.View.extend({
classNameBindings: [':tip', 'good', 'bad'],
good: function() {
return !this.get('validation.failed');
}.property('validation'),
bad: function() {
return this.get('validation.failed');
}.property('validation'),
triggerRender: function() {
return this.rerender();
}.observes('validation'),
shouldRerender: Discourse.View.renderIfChanged('validation'),
bad: Em.computed.alias('validation.failed'),
good: Em.computed.not('bad'),
render: function(buffer) {
var reason = this.get('validation.reason');

View file

@ -8,9 +8,13 @@
**/
Discourse.NavItemView = Discourse.View.extend({
tagName: 'li',
classNameBindings: ['isActive', 'content.hasIcon:has-icon'],
classNameBindings: ['active', 'content.hasIcon:has-icon'],
attributeBindings: ['title'],
countBinding: Ember.Binding.oneWay('content.count'),
hidden: Em.computed.not('content.visible'),
count: Ember.computed.alias('content.count'),
shouldRerender: Discourse.View.renderIfChanged('count'),
active: Discourse.computed.propertyEqual('contentNameSlug', 'controller.filterMode'),
title: function() {
var categoryName, extra, name;
@ -23,16 +27,13 @@ Discourse.NavItemView = Discourse.View.extend({
return I18n.t("filters." + name + ".help", extra);
}.property("content.filter"),
isActive: function() {
if (this.get("content.name").toLowerCase().replace(' ','-') === this.get("controller.filterMode")) return "active";
return "";
}.property("content.name", "controller.filterMode"),
contentNameSlug: function() {
return this.get("content.name").toLowerCase().replace(' ','-');
}.property('content.name'),
hidden: Em.computed.not('content.visible'),
countChanged: function(){
this.rerender();
}.observes('count'),
// active: function() {
// return (this.get("contentNameSlug") === this.get("controller.filterMode"));
// }.property("contentNameSlug", "controller.filterMode"),
name: function() {
var categoryName, extra, name;

View file

@ -10,6 +10,17 @@ Discourse.PostMenuView = Discourse.View.extend({
tagName: 'section',
classNames: ['post-menu-area', 'clearfix'],
shouldRerender: Discourse.View.renderIfChanged(
'post.deleted_at',
'post.flagsAvailable.@each',
'post.url',
'post.bookmarked',
'post.reply_count',
'post.showRepliesBelow',
'post.can_delete',
'post.read',
'post.topic.last_read_post_number'),
render: function(buffer) {
var post = this.get('post');
buffer.push("<nav class='post-controls'>");
@ -35,12 +46,6 @@ Discourse.PostMenuView = Discourse.View.extend({
handler.call(this);
},
// Trigger re-rendering
needsToRender: function() {
this.rerender();
}.observes('post.deleted_at', 'post.flagsAvailable.@each', 'post.url', 'post.bookmarked', 'post.reply_count',
'post.showRepliesBelow', 'post.can_delete', 'post.read', 'post.topic.last_read_post_number'),
// Replies Button
renderReplies: function(post, buffer) {
if (!post.get('showRepliesBelow')) return;

View file

@ -2,12 +2,10 @@
Discourse.RawDivView = Ember.View.extend({
shouldRerender: Discourse.View.renderIfChanged('content'),
render: function(buffer) {
buffer.push(this.get('content'));
},
}
contentChanged: function() {
this.rerender();
}.observes('content')
});

View file

@ -11,9 +11,7 @@ Discourse.TopicClosingView = Discourse.View.extend({
elementId: 'topic-closing-info',
delayedRerender: null,
contentChanged: function() {
this.rerender();
}.observes('topic.details.auto_close_at'),
shouldRerender: Discourse.View.renderIfChanged('topic.details.auto_close_at'),
render: function(buffer) {
if (!this.present('topic.details.auto_close_at')) return;
@ -49,12 +47,12 @@ Discourse.TopicClosingView = Discourse.View.extend({
buffer.push('</h3>');
// TODO Sam: concerned this can cause a heavy rerender loop
this.delayedRerender = Em.run.later(this, this.rerender, rerenderDelay);
this.set('delayedRerender', Em.run.later(this, this.rerender, rerenderDelay));
},
willDestroyElement: function() {
if( this.delayedRerender ) {
Em.run.cancel(this.delayedRerender);
Em.run.cancel(this.get('delayedRerender'));
}
}
});

View file

@ -17,9 +17,7 @@ Discourse.TopicStatusView = Discourse.View.extend({
return false;
}.property('topic.closed', 'topic.pinned', 'topic.visible'),
statusChanged: function() {
this.rerender();
}.observes('topic.closed', 'topic.pinned', 'topic.visible'),
shouldRerender: Discourse.View.renderIfChanged('topic.closed', 'topic.pinned', 'topic.visible'),
renderIcon: function(buffer, name, key) {
var title = I18n.t("topic_statuses." + key + ".help");
@ -27,8 +25,7 @@ Discourse.TopicStatusView = Discourse.View.extend({
},
render: function(buffer) {
if (!this.get('hasDisplayableStatus')) return;
if (!this.get('hasDisplayableStatus')) { return; }
// Allow a plugin to add a custom icon to a topic
this.trigger('addCustomIcon', buffer);

View file

@ -26,12 +26,10 @@ Discourse.TopicSummaryView = Discourse.ContainerView.extend({
return allLinks.slice(0, Discourse.TopicSummaryView.LINKS_SHOWN);
}.property('topic.details.links', 'allLinksShown'),
newPostCreated: function() {
this.rerender();
}.observes('topic.posts_count'),
shouldRerender: Discourse.View.renderIfChanged('topic.posts_count'),
hidden: function() {
if (this.get('post.post_number') !== 1) return true;
if (!this.get('post.firstPost')) return true;
if (this.get('controller.content.archetype') === 'private_message') return false;
if (this.get('controller.content.archetype') !== 'regular') return true;
return this.get('controller.content.posts_count') < 2;

View file

@ -11,10 +11,7 @@ Discourse.ActivityFilterView = Discourse.View.extend({
classNameBindings: ['active'],
stream: Em.computed.alias('controller.content'),
countChanged: function(){
this.rerender();
}.observes('count'),
shouldRerender: Discourse.View.renderIfChanged('count'),
active: function() {
var content = this.get('content');

View file

@ -26,6 +26,21 @@ Discourse.View.reopenClass({
Discourse.Utilities.normalizeHash(hash, types);
return Ember.Handlebars.helpers.view.call(this, helperClass, options);
});
},
/**
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() {
var args = Array.prototype.slice.call(arguments, 0);
args.unshift(function () { this.rerender(); });
return Ember.observer.apply(this, args);
}
});

View file

@ -40,7 +40,8 @@ class PostSerializer < BasicPostSerializer
:hidden,
:hidden_reason_id,
:deleted_at,
:trust_level
:trust_level,
:deleted_by
def moderator?
@ -72,7 +73,6 @@ class PostSerializer < BasicPostSerializer
end
def link_counts
return @single_post_link_counts if @single_post_link_counts.present?
# TODO: This could be better, just porting the old one over