Make post types/components more flexible

This commit is contained in:
Toby Zerner 2015-02-08 15:56:44 +10:30
parent 59964e3b22
commit 7863f0114c
12 changed files with 203 additions and 108 deletions

View File

@ -0,0 +1,149 @@
import Ember from 'ember';
import TaggedArray from '../../utils/tagged-array';
import ActionButton from '../ui/controls/action-button';
import ComposerEdit from '../discussions/composer-edit';
import AlertMessage from '../alert-message';
import humanTime from '../../utils/human-time';
var precompileTemplate = Ember.Handlebars.compile;
// @todo extend a base post class
export default Ember.Component.extend({
tagName: 'article',
layoutName: 'components/discussions/post-comment',
editDescription: function() {
return 'Edited by '+this.get('post.editUser.username')+' '+humanTime(this.get('post.editTime'));
}.property('post.editTime', 'post.editUser'),
post: Ember.computed.alias('content'),
classNames: ['post'],
classNameBindings: ['post.deleted', 'post.edited'],
didInsertElement: function() {
var $this = this.$();
$this.css({opacity: 0});
setTimeout(function() {
$this.animate({opacity: 1}, 'fast');
}, 100);
this.set('controls', TaggedArray.create());
this.trigger('populateControls', this.get('controls'));
this.set('header', TaggedArray.create());
this.trigger('populateHeader', this.get('header'));
},
populateControlsDefault: function(controls) {
if (this.get('post.deleted')) {
this.addControl('restore', 'Restore', 'reply', 'canEdit');
this.addControl('delete', 'Delete', 'times', 'canDelete');
} else {
this.addControl('edit', 'Edit', 'pencil', 'canEdit');
this.addControl('hide', 'Delete', 'times', 'canEdit');
}
}.on('populateControls'),
populateHeaderDefault: function(header) {
header.pushObjectWithTag(Ember.Component.create({
tagName: 'h3',
classNames: ['user'],
layout: precompileTemplate('{{#link-to "user" post.user}}{{user-avatar post.user}} {{post.user.username}}{{/link-to}}'),
post: this.get('post')
}));
header.pushObjectWithTag(Ember.Component.create({
tagName: 'li',
layout: precompileTemplate('{{#link-to "discussion" post.discussion (query-params start=post.number) class="time"}}{{human-time post.time}}{{/link-to}}'),
post: this.get('post')
}));
header.pushObjectWithTag(Ember.Component.extend({
tagName: 'li',
hideItem: Ember.computed.not('parent.post.isEdited'),
layout: precompileTemplate('<span class="post-edited" {{bind-attr title=parent.editDescription}}>{{fa-icon "pencil"}}</span>'),
parent: this,
didInsertElement: function() {
this.$('.post-edited').tooltip();
},
updateTooltip: function() {
Ember.run.scheduleOnce('afterRender', this, function() {
this.$('.post-edited').tooltip('fixTitle');
});
}.observes('parent.editDescription')
}).create());
}.on('populateHeader'),
addControl: function(tag, label, icon, permissionAttribute) {
if (permissionAttribute && !this.get('post').get(permissionAttribute)) {
return;
}
var self = this;
var action = function() {
self.get('controller').send(tag);
};
var item = ActionButton.create({label: label, icon: icon, action: action});
this.get('controls').pushObjectWithTag(item, tag);
},
savePost: function(post, data) {
var controller = this;
var composer = this.get('composer');
composer.set('content.loading', true);
this.get('alerts').send('clearAlerts');
post.set('content', data.content);
return post.save().then(function(post) {
composer.send('hide');
},
function(reason) {
var errors = reason.errors;
for (var i in reason.errors) {
var message = AlertMessage.create({
type: 'warning',
message: reason.errors[i]
});
controller.get('alerts').send('alert', message);
}
})
.finally(function() {
composer.set('content.loading', false);
});
},
actions: {
renderControls: function() {
this.set('renderControls', this.get('controls'));
// if (!this.get('controls.length')) {
// this.get('controls').pushObject(Ember.Component.create({tagName: 'li', classNames: ['dropdown-header'], layout: Ember.Handlebars.compile('No actions available')}));
// }
},
edit: function() {
var component = this;
var post = this.get('post');
var composer = this.get('composer');
// If the composer is already set up for this post, then we
// don't need to change its content - we can just show it.
if (!(composer.get('content') instanceof ComposerEdit) || composer.get('content.post') !== post) {
composer.switchContent(ComposerEdit.create({
user: post.get('user'),
post: post,
submit: function(data) {
component.savePost(post, data);
}
}));
}
composer.send('show');
}
}
});

View File

@ -1,8 +0,0 @@
import Ember from 'ember';
export default Ember.Component.extend({
tagName: 'article',
layoutName: 'components/discussions/post-content-comment',
editDescription: ''
});

View File

