diff --git a/src/main/java/com/commafeed/backend/dao/FeedEntryService.java b/src/main/java/com/commafeed/backend/dao/FeedEntryService.java index 0945c2c7..506e1a62 100644 --- a/src/main/java/com/commafeed/backend/dao/FeedEntryService.java +++ b/src/main/java/com/commafeed/backend/dao/FeedEntryService.java @@ -39,21 +39,39 @@ public class FeedEntryService extends GenericDAO { em.merge(feed); } - public List getUnreadEntries(Feed feed, User user) { + private TypedQuery unreadQuery(Feed feed, User user) { String query = "select e from FeedEntry e where e.feed=:feed and not exists (select s from FeedEntryStatus s where s.entry = e and s.user =:user and s.read = true)"; TypedQuery typedQuery = em.createQuery(query, FeedEntry.class); typedQuery.setParameter("feed", feed); typedQuery.setParameter("user", user); - return typedQuery.getResultList(); + return typedQuery; } - public List getAllEntries(Feed feed) { + public List getUnreadEntries(Feed feed, User user) { + return unreadQuery(feed, user).getResultList(); + } + + public List getUnreadEntries(Feed feed, User user, int offset, + int limit) { + return unreadQuery(feed, user).setFirstResult(offset) + .setMaxResults(limit).getResultList(); + } + + private TypedQuery allQuery(Feed feed) { String query = "select e from FeedEntry e where e.feed=:feed"; TypedQuery typedQuery = em.createQuery(query, FeedEntry.class); typedQuery.setParameter("feed", feed); - return typedQuery.getResultList(); + return typedQuery; } + public List getAllEntries(Feed feed) { + return allQuery(feed).getResultList(); + } + + public List getAllEntries(Feed feed, int offset, int limit) { + return allQuery(feed).setFirstResult(offset).setMaxResults(limit) + .getResultList(); + } } diff --git a/src/main/java/com/commafeed/backend/feeds/FeedTimer.java b/src/main/java/com/commafeed/backend/feeds/FeedTimer.java index 4fd80281..d5d50a73 100644 --- a/src/main/java/com/commafeed/backend/feeds/FeedTimer.java +++ b/src/main/java/com/commafeed/backend/feeds/FeedTimer.java @@ -50,9 +50,8 @@ public class FeedTimer { feedEntryService .updateEntries(feed.getUrl(), feed.getEntries()); } catch (Exception e) { - log.info( - "Unable to refresh feed " + key + " : " - + e.getMessage(), e); + log.info("Unable to refresh feed " + key + " : " + + e.getMessage()); Feed feed = feeds.get(key); feed.setLastUpdated(Calendar.getInstance().getTime()); diff --git a/src/main/java/com/commafeed/frontend/pages/HomePage.html b/src/main/java/com/commafeed/frontend/pages/HomePage.html index 660cfdd8..24683252 100644 --- a/src/main/java/com/commafeed/frontend/pages/HomePage.html +++ b/src/main/java/com/commafeed/frontend/pages/HomePage.html @@ -39,6 +39,7 @@ + diff --git a/src/main/java/com/commafeed/frontend/rest/resources/EntriesREST.java b/src/main/java/com/commafeed/frontend/rest/resources/EntriesREST.java index eed7f775..252d89cf 100644 --- a/src/main/java/com/commafeed/frontend/rest/resources/EntriesREST.java +++ b/src/main/java/com/commafeed/frontend/rest/resources/EntriesREST.java @@ -5,9 +5,11 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; +import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; +import javax.ws.rs.QueryParam; import org.apache.commons.lang.ObjectUtils; @@ -27,7 +29,9 @@ public class EntriesREST extends AbstractREST { @Path("get/{type}/{id}/{readType}") @GET public Entries getEntries(@PathParam("type") String type, - @PathParam("id") String id, @PathParam("readType") String readType) { + @PathParam("id") String id, @PathParam("readType") String readType, + @DefaultValue("0") @QueryParam("offset") int offset, + @DefaultValue("-1") @QueryParam("limit") int limit) { Entries entries = new Entries(); boolean unreadOnly = "unread".equals(readType); @@ -36,7 +40,8 @@ public class EntriesREST extends AbstractREST { FeedSubscription subscription = feedSubscriptionService.findById( getUser(), Long.valueOf(id)); entries.setName(subscription.getTitle()); - entries.getEntries().addAll(buildEntries(subscription, unreadOnly)); + entries.getEntries().addAll( + buildEntries(subscription, offset, limit, unreadOnly)); } else { FeedCategory feedCategory = "all".equals(id) ? null @@ -48,7 +53,7 @@ public class EntriesREST extends AbstractREST { entries.setName("all".equals(id) ? "All" : feedCategory.getName()); for (FeedSubscription subscription : subscriptions) { entries.getEntries().addAll( - buildEntries(subscription, unreadOnly)); + buildEntries(subscription, offset, limit, unreadOnly)); } } @@ -59,19 +64,27 @@ public class EntriesREST extends AbstractREST { return ObjectUtils.compare(e2.getDate(), e1.getDate()); } }); + + int lastIndex = entries.getEntries().size() + - (entries.getEntries().isEmpty() ? 0 : 1); + int from = Math.min(lastIndex, offset); + int to = limit == -1 ? lastIndex : Math.min(lastIndex, offset + limit); + + List subList = entries.getEntries().subList(from, to); + entries.setEntries(Lists.newArrayList(subList)); return entries; } - private List buildEntries(FeedSubscription subscription, - boolean unreadOnly) { + private List buildEntries(FeedSubscription subscription, int offset, + int limit, boolean unreadOnly) { List feedEntries = null; if (unreadOnly) { feedEntries = feedEntryService.getUnreadEntries( - subscription.getFeed(), getUser()); + subscription.getFeed(), getUser(), offset, limit); } else { - feedEntries = feedEntryService - .getAllEntries(subscription.getFeed()); + feedEntries = feedEntryService.getAllEntries( + subscription.getFeed(), offset, limit); } List entries = Lists.newArrayList(); diff --git a/src/main/webapp/js/controllers.js b/src/main/webapp/js/controllers.js index 4b8e5bd8..44f885f6 100644 --- a/src/main/webapp/js/controllers.js +++ b/src/main/webapp/js/controllers.js @@ -102,15 +102,45 @@ module.controller('FeedListCtrl', function($scope, $routeParams, $http, $scope.refreshList(); }); + + $scope.offset = 0; + $scope.limit = 10; + $scope.busy = false; + $scope.hasMore = true; + $scope.refreshList = function() { if ($scope.settings.readingMode) { $scope.entryList = EntryService.get({ _type : $scope.selectedType, _id : $scope.selectedId, - _readtype : $scope.settings.readingMode + _readtype : $scope.settings.readingMode, + offset : $scope.offset, + limit : 30 }); } }; + + $scope.loadMoreEntries = function() { + if ($scope.busy || !$scope.hasMore) + return; + if (!$scope.settings.readingMode) + return; + $scope.busy = true; + EntryService.get({ + _type : $scope.selectedType, + _id : $scope.selectedId, + _readtype : $scope.settings.readingMode, + offset : $scope.offset, + limit : $scope.limit + }, function(data) { + for ( var i = 0; i < data.entries.length; i++) { + $scope.entryList.entries.push(data.entries[i]); + } + $scope.offset = $scope.offset + data.entries.length; + $scope.busy = false; + $scope.hasMore = data.entries.length == $scope.limit; + }); + } $scope.mark = function(entry, read) { if (entry.read != read) { diff --git a/src/main/webapp/js/main.js b/src/main/webapp/js/main.js index fde46d90..009e1a1b 100644 --- a/src/main/webapp/js/main.js +++ b/src/main/webapp/js/main.js @@ -1,6 +1,6 @@ var app = angular.module('commafeed', [ 'ui', 'ui.bootstrap', 'commafeed.directives', 'commafeed.controllers', 'commafeed.services', - 'ngSanitize', 'ngUpload' ]); + 'ngSanitize', 'ngUpload', 'infinite-scroll' ]); app.config([ '$routeProvider', function($routeProvider) { $routeProvider.when('/feeds/view/:_type/:_id', { diff --git a/src/main/webapp/templates/feeds.html b/src/main/webapp/templates/feeds.html index cc80d1fb..6826a3b8 100644 --- a/src/main/webapp/templates/feeds.html +++ b/src/main/webapp/templates/feeds.html @@ -2,7 +2,7 @@ {{readType}} {{entryList.name}} » - \ No newline at end of file diff --git a/src/main/webapp/vendor/angular-infinite-scroll/ng-infinite-scroll.min.js b/src/main/webapp/vendor/angular-infinite-scroll/ng-infinite-scroll.min.js new file mode 100644 index 00000000..d4410b93 --- /dev/null +++ b/src/main/webapp/vendor/angular-infinite-scroll/ng-infinite-scroll.min.js @@ -0,0 +1,2 @@ +/* ng-infinite-scroll - v1.0.0 - 2013-02-23 */ +var mod;mod=angular.module("infinite-scroll",[]),mod.directive("infiniteScroll",["$rootScope","$window","$timeout",function(i,n,e){return{link:function(t,l,o){var r,c,f,a;return n=angular.element(n),f=0,null!=o.infiniteScrollDistance&&t.$watch(o.infiniteScrollDistance,function(i){return f=parseInt(i,10)}),a=!0,r=!1,null!=o.infiniteScrollDisabled&&t.$watch(o.infiniteScrollDisabled,function(i){return a=!i,a&&r?(r=!1,c()):void 0}),c=function(){var e,c,u,d;return d=n.height()+n.scrollTop(),e=l.offset().top+l.height(),c=e-d,u=n.height()*f>=c,u&&a?i.$$phase?t.$eval(o.infiniteScroll):t.$apply(o.infiniteScroll):u?r=!0:void 0},n.on("scroll",c),t.$on("$destroy",function(){return n.off("scroll",c)}),e(function(){return o.infiniteScrollImmediateCheck?t.$eval(o.infiniteScrollImmediateCheck)?c():void 0:c()},0)}}}]); \ No newline at end of file