import Ember from 'ember'; import PostStream from 'flarum/models/post-stream'; export default Ember.Route.extend({ queryParams: { start: {replace: true} }, discussion: function(id, start) { return this.store.findQueryOne('discussion', id, { include: 'posts', near: start }); }, // When we fetch the discussion from the model hook (i.e. on a fresh page // load), we'll wrap it in an object proxy and set a `loaded` flag to true // so that it won't be reloaded later on. model: function(params) { return this.discussion(params.id, params.start).then(function(discussion) { return Ember.ObjectProxy.create({content: discussion, loaded: true}); }); }, resetController: function(controller) { // Whenever we exit the discussion view, or transition to a different // discussion, we want to reset the query params so that they don't stick. controller.set('start', '1'); controller.set('searchQuery', ''); controller.set('loaded', false); controller.set('stream', null); }, setupController: function(controller, discussion) { controller.set('model', discussion); this.controllerFor('index/index').set('lastDiscussion', discussion); // Set up the post stream object. It needs to know about the discussion // it's representing the posts for, and we also need to inject the Ember // Data store. var stream = PostStream.create({ discussion: discussion, store: this.store }); controller.set('stream', stream); // We need to make sure we have an up-to-date list of the discussion's // post IDs. If we didn't enter this route using the model hook (like if // clicking on a discussion in the index), then we'll reload the model. var promise = discussion.get('loaded') ? Ember.RSVP.resolve(discussion.get('content')) : this.discussion(discussion.get('id'), controller.get('start')); // 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) { controller.set('model', discussion); 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; })); } // 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() { controller.trigger('loaded'); }); } }); }, actions: { queryParamsDidChange: function(params) { // If the ?start param has changed, we want to tell the view to // tell the streamContent component to jump to this start point. // We postpone running this code until the next run loop because // when transitioning directly from one discussion to another, // queryParamsDidChange is fired before the controller is reset. // Thus, controller.loaded would still be true and the // startWasChanged event would be triggered inappropriately. var newStart = parseInt(params.start) || 1; var controller = this.controllerFor('discussion'); var oldStart = parseInt(controller.get('start')); Ember.run.next(function() { if (controller.get('loaded') && newStart !== oldStart) { controller.trigger('startWasChanged', newStart); } }); }, didTransition: function() { // When we transition into a new discussion, we want to hide the // discussions list pane. This means that when the user selects a // different discussion within the pane, the pane will slide away. // We also minimize the composer. this.controllerFor('index') .set('paned', true) .set('paneShowing', false); this.controllerFor('composer').send('minimize'); var application = this.controllerFor('application'); if (!application.get('backButtonTarget')) { application.set('backButtonTarget', this.controllerFor('index')); } } } });