Skip to content

Commit

Permalink
Replace posts pagination with 'load more'
Browse files Browse the repository at this point in the history
- Replace posts pagination with load more
- Fix broken </pagination> close tags
- Increase posts page size to 20
- Update post list after delete/status change without reloading
- Add 'loading' button when posts are loading
  • Loading branch information
rjmackay committed Nov 17, 2016
1 parent ec47ea9 commit 94702de
Show file tree
Hide file tree
Showing 8 changed files with 144 additions and 61 deletions.
2 changes: 2 additions & 0 deletions app/common/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
"forbidden": "Sorry, you're not allowed to do that.",
"get_notifications" : "Get notifications",
"turn_off_notifications" : "Turn off notifications",
"load_more": "Load more",
"loading": "Loading",
"more" : "More",
"cancel" : "Cancel",
"save" : "Save",
Expand Down
2 changes: 1 addition & 1 deletion app/main/posts/detail/post-messages.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ <h2 class="listing-item-title">
<div class="listing-item" ng-show="totalItems > itemsPerPage">
<div class="listing-item-primary">

<uib-pagination ng-model="currentPage" items-per-page="itemsPerPage" total-items="totalItems" ng-change="pageChanged()" max-size="5" rotate="false"></pagination>
<uib-pagination ng-model="currentPage" items-per-page="itemsPerPage" total-items="totalItems" ng-change="pageChanged()" max-size="5" rotate="false"></uib-pagination>

</div>
</div>
Expand Down
108 changes: 72 additions & 36 deletions app/main/posts/views/post-view-list.directive.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,18 @@ function PostListController(
$scope.currentPage = 1;
$scope.selectedPosts = [];
$scope.itemsPerPageOptions = [10, 20, 50];
$scope.itemsPerPage = $scope.itemsPerPageOptions[0];
$scope.itemsPerPage = $scope.itemsPerPageOptions[1];
// until we have the correct total_count value from backend request:
$scope.totalItems = $scope.itemsPerPage;
$scope.posts = [];
$scope.groupedPosts = {};

$scope.deletePosts = deletePosts;
$scope.hasFilters = hasFilters;
$scope.itemsPerPageChanged = itemsPerPageChanged;
$scope.userHasBulkActionPermissions = userHasBulkActionPermissions;
$scope.pageChanged = getPostsForPagination;
$scope.statuses = PostActionsService.getStatuses();
$scope.changeStatus = changeStatus;
$scope.loadMore = loadMore;

activate();

Expand All @@ -62,20 +63,30 @@ function PostListController(
return $scope.filters;
}, function (newValue, oldValue) {
if (newValue !== oldValue) {
getPostsForPagination();
resetPosts();
getPosts();
}
}, true);

function activate() {
// Initial load
getPostsForPagination();
resetPosts();
getPosts();

$scope.$watch('selectedPosts.length', function () {
$scope.$emit('post:list:selected', $scope.selectedPosts);
});
}

