mirror of
https://github.com/flarum/framework.git
synced 2024-11-25 08:00:24 +08:00
Implement post editing
This commit is contained in:
parent
e23192a4e0
commit
b12fcf1b2d
|
@ -0,0 +1,48 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
import TaggedArray from '../../utils/tagged-array';
|
||||
|
||||
var precompileTemplate = Ember.Handlebars.compile;
|
||||
|
||||
export default Ember.Component.extend(Ember.Evented, {
|
||||
layoutName: 'components/discussions/composer-body',
|
||||
|
||||
submitLabel: 'Save Changes',
|
||||
placeholder: '',
|
||||
content: Ember.computed.oneWay('post.content'),
|
||||
originalContent: Ember.computed.oneWay('post.content'),
|
||||
submit: null,
|
||||
loading: false,
|
||||
|
||||
didInsertElement: function() {
|
||||
var controls = TaggedArray.create();
|
||||
this.trigger('populateControls', controls);
|
||||
this.set('controls', controls);
|
||||
},
|
||||
|
||||
populateControls: function(controls) {
|
||||
var title = Ember.Component.create({
|
||||
tagName: 'h3',
|
||||
layout: precompileTemplate('Editing Post #{{component.post.number}} in <em>{{discussion.title}}</em>'),
|
||||
discussion: this.get('post.discussion'),
|
||||
component: this
|
||||
});
|
||||
controls.pushObjectWithTag(title, 'title');
|
||||
},
|
||||
|
||||
actions: {
|
||||
submit: function(content) {
|
||||
this.get('submit')({
|
||||
content: content
|
||||
});
|
||||
},
|
||||
|
||||
willExit: function(abort) {
|
||||
// If the user has typed something, prompt them before exiting
|
||||
// this composer state.
|
||||
if (this.get('content') !== this.get('originalContent') && ! confirm('You have not saved your post. Do you wish to discard your changes?')) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
|
@ -35,6 +35,7 @@ var Discussion = DS.Model.extend({
|
|||
var posts = this.get('posts') || '';
|
||||
return posts.split(',');
|
||||
}.property('posts'),
|
||||
loadedPosts: DS.hasMany('post'),
|
||||
|
||||
readTime: DS.attr('date'),
|
||||
readNumber: DS.attr('number'),
|
||||
|
|
|
@ -42,17 +42,6 @@ export default Ember.ArrayProxy.extend(Ember.Evented, {
|
|||
// stream to a big gap that covers the amount of posts we now have.
|
||||
this.set('ids', ids);
|
||||
this.clear();
|
||||
|
||||
// Look in the store and see if we already have the data for any of
|
||||
// these posts. If we do, we can add them to the stream.
|
||||
var posts = [];
|
||||
var store = this.get('store');
|
||||
ids.forEach(function(id) {
|
||||
if (store.hasRecordForId('post', id)) {
|
||||
posts.pushObject(store.getById('post', id));
|
||||
}
|
||||
});
|
||||
this.addPosts(posts);
|
||||
},
|
||||
|
||||
// Clear the contents of the post stream, resetting it to one big gap.
|
||||
|
|
|
@ -3,22 +3,22 @@ import DS from 'ember-data';
|
|||
|
||||
export default DS.Model.extend({
|
||||
|
||||
discussion: DS.belongsTo('discussion', {inverse: null}),
|
||||
discussion: DS.belongsTo('discussion', {inverse: 'loadedPosts'}),
|
||||
number: DS.attr('number'),
|
||||
|
||||
time: DS.attr('string'),
|
||||
time: DS.attr('date'),
|
||||
user: DS.belongsTo('user'),
|
||||
type: DS.attr('string'),
|
||||
content: DS.attr('string'),
|
||||
contentHtml: DS.attr('string'),
|
||||
|
||||
editTime: DS.attr('string'),
|
||||
editTime: DS.attr('date'),
|
||||
editUser: DS.belongsTo('user'),
|
||||
edited: Ember.computed.notEmpty('editTime'),
|
||||
isEdited: Ember.computed.notEmpty('editTime'),
|
||||
|
||||
deleteTime: DS.attr('string'),
|
||||
deleteTime: DS.attr('date'),
|
||||
deleteUser: DS.belongsTo('user'),
|
||||
deleted: Ember.computed.notEmpty('deleteTime'),
|
||||
isDeleted: Ember.computed.notEmpty('deleteTime'),
|
||||
|
||||
canEdit: DS.attr('boolean'),
|
||||
canDelete: DS.attr('boolean')
|
||||
|
|
|
@ -44,18 +44,41 @@ export default Ember.Route.extend({
|
|||
start: controller.get('start')
|
||||
});
|
||||
|
||||
// Each time we view a discussion we want to reload its posts from
|
||||
// scratch so that we have the most up-to-date data. Also, if we were
|
||||
// to leave them in the store, the stream would try and render them
|
||||
// which has the potential to be slow.
|
||||
this.store.unloadAll('post');
|
||||
|
||||
// When we know we have the post IDs, we can set up the post stream with
|
||||
// them. Then we will tell the view that we have finished loading so that
|
||||
// it can scroll down to the appropriate post.
|
||||
promise.then(function(discussion) {
|
||||
stream.setup(discussion.get('postIds'));
|
||||
var postIds = discussion.get('postIds');
|
||||
stream.setup(postIds);
|
||||
|
||||
// A page of posts will have been returned as linked data by this
|
||||
// request, and automatically loaded into the store. In turn, we
|
||||
// want to load them into the stream. However, since there is no
|
||||
// way to access them directly, we need to retrieve them based on
|
||||
// the requested start number. This code finds the post for that
|
||||
// number, gets its index, slices an array of surrounding post
|
||||
// IDs, and finally adds these posts to the stream.
|
||||
var posts = discussion.get('loadedPosts');
|
||||
var startPost = posts.findBy('number', parseInt(controller.get('start')));
|
||||
if (startPost) {
|
||||
var startIndex = postIds.indexOf(startPost.get('id'));
|
||||
var count = stream.get('postLoadCount');
|
||||
startIndex = Math.max(0, startIndex - count / 2);
|
||||
var loadIds = postIds.slice(startIndex, startIndex + count);
|
||||
stream.addPosts(posts.filter(function(item) {
|
||||
return loadIds.indexOf(item.get('id')) !== -1;
|
||||
}));
|
||||
}
|
||||
|
||||
// Clear the list of post IDs for this discussion (without
|
||||
// dirtying the record), so that next time we load the discussion,
|
||||
// the discussion details and post IDs will be refreshed.
|
||||
controller.store.push('discussion', {id: discussion.get('id'), posts: ''});
|
||||
|
||||
// It's possible for this promise to have resolved but the user
|
||||
// has clicked away to a different discussion. So only if we're
|
||||
// still on the original one, we will tell the view that we're
|
||||
// done loading.
|
||||
if (controller.get('model') === discussion) {
|
||||
controller.set('loaded', true);
|
||||
Ember.run.scheduleOnce('afterRender', function() {
|
||||
|
|
|
@ -227,7 +227,6 @@ export default Ember.View.extend(Ember.Evented, {
|
|||
var deltaPixels = event.data.mouseStart - event.clientY;
|
||||
var height = event.data.heightStart + deltaPixels;
|
||||
view.set('height', height);
|
||||
view.setContentHeight(height);
|
||||
view.updateBodyPadding();
|
||||
|
||||
localStorage.setItem('composerHeight', height);
|
||||
|
|
|
@ -32,6 +32,7 @@ class PostSerializer extends PostBasicSerializer
|
|||
protected function attributes(Post $post)
|
||||
{
|
||||
$attributes = parent::attributes($post);
|
||||
$user = User::current();
|
||||
|
||||
unset($attributes['content']);
|
||||
if ($post->type != 'comment') {
|
||||
|
@ -39,17 +40,19 @@ class PostSerializer extends PostBasicSerializer
|
|||
} else {
|
||||
// @todo move to a formatter class
|
||||
$attributes['contentHtml'] = $post->content_html ?: '<p>'.nl2br(htmlspecialchars(trim($post->content))).'</p>';
|
||||
if ($post->can($user, 'edit')) {
|
||||
$attributes['content'] = $post->content;
|
||||
}
|
||||
}
|
||||
|
||||
if ($post->edit_time) {
|
||||
$attributes['editTime'] = (string) $post->edit_time;
|
||||
$attributes['editTime'] = $post->edit_time->toRFC3339String();
|
||||
}
|
||||
|
||||
if ($post->delete_time) {
|
||||
$attributes['deleteTime'] = (string) $post->delete_time;
|
||||
$attributes['deleteTime'] = $post->delete_time->toRFC3339String();
|
||||
}
|
||||
|
||||
$user = User::current();
|
||||
|
||||
$attributes += [
|
||||
'canEdit' => $post->can($user, 'edit'),
|
||||
|
|
|
@ -14,7 +14,7 @@ class CommentPost extends Post
|
|||
{
|
||||
parent::boot();
|
||||
|
||||
static::saving(function ($post) {
|
||||
static::creating(function ($post) {
|
||||
$post->number = ++$post->discussion->number_index;
|
||||
$post->discussion->save();
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue
Block a user