@ -1,62 +0,0 @@
import Ember from 'ember';
// import TaggedArray from '../../utils/tagged-array';
// import ActionButton from '../ui/controls/action-button';
export default Ember.Component.extend({
tagName: 'article',
layoutName: 'components/discussions/post-wrapper',
// controls: null,
post: Ember.computed.alias('content'),
contentComponent: function() {
return 'discussions/post-content-'+this.get('post.type');
}.property('post.type'),
classNames: ['post'],
classNameBindings: ['post.deleted', 'post.edited'],
// construct: function() {
// // this.set('controls', Menu.create());
// // var post = this.get('post');
// // if (post.get('deleted')) {
// // this.addControl('restore', 'Restore', 'reply', 'canEdit');
// // this.addControl('delete', 'Delete', 'times', 'canDelete');
// // } else {
// // if (post.get('type') == 'comment') {
// // this.addControl('edit', 'Edit', 'pencil', 'canEdit');
// // this.addControl('hide', 'Delete', 'times', 'canEdit');
// // } else {
// // this.addControl('delete', 'Delete', 'times', 'canDelete');
// // }
// // }
// }.on('init'),
didInsertElement: function() {
var $this = this.$();
$this.css({opacity: 0});
setTimeout(function() {
$this.animate({opacity: 1}, 'fast');
}, 100);
},
// addControl: function(tag, title, icon, permissionAttribute) {
// if (permissionAttribute && ! this.get('post').get(permissionAttribute)) {
// return;
// }
// var self = this;
// var action = function(post) {
// self.get('controller').send(actionName, post);
// };
// var item = MenuItem.extend({title: title, icon: icon, action: action});
// this.get('controls').addItem(tag, item);
// }
});

View File

@ -12,7 +12,7 @@ export default Ember.Component.extend({
}
var listItems = [];
this.get('items').forEach(function(item) {
if (item.tagName !== 'li') {
if (item.get('tagName') !== 'li') {
item = ComponentItem.extend({component: item});
}
listItems.push(item);

View File

@ -168,11 +168,17 @@ export default Ember.ArrayProxy.extend(Ember.Evented, {
},
makeItem: function(indexStart, indexEnd, post) {
return Ember.Object.create({
var item = Ember.Object.create({
indexStart: indexStart,
indexEnd: indexEnd,
content: post
indexEnd: indexEnd
});
if (post) {
item.setProperties({
content: post,
component: 'discussions/post-'+post.get('type')
});
}
return item;
},
findNearestTo: function(index, property) {

View File

@ -0,0 +1,24 @@
{{#if controls}}
{{ui/controls/dropdown-button
items=renderControls
class="contextual-controls"
buttonClass="btn btn-default btn-icon btn-sm btn-naked"
buttonClick="renderControls"
menuClass="pull-right"}}
{{/if}}
{{#if post.isDeleted}}
{{fa-icon "trash-o" class="post-icon"}}
{{/if}}
<header class="post-header">
{{ui/controls/item-list items=header}}
</header>
<div class="post-body">
{{{post.contentHtml}}}
</div>
<aside class="post-meta">
{{ui/controls/item-list items=meta}}
</aside>

View File

@ -1,23 +0,0 @@
{{#if post.deleteTime}}
{{fa-icon "trash-o" class="post-icon"}}
{{/if}}
<header class="post-header">
<h3 class="user">
{{#link-to "user" post.user}}{{user-avatar post.user}} {{post.user.username}}{{/link-to}}
</h3>
{{#link-to "discussion" post.discussion (query-params start=post.number) class="time"}}
{{human-time post.time}}
{{/link-to}}
{{#if post.editTime}}
<span class="post-edited" {{bind-attr title=editDescription}}>{{fa-icon "pencil"}}</span>
{{/if}}
</header>
<div class="post-body">
{{{post.contentHtml}}}
</div>
<aside class="post-meta">
{{ui/controls/item-list items=meta}}
</aside>

View File

@ -1,8 +0,0 @@
{{ui/controls/dropdown-button
items=controls
class="contextual-controls"
buttonClass="btn btn-default btn-icon btn-sm btn-naked"
buttonClick="populateControls"
menuClass="pull-right"}}
{{dynamic-component type=contentComponent post=post}}

View File

@ -1,7 +1,7 @@
{{#each item in stream}}
{{#discussions/stream-item item=item stream=stream loadRange="loadRange"}}
{{#if item.content}}
{{dynamic-component type=component content=item.content}}
{{dynamic-component type=item.component content=item.content}}
{{/if}}
{{/discussions/stream-item}}
{{/each}}
{{/each}}

View File

@ -1,3 +1,5 @@
{{#each item in listItems}}
{{view item}}
{{#unless item.hideItem}}
{{view item}}
{{/unless}}
{{/each}}

View File

@ -110,4 +110,19 @@ class Post extends Entity
throw new PermissionDeniedException;
}
}
public function newFromBuilder($attributes = [])
{
if (!empty($attributes->type)) {
$class = 'Flarum\Core\Posts\\'.ucfirst($attributes->type).'Post';
if (class_exists($class)) {
$instance = new $class;
$instance->exists = true;
$instance->setRawAttributes((array) $attributes, true);
return $instance;
}
}
return parent::newFromBuilder($attributes);
}
}