mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-26 12:21:36 +03:00
Merge pull request #2834 from appleYaks/infinite-scroll
Infinite Scroll on Posts List
This commit is contained in:
commit
11c5272cb8
65
core/client/controllers/posts.js
Normal file
65
core/client/controllers/posts.js
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import { getRequestErrorMessage } from 'ghost/utils/ajax';
|
||||||
|
|
||||||
|
var PostsController = Ember.ArrayController.extend({
|
||||||
|
// set from PostsRoute
|
||||||
|
paginationSettings: null,
|
||||||
|
|
||||||
|
// holds the next page to load during infinite scroll
|
||||||
|
nextPage: null,
|
||||||
|
|
||||||
|
// indicates whether we're currently loading the next page
|
||||||
|
isLoading: null,
|
||||||
|
|
||||||
|
init: function () {
|
||||||
|
this._super();
|
||||||
|
|
||||||
|
var metadata = this.store.metadataFor('post');
|
||||||
|
this.set('nextPage', metadata.pagination.next);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes an ajax response, concatenates any error messages, then generates an error notification.
|
||||||
|
* @param {jqXHR} response The jQuery ajax reponse object.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
reportLoadError: function (response) {
|
||||||
|
var message = 'A problem was encountered while loading more posts';
|
||||||
|
|
||||||
|
if (response) {
|
||||||
|
// Get message from response
|
||||||
|
message += ': ' + getRequestErrorMessage(response);
|
||||||
|
} else {
|
||||||
|
message += '.';
|
||||||
|
}
|
||||||
|
|
||||||
|
this.notifications.showError(message);
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
/**
|
||||||
|
* Loads the next paginated page of posts into the ember-data store. Will cause the posts list UI to update.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
loadNextPage: function () {
|
||||||
|
var self = this,
|
||||||
|
store = this.get('store'),
|
||||||
|
nextPage = this.get('nextPage'),
|
||||||
|
paginationSettings = this.get('paginationSettings');
|
||||||
|
|
||||||
|
if (nextPage) {
|
||||||
|
this.set('isLoading', true);
|
||||||
|
this.set('paginationSettings.page', nextPage);
|
||||||
|
store.find('post', paginationSettings).then(function () {
|
||||||
|
var metadata = store.metadataFor('post');
|
||||||
|
|
||||||
|
self.set('nextPage', metadata.pagination.next);
|
||||||
|
self.set('isLoading', false);
|
||||||
|
}, function (response) {
|
||||||
|
self.reportLoadError(response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default PostsController;
|
@ -1,11 +1,27 @@
|
|||||||
import styleBody from 'ghost/mixins/style-body';
|
import styleBody from 'ghost/mixins/style-body';
|
||||||
import AuthenticatedRoute from 'ghost/routes/authenticated';
|
import AuthenticatedRoute from 'ghost/routes/authenticated';
|
||||||
|
|
||||||
|
var paginationSettings = {
|
||||||
|
status: 'all',
|
||||||
|
staticPages: 'all',
|
||||||
|
page: 1,
|
||||||
|
limit: 15
|
||||||
|
};
|
||||||
|
|
||||||
var PostsRoute = AuthenticatedRoute.extend(styleBody, {
|
var PostsRoute = AuthenticatedRoute.extend(styleBody, {
|
||||||
classNames: ['manage'],
|
classNames: ['manage'],
|
||||||
|
|
||||||
model: function () {
|
model: function () {
|
||||||
return this.store.find('post', { status: 'all', staticPages: 'all' });
|
// using `.filter` allows the template to auto-update when new models are pulled in from the server.
|
||||||
|
// we just need to 'return true' to allow all models by default.
|
||||||
|
return this.store.filter('post', paginationSettings, function () {
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setupController: function (controller, model) {
|
||||||
|
this._super(controller, model);
|
||||||
|
controller.set('paginationSettings', paginationSettings);
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
export default Ember.Route.extend({
|
export default Ember.Route.extend({
|
||||||
model: function (params) {
|
model: function (params) {
|
||||||
return this.store.find('post', params.post_id);
|
var post = this.modelFor('posts').findBy('id', params.post_id);
|
||||||
|
|
||||||
|
if (!post) {
|
||||||
|
this.transitionTo('posts.index');
|
||||||
|
}
|
||||||
|
|
||||||
|
return post;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
</section>
|
</section>
|
||||||
{{#link-to "new" class="button button-add" title="New Post"}}<span class="hidden">New Post</span>{{/link-to}}
|
{{#link-to "new" class="button button-add" title="New Post"}}<span class="hidden">New Post</span>{{/link-to}}
|
||||||
</header>
|
</header>
|
||||||
<section class="content-list-content">
|
{{#view 'content-list-content-view' tagName="section"}}
|
||||||
<ol class="posts-list">
|
<ol class="posts-list">
|
||||||
{{#each itemController="posts/post" itemView="post-item-view" itemTagName="li"}}
|
{{#each itemController="posts/post" itemView="post-item-view" itemTagName="li"}}
|
||||||
{{!-- @TODO: Restore functionality where 'featured' and 'page' classes are added for proper posts --}}
|
{{!-- @TODO: Restore functionality where 'featured' and 'page' classes are added for proper posts --}}
|
||||||
@ -30,7 +30,7 @@
|
|||||||
{{/link-to}}
|
{{/link-to}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</ol>
|
</ol>
|
||||||
</section>
|
{{/view}}
|
||||||
</section>
|
</section>
|
||||||
<section class="content-preview js-content-preview">
|
<section class="content-preview js-content-preview">
|
||||||
{{outlet}}
|
{{outlet}}
|
||||||
|
@ -1,4 +1,43 @@
|
|||||||
/* global ic */
|
/* global ic */
|
||||||
export default window.ajax = function () {
|
|
||||||
|
var ajax = window.ajax = function () {
|
||||||
return ic.ajax.request.apply(null, arguments);
|
return ic.ajax.request.apply(null, arguments);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Used in API request fail handlers to parse a standard api error
|
||||||
|
// response json for the message to display
|
||||||
|
var getRequestErrorMessage = function (request) {
|
||||||
|
var message,
|
||||||
|
msgDetail;
|
||||||
|
|
||||||
|
// Can't really continue without a request
|
||||||
|
if (!request) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seems like a sensible default
|
||||||
|
message = request.statusText;
|
||||||
|
|
||||||
|
// If a non 200 response
|
||||||
|
if (request.status !== 200) {
|
||||||
|
try {
|
||||||
|
// Try to parse out the error, or default to "Unknown"
|
||||||
|
if (request.responseJSON.errors && Ember.isArray(request.responseJSON.errors)) {
|
||||||
|
|
||||||
|
message = request.responseJSON.errors.map(function (errorItem) {
|
||||||
|
return errorItem.message;
|
||||||
|
}).join('; ');
|
||||||
|
} else {
|
||||||
|
message = request.responseJSON.error || "Unknown Error";
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
msgDetail = request.status ? request.status + " - " + request.statusText : "Server was not available";
|
||||||
|
message = "The server returned an error (" + msgDetail + ").";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return message;
|
||||||
|
};
|
||||||
|
|
||||||
|
export { getRequestErrorMessage, ajax };
|
||||||
|
export default ajax;
|
||||||
|
29
core/client/views/content-list-content-view.js
Normal file
29
core/client/views/content-list-content-view.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
var PostsListView = Ember.View.extend({
|
||||||
|
classNames: ['content-list-content'],
|
||||||
|
|
||||||
|
checkScroll: function (event) {
|
||||||
|
var element = event.target,
|
||||||
|
triggerPoint = 100,
|
||||||
|
controller = this.get('controller'),
|
||||||
|
isLoading = controller.get('isLoading');
|
||||||
|
|
||||||
|
// If we haven't passed our threshold, exit
|
||||||
|
if (isLoading || (element.scrollTop + element.clientHeight + triggerPoint <= element.scrollHeight)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
controller.send('loadNextPage');
|
||||||
|
},
|
||||||
|
|
||||||
|
didInsertElement: function () {
|
||||||
|
var el = this.$();
|
||||||
|
el.bind('scroll', Ember.run.bind(this, this.checkScroll));
|
||||||
|
},
|
||||||
|
|
||||||
|
willDestroyElement: function () {
|
||||||
|
var el = this.$();
|
||||||
|
el.unbind('scroll');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default PostsListView;
|
Loading…
Reference in New Issue
Block a user