diff --git a/ember/app/components/discussions/composer-edit.js b/ember/app/components/discussions/composer-edit.js new file mode 100644 index 000000000..d738e67c0 --- /dev/null +++ b/ember/app/components/discussions/composer-edit.js @@ -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 {{discussion.title}}'), + 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(); + } + } + } +}); diff --git a/ember/app/models/discussion.js b/ember/app/models/discussion.js index 8d9f6da3d..468c6ae46 100644 --- a/ember/app/models/discussion.js +++ b/ember/app/models/discussion.js @@ -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'), diff --git a/ember/app/models/post-stream.js b/ember/app/models/post-stream.js index c759cbe90..7c7233072 100644 --- a/ember/app/models/post-stream.js +++ b/ember/app/models/post-stream.js @@ -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. diff --git a/ember/app/models/post.js b/ember/app/models/post.js index f90ccd23d..2f5c97b8c 100644 --- a/ember/app/models/post.js +++ b/ember/app/models/post.js @@ -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') diff --git a/ember/app/routes/discussion.js b/ember/app/routes/discussion.js index a6a901290..04ae5f168 100644 --- a/ember/app/routes/discussion.js +++ b/ember/app/routes/discussion.js @@ -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() { diff --git a/ember/app/views/composer.js b/ember/app/views/composer.js index 5b482eb55..cbeed07b1 100644 --- a/ember/app/views/composer.js +++ b/ember/app/views/composer.js @@ -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); diff --git a/src/Flarum/Api/Serializers/PostSerializer.php b/src/Flarum/Api/Serializers/PostSerializer.php index fa23f901c..6a297d23f 100644 --- a/src/Flarum/Api/Serializers/PostSerializer.php +++ b/src/Flarum/Api/Serializers/PostSerializer.php @@ -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 ?: '
'.nl2br(htmlspecialchars(trim($post->content))).'
'; + 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'), diff --git a/src/Flarum/Core/Posts/CommentPost.php b/src/Flarum/Core/Posts/CommentPost.php index 32999f863..fdff4a88b 100755 --- a/src/Flarum/Core/Posts/CommentPost.php +++ b/src/Flarum/Core/Posts/CommentPost.php @@ -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(); });