function getPostsForPagination(query) {
function resetPosts() {
$scope.posts = [];
$scope.groupedPosts = {};
$scope.totalItems = $scope.itemsPerPage;
$scope.currentPage = 1;
$scope.selectedPosts = [];
}

function getPosts(query) {
query = query || PostFilters.getQueryParams($scope.filters);
var postQuery = _.extend({}, query, {
offset: ($scope.currentPage - 1) * $scope.itemsPerPage,
Expand All @@ -84,37 +95,46 @@ function PostListController(

$scope.isLoading = true;
PostEndpoint.query(postQuery).$promise.then(function (postsResponse) {
if (postsResponse.count === 0 && !PostFilters.hasFilters($scope.filters)) {
PostViewService.showNoPostsSlider();
}
$scope.posts = postsResponse.results;
var now = moment(),
yesterday = moment().subtract(1, 'days');

$scope.groupedPosts = _.groupBy(postsResponse.results, function (post) {
var postDate = moment(post.post_date);
if (now.isSame(postDate, 'd')) {
return $translate.instant('nav.today');
} else if (yesterday.isSame(postDate, 'd')) {
return $translate.instant('nav.yesterday');
// Add posts to full set of posts
// @todo figure out if we can store these more efficiently
Array.prototype.push.apply($scope.posts, postsResponse.results);

// Merge grouped posts into existing groups
angular.forEach(groupPosts(postsResponse.results), function (posts, group) {
if (angular.isArray($scope.groupedPosts[group])) {
Array.prototype.push.apply($scope.groupedPosts[group], posts);
} else {
return postDate.fromNow();
$scope.groupedPosts[group] = posts;
}
});

$scope.totalItems = postsResponse.total_count;
$scope.isLoading = false;

if ($scope.posts.count === 0 && !PostFilters.hasFilters($scope.filters)) {
PostViewService.showNoPostsSlider();
}
});
}

function groupPosts(posts) {
var now = moment(),
yesterday = moment().subtract(1, 'days');

return _.groupBy(posts, function (post) {
var postDate = moment(post.post_date);
if (now.isSame(postDate, 'd')) {
return $translate.instant('nav.today');
} else if (yesterday.isSame(postDate, 'd')) {
return $translate.instant('nav.yesterday');
} else {
return postDate.fromNow();
}
});
}

function deletePosts() {
Notify.confirmDelete('notify.post.bulk_destroy_confirm', { count: $scope.selectedPosts.length }).then(function () {
var handleDeleteErrors = function (errorResponse) {
Notify.apiErrors(errorResponse);
},
handleDeleteSuccess = function () {
Notify.notify('notify.post.destroy_success_bulk');
};

// ask server to delete selected posts
// and refetch posts from server
var deletePostsPromises = _.map(
Expand All @@ -124,7 +144,24 @@ function PostListController(
return PostEndpoint.delete({ id: postId }).$promise;
});
$q.all(deletePostsPromises).then(handleDeleteSuccess, handleDeleteErrors)
.finally(getPostsForPagination);
;

function handleDeleteErrors(errorResponse) {
Notify.apiErrors(errorResponse);
}
function handleDeleteSuccess(deleted) {
Notify.notify('notify.post.destroy_success_bulk');
// Remove deleted posts from state
var deletedIds = _.pluck(deleted, 'id');
angular.forEach($scope.groupedPosts, function (posts, group) {
$scope.groupedPosts[group] = _.reject(posts, function (post) {
return _.contains(deletedIds, post.id);
});
});
$scope.posts = _.reject($scope.posts, function (post) {
return _.contains(deletedIds, post.id);
});
}
});
}

Expand All @@ -137,7 +174,7 @@ function PostListController(

var updateStatusPromises = _.map(selectedPosts, function (post) {
post.status = status;
$scope.selectedPosts = _.without($scope.selectedPosts, post.id);
// $scope.selectedPosts = _.without($scope.selectedPosts, post.id);
return PostEndpoint.update(post).$promise;
});

Expand All @@ -146,12 +183,7 @@ function PostListController(
}, function (errorResponse) {
Notify.apiErrors(errorResponse);
})
.finally(getPostsForPagination);
}

function itemsPerPageChanged(count) {
$scope.itemsPerPage = count;
getPostsForPagination();
;
}

// @todo reconsider: show this for ALL logged in users??
Expand All @@ -165,5 +197,9 @@ function PostListController(
return PostFilters.hasFilters($scope.filters);
}

$scope.open = true;
function loadMore() {
// Increment page
$scope.currentPage++;
getPosts();
}
}
23 changes: 17 additions & 6 deletions app/main/posts/views/post-view-list.html
Original file line number Diff line number Diff line change
Expand Up @@ -65,18 +65,29 @@ <h2 class="listing-heading-section" ng-repeat-start="(timeAgo, posts) in grouped

</listing-toolbar>

<!-- todo: check style -->
<div class="list-item" ng-if="posts.length == 0 && hasFilters()">
<div class="listing-item" ng-if="posts.length == 0 && hasFilters() && !isLoading">
<h4 translate>post.no_search_results</h5>
</div>

<!-- todo: check style -->
<div class="list-item" ng-if="posts.length == 0 && !hasFilters()">
<div class="listing-item" ng-if="posts.length == 0 && !hasFilters() && !isLoading">
<h4 translate>post.no_posts_yet</h5>
</div>

</div> <!-- /.listing.timeline -->
<div class="listing-item" ng-if="posts.length > 0 || isLoading">
<div class="listing-item-primary">
<button ng-disabled="isLoading" ng-hide="isLoading" class="button-gamma button-flat" ng-click="loadMore()" translate="app.load_more">Load more
</button>
<button type="button" class="button-gamma button-flat" ng-show="isLoading">
<div class="loading">
<div class="line"></div>
<div class="line"></div>
<div class="line"></div>
</div>
<span class="button-label" translate="app.loading">Loading</span>
</button>
</div>
</div>

<uib-pagination ng-model="currentPage" total-items="totalItems" ng-change="pageChanged()" max-size="5" rotate="false"></pagination>
</div> <!-- /.listing.timeline -->

</div>
2 changes: 1 addition & 1 deletion app/settings/categories/categories.html
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ <h2 class="listing-item-title"><a ng-href="/settings/categories/{{category.id}}"
</listing-toolbar>
</div>

<uib-pagination ng-model="currentPage" items-per-page="itemsPerPage" total-items="totalItems" ng-change="pageChanged()" max-size="5" boundary-links="false" rotate="false"></pagination>
<uib-pagination ng-model="currentPage" items-per-page="itemsPerPage" total-items="totalItems" ng-change="pageChanged()" max-size="5" boundary-links="false" rotate="false"></uib-pagination>

</div>

Expand Down
2 changes: 1 addition & 1 deletion app/settings/roles/roles.html
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ <h2 class="listing-item-title"><a ng-href="/settings/roles/{{role.id}}">{{role.d
</div>
</div>

<uib-pagination ng-model="currentPage" items-per-page="itemsPerPage" total-items="totalItems" ng-change="pageChanged()" max-size="5" boundary-links="false" rotate="false"></pagination>
<uib-pagination ng-model="currentPage" items-per-page="itemsPerPage" total-items="totalItems" ng-change="pageChanged()" max-size="5" boundary-links="false" rotate="false"></uib-pagination>

</div>
</main>
Expand Down
2 changes: 1 addition & 1 deletion app/settings/users/users.html
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ <h2 class="listing-item-title"><a ng-href="/settings/users/{{user.id}}">{{user.r
</listing-toolbar>
</div>

<uib-pagination ng-model="currentPage" items-per-page="itemsPerPage" total-items="totalItems" ng-change="pageChanged()" max-size="5" boundary-links="false" rotate="false"></pagination>
<uib-pagination ng-model="currentPage" items-per-page="itemsPerPage" total-items="totalItems" ng-change="pageChanged()" max-size="5" boundary-links="false" rotate="false"></uib-pagination>

</div>
</main>
Expand Down
64 changes: 49 additions & 15 deletions test/unit/main/post/views/post-view-list.directive.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,49 @@ describe('post view list directive', function () {
return function () {};
})
.value('PostEntity', {})
.value('moment', function () {
.value('moment', require('moment'))
.service('PostEndpoint', function () {
return {
subtract : function () {
return this;
},
fromNow : function () {
return '';
},
isSame : function () {
return true;
query: function (params) {
var data;
if (params.offset === 20) {
data = {
results : [
{ id: 1, post_date: '2016-01-03'},
{ id: 2, post_date: '2016-02-03'},
{ id: 3, post_date: '2016-03-03'}
]
};
} else {
data = {
results : [
{ id: 10, post_date: '2016-03-03', 'allowed_privileges': ['update']},
{ id: 11, post_date: '2016-03-04'}
]
};
}

return {
'$promise' : {
then: function (cb) {
cb(data);
}
}
};
}
};
});

// Mock current date
jasmine.clock().mockDate(new Date(2016, 9, 4));

angular.mock.module('testApp');
});


afterEach(function () {
// Restore clock, otherwise test timing breaks
jasmine.clock().mockDate();
});

beforeEach(angular.mock.inject(function (_$rootScope_, $compile, _Notify_, _PostFilters_) {
$rootScope = _$rootScope_;
Expand All @@ -56,11 +81,6 @@ describe('post view list directive', function () {
expect(isolateScope.currentPage).toEqual(1);
});

it('should update the number of items per page', function () {
isolateScope.itemsPerPageChanged(1);
expect(isolateScope.itemsPerPage).toEqual(1);
});

it('should check if the user has bulk action permissions', function () {
var result = isolateScope.userHasBulkActionPermissions();
expect(result).toBe(true);
Expand Down Expand Up @@ -89,4 +109,18 @@ describe('post view list directive', function () {
$rootScope.$digest();
expect(Notify.confirmDelete).toHaveBeenCalled();
});

it('should append new posts to groups', function () {
expect(isolateScope.groupedPosts['7 months ago'].length).toEqual(2);
expect(isolateScope.groupedPosts['8 months ago']).toBeUndefined();
expect(isolateScope.groupedPosts['9 months ago']).toBeUndefined();
expect(isolateScope.posts.length).toEqual(2);

isolateScope.loadMore();

expect(isolateScope.groupedPosts['7 months ago'].length).toEqual(3);
expect(isolateScope.groupedPosts['8 months ago'].length).toEqual(1);
expect(isolateScope.groupedPosts['9 months ago'].length).toEqual(1);
expect(isolateScope.posts.length).toEqual(5);
});
});

0 comments on commit 94702de

Please sign in to comment.