mirror of
https://github.com/discourse/discourse.git
synced 2025-06-08 00:27:32 +08:00
Added some macros to simplify code
This commit is contained in:
17
app/assets/javascripts/discourse/components/computed.js
Normal file
17
app/assets/javascripts/discourse/components/computed.js
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -37,14 +37,11 @@ Ember.Handlebars.registerHelper('i18n', function(property, options) {
|
|||||||
Ember.Handlebars.registerHelper('countI18n', function(key, options) {
|
Ember.Handlebars.registerHelper('countI18n', function(key, options) {
|
||||||
var view = Discourse.View.extend({
|
var view = Discourse.View.extend({
|
||||||
tagName: 'span',
|
tagName: 'span',
|
||||||
|
shouldRerender: Discourse.View.renderIfChanged('countChanged'),
|
||||||
|
|
||||||
render: function(buffer) {
|
render: function(buffer) {
|
||||||
buffer.push(I18n.t(key, { count: this.get('count') }));
|
buffer.push(I18n.t(key, { count: this.get('count') }));
|
||||||
},
|
}
|
||||||
|
|
||||||
countChanged: function() {
|
|
||||||
this.rerender();
|
|
||||||
}.observes('count')
|
|
||||||
|
|
||||||
});
|
});
|
||||||
return Ember.Handlebars.helpers.view.call(this, view, options);
|
return Ember.Handlebars.helpers.view.call(this, view, options);
|
||||||
|
@ -60,7 +60,7 @@
|
|||||||
|
|
||||||
<div class='span5 gutter'>
|
<div class='span5 gutter'>
|
||||||
{{collection contentBinding="internalLinks" itemViewClass="Discourse.PostLinkView" tagName="ul" classNames="post-links"}}
|
{{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>
|
<a href='#' class='reply-new' {{action replyAsNewTopic this}}><i class='icon icon-plus'></i>{{i18n post.reply_as_new_topic}}</a>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
@ -11,18 +11,14 @@ Discourse.ActionsHistoryView = Discourse.View.extend({
|
|||||||
tagName: 'section',
|
tagName: 'section',
|
||||||
classNameBindings: [':post-actions', 'hidden'],
|
classNameBindings: [':post-actions', 'hidden'],
|
||||||
|
|
||||||
hidden: (function() {
|
hidden: Em.computed.empty('content'),
|
||||||
return this.blank('content');
|
|
||||||
}).property('content.@each'),
|
|
||||||
|
|
||||||
usersChanged: (function() {
|
shouldRerender: Discourse.View.renderIfChanged('content.@each', 'content.users.@each'),
|
||||||
return this.rerender();
|
|
||||||
}).observes('content.@each', 'content.users.@each'),
|
|
||||||
|
|
||||||
// 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) {
|
render: function(buffer) {
|
||||||
|
|
||||||
if (!this.present('content')) return;
|
if (!this.present('content')) return;
|
||||||
|
|
||||||
return this.get('content').forEach(function(c) {
|
return this.get('content').forEach(function(c) {
|
||||||
var actionString, iconsHtml;
|
var actionString, iconsHtml;
|
||||||
buffer.push("<div class='post-action'>");
|
buffer.push("<div class='post-action'>");
|
||||||
|
@ -10,6 +10,8 @@ Discourse.DropdownButtonView = Discourse.View.extend({
|
|||||||
classNames: ['btn-group'],
|
classNames: ['btn-group'],
|
||||||
attributeBindings: ['data-not-implemented'],
|
attributeBindings: ['data-not-implemented'],
|
||||||
|
|
||||||
|
shouldRerender: Discourse.View.renderIfChanged('text', 'longDescription'),
|
||||||
|
|
||||||
didInsertElement: function(e) {
|
didInsertElement: function(e) {
|
||||||
// If there's a click handler, call it
|
// If there's a click handler, call it
|
||||||
if (this.clicked) {
|
if (this.clicked) {
|
||||||
@ -26,10 +28,6 @@ Discourse.DropdownButtonView = Discourse.View.extend({
|
|||||||
this.$('ul li').off('click.dropdown-button');
|
this.$('ul li').off('click.dropdown-button');
|
||||||
},
|
},
|
||||||
|
|
||||||
textChanged: function() {
|
|
||||||
this.rerender();
|
|
||||||
}.observes('text', 'longDescription'),
|
|
||||||
|
|
||||||
render: function(buffer) {
|
render: function(buffer) {
|
||||||
buffer.push("<h4 class='title'>" + this.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'>");
|
||||||
|
@ -10,9 +10,7 @@ Discourse.FavoriteButton = Discourse.ButtonView.extend({
|
|||||||
textKey: 'favorite.title',
|
textKey: 'favorite.title',
|
||||||
helpKeyBinding: 'controller.content.favoriteTooltipKey',
|
helpKeyBinding: 'controller.content.favoriteTooltipKey',
|
||||||
|
|
||||||
favoriteChanged: function() {
|
shouldRerender: Discourse.View.renderIfChanged('controller.content.starred'),
|
||||||
this.rerender();
|
|
||||||
}.observes('controller.content.starred'),
|
|
||||||
|
|
||||||
click: function() {
|
click: function() {
|
||||||
this.get('controller').toggleStar();
|
this.get('controller').toggleStar();
|
||||||
|
@ -10,6 +10,8 @@
|
|||||||
Discourse.HotnessView = Discourse.View.extend({
|
Discourse.HotnessView = Discourse.View.extend({
|
||||||
classNames: ['hotness-control'],
|
classNames: ['hotness-control'],
|
||||||
|
|
||||||
|
shouldRerender: Discourse.View.renderIfChanged('hotness'),
|
||||||
|
|
||||||
render: function(buffer) {
|
render: function(buffer) {
|
||||||
// Our scale goes to 11!
|
// Our scale goes to 11!
|
||||||
for (var i=1; i<12; i++) {
|
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.
|
When the user clicks on a hotness value button, change it.
|
||||||
|
|
||||||
|
@ -9,17 +9,9 @@
|
|||||||
Discourse.InputTipView = Discourse.View.extend({
|
Discourse.InputTipView = Discourse.View.extend({
|
||||||
classNameBindings: [':tip', 'good', 'bad'],
|
classNameBindings: [':tip', 'good', 'bad'],
|
||||||
|
|
||||||
good: function() {
|
shouldRerender: Discourse.View.renderIfChanged('validation'),
|
||||||
return !this.get('validation.failed');
|
bad: Em.computed.alias('validation.failed'),
|
||||||
}.property('validation'),
|
good: Em.computed.not('bad'),
|
||||||
|
|
||||||
bad: function() {
|
|
||||||
return this.get('validation.failed');
|
|
||||||
}.property('validation'),
|
|
||||||
|
|
||||||
triggerRender: function() {
|
|
||||||
return this.rerender();
|
|
||||||
}.observes('validation'),
|
|
||||||
|
|
||||||
render: function(buffer) {
|
render: function(buffer) {
|
||||||
var reason = this.get('validation.reason');
|
var reason = this.get('validation.reason');
|
||||||
|
@ -8,9 +8,13 @@
|
|||||||
**/
|
**/
|
||||||
Discourse.NavItemView = Discourse.View.extend({
|
Discourse.NavItemView = Discourse.View.extend({
|
||||||
tagName: 'li',
|
tagName: 'li',
|
||||||
classNameBindings: ['isActive', 'content.hasIcon:has-icon'],
|
classNameBindings: ['active', 'content.hasIcon:has-icon'],
|
||||||
attributeBindings: ['title'],
|
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() {
|
title: function() {
|
||||||
var categoryName, extra, name;
|
var categoryName, extra, name;
|
||||||
@ -23,16 +27,13 @@ Discourse.NavItemView = Discourse.View.extend({
|
|||||||
return I18n.t("filters." + name + ".help", extra);
|
return I18n.t("filters." + name + ".help", extra);
|
||||||
}.property("content.filter"),
|
}.property("content.filter"),
|
||||||
|
|
||||||
isActive: function() {
|
contentNameSlug: function() {
|
||||||
if (this.get("content.name").toLowerCase().replace(' ','-') === this.get("controller.filterMode")) return "active";
|
return this.get("content.name").toLowerCase().replace(' ','-');
|
||||||
return "";
|
}.property('content.name'),
|
||||||
}.property("content.name", "controller.filterMode"),
|
|
||||||
|
|
||||||
hidden: Em.computed.not('content.visible'),
|
// active: function() {
|
||||||
|
// return (this.get("contentNameSlug") === this.get("controller.filterMode"));
|
||||||
countChanged: function(){
|
// }.property("contentNameSlug", "controller.filterMode"),
|
||||||
this.rerender();
|
|
||||||
}.observes('count'),
|
|
||||||
|
|
||||||
name: function() {
|
name: function() {
|
||||||
var categoryName, extra, name;
|
var categoryName, extra, name;
|
||||||
|
@ -10,6 +10,17 @@ Discourse.PostMenuView = Discourse.View.extend({
|
|||||||
tagName: 'section',
|
tagName: 'section',
|
||||||
classNames: ['post-menu-area', 'clearfix'],
|
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) {
|
render: function(buffer) {
|
||||||
var post = this.get('post');
|
var post = this.get('post');
|
||||||
buffer.push("<nav class='post-controls'>");
|
buffer.push("<nav class='post-controls'>");
|
||||||
@ -35,12 +46,6 @@ Discourse.PostMenuView = Discourse.View.extend({
|
|||||||
handler.call(this);
|
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
|
// Replies Button
|
||||||
renderReplies: function(post, buffer) {
|
renderReplies: function(post, buffer) {
|
||||||
if (!post.get('showRepliesBelow')) return;
|
if (!post.get('showRepliesBelow')) return;
|
||||||
|
@ -2,12 +2,10 @@
|
|||||||
|
|
||||||
Discourse.RawDivView = Ember.View.extend({
|
Discourse.RawDivView = Ember.View.extend({
|
||||||
|
|
||||||
|
shouldRerender: Discourse.View.renderIfChanged('content'),
|
||||||
|
|
||||||
render: function(buffer) {
|
render: function(buffer) {
|
||||||
buffer.push(this.get('content'));
|
buffer.push(this.get('content'));
|
||||||
},
|
}
|
||||||
|
|
||||||
contentChanged: function() {
|
|
||||||
this.rerender();
|
|
||||||
}.observes('content')
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -11,9 +11,7 @@ Discourse.TopicClosingView = Discourse.View.extend({
|
|||||||
elementId: 'topic-closing-info',
|
elementId: 'topic-closing-info',
|
||||||
delayedRerender: null,
|
delayedRerender: null,
|
||||||
|
|
||||||
contentChanged: function() {
|
shouldRerender: Discourse.View.renderIfChanged('topic.details.auto_close_at'),
|
||||||
this.rerender();
|
|
||||||
}.observes('topic.details.auto_close_at'),
|
|
||||||
|
|
||||||
render: function(buffer) {
|
render: function(buffer) {
|
||||||
if (!this.present('topic.details.auto_close_at')) return;
|
if (!this.present('topic.details.auto_close_at')) return;
|
||||||
@ -49,12 +47,12 @@ Discourse.TopicClosingView = Discourse.View.extend({
|
|||||||
buffer.push('</h3>');
|
buffer.push('</h3>');
|
||||||
|
|
||||||
// TODO Sam: concerned this can cause a heavy rerender loop
|
// 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() {
|
willDestroyElement: function() {
|
||||||
if( this.delayedRerender ) {
|
if( this.delayedRerender ) {
|
||||||
Em.run.cancel(this.delayedRerender);
|
Em.run.cancel(this.get('delayedRerender'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -17,9 +17,7 @@ Discourse.TopicStatusView = Discourse.View.extend({
|
|||||||
return false;
|
return false;
|
||||||
}.property('topic.closed', 'topic.pinned', 'topic.visible'),
|
}.property('topic.closed', 'topic.pinned', 'topic.visible'),
|
||||||
|
|
||||||
statusChanged: function() {
|
shouldRerender: Discourse.View.renderIfChanged('topic.closed', 'topic.pinned', 'topic.visible'),
|
||||||
this.rerender();
|
|
||||||
}.observes('topic.closed', 'topic.pinned', 'topic.visible'),
|
|
||||||
|
|
||||||
renderIcon: function(buffer, name, key) {
|
renderIcon: function(buffer, name, key) {
|
||||||
var title = I18n.t("topic_statuses." + key + ".help");
|
var title = I18n.t("topic_statuses." + key + ".help");
|
||||||
@ -27,8 +25,7 @@ Discourse.TopicStatusView = Discourse.View.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
render: function(buffer) {
|
render: function(buffer) {
|
||||||
|
if (!this.get('hasDisplayableStatus')) { return; }
|
||||||
if (!this.get('hasDisplayableStatus')) return;
|
|
||||||
|
|
||||||
// Allow a plugin to add a custom icon to a topic
|
// Allow a plugin to add a custom icon to a topic
|
||||||
this.trigger('addCustomIcon', buffer);
|
this.trigger('addCustomIcon', buffer);
|
||||||
|
@ -26,12 +26,10 @@ Discourse.TopicSummaryView = Discourse.ContainerView.extend({
|
|||||||
return allLinks.slice(0, Discourse.TopicSummaryView.LINKS_SHOWN);
|
return allLinks.slice(0, Discourse.TopicSummaryView.LINKS_SHOWN);
|
||||||
}.property('topic.details.links', 'allLinksShown'),
|
}.property('topic.details.links', 'allLinksShown'),
|
||||||
|
|
||||||
newPostCreated: function() {
|
shouldRerender: Discourse.View.renderIfChanged('topic.posts_count'),
|
||||||
this.rerender();
|
|
||||||
}.observes('topic.posts_count'),
|
|
||||||
|
|
||||||
hidden: function() {
|
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') === 'private_message') return false;
|
||||||
if (this.get('controller.content.archetype') !== 'regular') return true;
|
if (this.get('controller.content.archetype') !== 'regular') return true;
|
||||||
return this.get('controller.content.posts_count') < 2;
|
return this.get('controller.content.posts_count') < 2;
|
||||||
|
@ -11,10 +11,7 @@ Discourse.ActivityFilterView = Discourse.View.extend({
|
|||||||
classNameBindings: ['active'],
|
classNameBindings: ['active'],
|
||||||
|
|
||||||
stream: Em.computed.alias('controller.content'),
|
stream: Em.computed.alias('controller.content'),
|
||||||
|
shouldRerender: Discourse.View.renderIfChanged('count'),
|
||||||
countChanged: function(){
|
|
||||||
this.rerender();
|
|
||||||
}.observes('count'),
|
|
||||||
|
|
||||||
active: function() {
|
active: function() {
|
||||||
var content = this.get('content');
|
var content = this.get('content');
|
||||||
|
@ -26,6 +26,21 @@ Discourse.View.reopenClass({
|
|||||||
Discourse.Utilities.normalizeHash(hash, types);
|
Discourse.Utilities.normalizeHash(hash, types);
|
||||||
return Ember.Handlebars.helpers.view.call(this, helperClass, options);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -40,7 +40,8 @@ class PostSerializer < BasicPostSerializer
|
|||||||
:hidden,
|
:hidden,
|
||||||
:hidden_reason_id,
|
:hidden_reason_id,
|
||||||
:deleted_at,
|
:deleted_at,
|
||||||
:trust_level
|
:trust_level,
|
||||||
|
:deleted_by
|
||||||
|
|
||||||
|
|
||||||
def moderator?
|
def moderator?
|
||||||
@ -72,7 +73,6 @@ class PostSerializer < BasicPostSerializer
|
|||||||
end
|
end
|
||||||
|
|
||||||
def link_counts
|
def link_counts
|
||||||
|
|
||||||
return @single_post_link_counts if @single_post_link_counts.present?
|
return @single_post_link_counts if @single_post_link_counts.present?
|
||||||
|
|
||||||
# TODO: This could be better, just porting the old one over
|
# TODO: This could be better, just porting the old one over
|
||||||
|
Reference in New Issue
Block a user