diff options
-rw-r--r-- | controller/itemcontroller.php | 10 | ||||
-rw-r--r-- | css/feeds.css | 2 | ||||
-rw-r--r-- | db/itemmapper.php | 6 | ||||
-rw-r--r-- | js/app/app.coffee | 4 | ||||
-rw-r--r-- | js/app/controllers/itemcontroller.coffee | 10 | ||||
-rw-r--r-- | js/app/directives/newsitemscroll.coffee | 8 | ||||
-rw-r--r-- | js/app/services/businesslayer/itembusinesslayer.coffee | 7 | ||||
-rw-r--r-- | js/app/services/models/itemmodel.coffee | 11 | ||||
-rw-r--r-- | js/public/app.js | 45 | ||||
-rw-r--r-- | js/tests/services/businesslayer/itembusinesslayerSpec.coffee | 15 | ||||
-rw-r--r-- | js/tests/services/models/itemmodelSpec.coffee | 9 | ||||
-rw-r--r-- | templates/part.feed.unread.php | 3 | ||||
-rw-r--r-- | templates/part.listfeed.php | 2 | ||||
-rw-r--r-- | templates/part.listfolder.php | 3 | ||||
-rw-r--r-- | tests/unit/controller/ItemControllerTest.php | 5 | ||||
-rw-r--r-- | tests/unit/db/ItemMapperTest.php | 6 |
16 files changed, 116 insertions, 30 deletions
diff --git a/controller/itemcontroller.php b/controller/itemcontroller.php index cfdfc4156..5387f40a2 100644 --- a/controller/itemcontroller.php +++ b/controller/itemcontroller.php @@ -50,7 +50,7 @@ class ItemController extends Controller { */ public function items(){ $userId = $this->api->getUserId(); - $showAll = $this->api->getUserValue($userId, 'showAll') === 'true'; + $showAll = $this->api->getUserValue('showAll') === '1'; $limit = $this->params('limit'); $type = (int) $this->params('type'); @@ -61,12 +61,12 @@ class ItemController extends Controller { if($limit !== null){ $offset = (int) $this->params('offset', 0); - $items = $this->itemBusinessLayer->findAll($id, $type, (int) $limit, $offset, - $showAll, $userId); + $items = $this->itemBusinessLayer->findAll($id, $type, (int) $limit, + $offset, $showAll, $userId); } else { $updatedSince = (int) $this->params('updatedSince'); - $items = $this->itemBusinessLayer->findAllNew($id, $type, $updatedSince, - $showAll, $userId); + $items = $this->itemBusinessLayer->findAllNew($id, $type, + $updatedSince, $showAll, $userId); } $params = array( diff --git a/css/feeds.css b/css/feeds.css index 441b77720..13da15bf8 100644 --- a/css/feeds.css +++ b/css/feeds.css @@ -28,7 +28,7 @@ background-image: url('%appswebroot%/news/img/rss.svg'); } -.unread { +.unread > a { font-weight: bold; } diff --git a/db/itemmapper.php b/db/itemmapper.php index 077614695..bffab6348 100644 --- a/db/itemmapper.php +++ b/db/itemmapper.php @@ -158,7 +158,7 @@ class ItemMapper extends Mapper implements IMapper { $params = array($userId, $id); $sql = 'AND `items`.`feed_id` = ? '; if($offset !== 0){ - $sql .= 'AND `items`.`id` > ? '; + $sql .= 'AND `items`.`id` < ? '; array_push($params, $offset); } $sql .= 'ORDER BY `items`.`id` DESC '; @@ -171,7 +171,7 @@ class ItemMapper extends Mapper implements IMapper { $params = array($userId, $id); $sql = 'AND `feeds`.`folder_id` = ? '; if($offset !== 0){ - $sql .= 'AND `items`.`id` > ? '; + $sql .= 'AND `items`.`id` < ? '; array_push($params, $offset); } $sql .= 'ORDER BY `items`.`id` DESC '; @@ -184,7 +184,7 @@ class ItemMapper extends Mapper implements IMapper { $params = array($userId); $sql = ''; if($offset !== 0){ - $sql .= 'AND `items`.`id` > ? '; + $sql .= 'AND `items`.`id` < ? '; array_push($params, $offset); } $sql .= 'ORDER BY `items`.`id` DESC '; diff --git a/js/app/app.coffee b/js/app/app.coffee index 2042a9ebf..264078b57 100644 --- a/js/app/app.coffee +++ b/js/app/app.coffee @@ -28,6 +28,10 @@ angular.module('News', ['OC', 'ui']).config ($provide) -> scrollTimeout: 500 feedUpdateInterval: 600000 itemBatchSize: 20 + # the autoPageFactor defines how many heights of the box must be left + # before it starts autopaging e.g. if it was 2, then it will start + # to fetch new items if less than the height*2 px is left to scroll + autoPageFactor: 6 angular.module('News').run ['Persistence', 'Config', 'FeedBusinessLayer', diff --git a/js/app/controllers/itemcontroller.coffee b/js/app/controllers/itemcontroller.coffee index 4fd00ec59..0d912b507 100644 --- a/js/app/controllers/itemcontroller.coffee +++ b/js/app/controllers/itemcontroller.coffee @@ -31,6 +31,8 @@ Language) -> constructor: (@_$scope, @_itemBusinessLayer, @_feedModel, @_feedLoading, @_feedBusinessLayer, @_language) -> + @_autoPaging = true + @_$scope.itemBusinessLayer = @_itemBusinessLayer @_$scope.feedBusinessLayer = @_feedBusinessLayer @@ -54,10 +56,14 @@ Language) -> @_$scope.$on 'readItem', (scope, data) => - console.log data @_itemBusinessLayer.setRead(data) - + @_$scope.$on 'autoPage', => + if @_autoPaging + # prevent multiple autopaging requests + @_autoPaging = false + @_itemBusinessLayer.loadNext => + @_autoPaging = true return new ItemController($scope, ItemBusinessLayer, FeedModel, FeedLoading, diff --git a/js/app/directives/newsitemscroll.coffee b/js/app/directives/newsitemscroll.coffee index 5e7e42a3e..619710bf3 100644 --- a/js/app/directives/newsitemscroll.coffee +++ b/js/app/directives/newsitemscroll.coffee @@ -49,14 +49,18 @@ angular.module('News').directive 'newsItemScroll', ['$rootScope', 'Config', offset = $(feedItem).position().top if offset <= -50 id = parseInt($(feedItem).data('id'), 10) - #$rootScope.$broadcast 'readItem', id + $rootScope.$broadcast 'readItem', id else break , Config.MarkReadTimeout - scope.$apply attr.newsItemScroll + # autopaging + tolerance = elm.height() * Config.autoPageFactor + remaining = elm[0].scrollHeight - elm.scrollTop() - tolerance + if remaining <= 0 + $rootScope.$broadcast 'autoPage' ] diff --git a/js/app/services/businesslayer/itembusinesslayer.coffee b/js/app/services/businesslayer/itembusinesslayer.coffee index b93f9035d..0b79cd638 100644 --- a/js/app/services/businesslayer/itembusinesslayer.coffee +++ b/js/app/services/businesslayer/itembusinesslayer.coffee @@ -105,7 +105,12 @@ StarredBusinessLayer) -> return feed.title - loadNext: -> + loadNext: (callback) -> + lowestItemId = @_itemModel.getLowestId() + if angular.isDefined(lowestItemId) + @_persistence.getItems @_activeFeed.getType(), + @_activeFeed.getId(), lowestItemId, + callback diff --git a/js/app/services/models/itemmodel.coffee b/js/app/services/models/itemmodel.coffee index 70b3a9f5c..1b03d65f8 100644 --- a/js/app/services/models/itemmodel.coffee +++ b/js/app/services/models/itemmodel.coffee @@ -109,5 +109,16 @@ angular.module('News').factory 'ItemModel', return 0 + getLowestId: -> + query = new _MinimumQuery('id') + lowestId = @get(query) + + if angular.isDefined(lowestId) + return lowestId.id + else + return 0 + + + return new ItemModel() ]
\ No newline at end of file diff --git a/js/public/app.js b/js/public/app.js index ab3c73783..406007894 100644 --- a/js/public/app.js +++ b/js/public/app.js @@ -42,7 +42,8 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>. markReadTimeout: 500, scrollTimeout: 500, feedUpdateInterval: 600000, - itemBatchSize: 20 + itemBatchSize: 20, + autoPageFactor: 6 }); }); @@ -247,6 +248,8 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>. '$rootScope', 'Config', function($rootScope, Config) { return function(scope, elm, attr) { return elm.bind('scroll', function() { + var remaining, tolerance; + if (scrolling) { scrolling = false; setTimeout(function() { @@ -264,7 +267,8 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>. feedItem = $elems[_i]; offset = $(feedItem).position().top; if (offset <= -50) { - _results.push(id = parseInt($(feedItem).data('id'), 10)); + id = parseInt($(feedItem).data('id'), 10); + _results.push($rootScope.$broadcast('readItem', id)); } else { break; } @@ -272,7 +276,11 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>. return _results; }, Config.MarkReadTimeout); } - return scope.$apply(attr.newsItemScroll); + tolerance = elm.height() * Config.autoPageFactor; + remaining = elm[0].scrollHeight - elm.scrollTop() - tolerance; + if (remaining <= 0) { + return $rootScope.$broadcast('autoPage'); + } } }); }; @@ -444,6 +452,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>. this._feedLoading = _feedLoading; this._feedBusinessLayer = _feedBusinessLayer; this._language = _language; + this._autoPaging = true; this._$scope.itemBusinessLayer = this._itemBusinessLayer; this._$scope.feedBusinessLayer = this._feedBusinessLayer; this._$scope.isLoading = function() { @@ -467,9 +476,16 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>. } }; this._$scope.$on('readItem', function(scope, data) { - console.log(data); return _this._itemBusinessLayer.setRead(data); }); + this._$scope.$on('autoPage', function() { + if (_this._autoPaging) { + _this._autoPaging = false; + return _this._itemBusinessLayer.loadNext(function() { + return _this._autoPaging = true; + }); + } + }); } return ItemController; @@ -1223,7 +1239,14 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>. } }; - ItemBusinessLayer.prototype.loadNext = function() {}; + ItemBusinessLayer.prototype.loadNext = function(callback) { + var lowestItemId; + + lowestItemId = this._itemModel.getLowestId(); + if (angular.isDefined(lowestItemId)) { + return this._persistence.getItems(this._activeFeed.getType(), this._activeFeed.getId(), lowestItemId, callback); + } + }; ItemBusinessLayer.prototype.loadNew = function() {}; @@ -2015,6 +2038,18 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>. } }; + ItemModel.prototype.getLowestId = function() { + var lowestId, query; + + query = new _MinimumQuery('id'); + lowestId = this.get(query); + if (angular.isDefined(lowestId)) { + return lowestId.id; + } else { + return 0; + } + }; + return ItemModel; })(_Model); diff --git a/js/tests/services/businesslayer/itembusinesslayerSpec.coffee b/js/tests/services/businesslayer/itembusinesslayerSpec.coffee index d5333c1f4..5f4744ffa 100644 --- a/js/tests/services/businesslayer/itembusinesslayerSpec.coffee +++ b/js/tests/services/businesslayer/itembusinesslayerSpec.coffee @@ -35,9 +35,9 @@ describe 'ItemBusinessLayer', -> @FeedType, @FeedModel, @StarredBusinessLayer) => @item1 = {id: 5, title: 'hi', unreadCount:134, urlHash: 'a3', folderId: 3} @FeedModel.add(@item1) + @ActiveFeed.handle({type: @FeedType.Feed, id: 3}) - it 'should return all items', => item1 = {id: 6, feedId: 5, guidHash: 'a1'} item2 = {id: 3, feedId: 5, guidHash: 'a2'} @@ -233,3 +233,16 @@ describe 'ItemBusinessLayer', -> expect(@item1.unreadCount).toBe(135) + it 'should load the next items', => + @persistence.getItems = jasmine.createSpy('autopage') + callback = -> + + @ItemModel.add({id: 2, guidHash: 'abc', feedId: 2, status: 16}) + @ItemModel.add({id: 3, guidHash: 'abcd', feedId: 2, status: 16}) + @ItemModel.add({id: 1, guidHash: 'abce', feedId: 2, status: 16}) + @ItemModel.add({id: 6, guidHash: 'abcf', feedId: 2, status: 16}) + + @ItemBusinessLayer.loadNext(callback) + + expect(@persistence.getItems).toHaveBeenCalledWith( + @FeedType.Feed, 3, 1, jasmine.any(Function)) diff --git a/js/tests/services/models/itemmodelSpec.coffee b/js/tests/services/models/itemmodelSpec.coffee index ef6b535c4..4e93609ea 100644 --- a/js/tests/services/models/itemmodelSpec.coffee +++ b/js/tests/services/models/itemmodelSpec.coffee @@ -103,4 +103,11 @@ describe 'ItemModel', -> expect(@ItemModel.getById(3).isStarred()).toBe(false) -
\ No newline at end of file + + it 'should return the lowest id', => + @ItemModel.add({id: 2, guidHash: 'abc', feedId: 2, status: 16}) + @ItemModel.add({id: 3, guidHash: 'abcd', feedId: 2, status: 16}) + @ItemModel.add({id: 1, guidHash: 'abce', feedId: 2, status: 16}) + @ItemModel.add({id: 6, guidHash: 'abcf', feedId: 2, status: 16}) + + expect(@ItemModel.getLowestId()).toBe(1)
\ No newline at end of file diff --git a/templates/part.feed.unread.php b/templates/part.feed.unread.php index 03ec2754d..0c6527af3 100644 --- a/templates/part.feed.unread.php +++ b/templates/part.feed.unread.php @@ -16,7 +16,8 @@ <?php p($l->t('All articles'))?> </a> <span class="utils"> - <span class="unread-counter"> + <span class="unread-counter" + ng-show="subscriptionsBusinessLayer.getUnreadCount() > 0"> {{ subscriptionsBusinessLayer.getUnreadCount() }} </span> <button class="svg action mark-read-icon" diff --git a/templates/part.listfeed.php b/templates/part.listfeed.php index 6ab8751ad..1a56febf2 100644 --- a/templates/part.listfeed.php +++ b/templates/part.listfeed.php @@ -29,7 +29,7 @@ <span class="utils"> <span class="unread-counter" - ng-show="feed.id"> + ng-show="feed.id && feedBusinessLayer.getUnreadCount(feed.id) > 0"> {{ feedBusinessLayer.getUnreadCount(feed.id) }} </span> diff --git a/templates/part.listfolder.php b/templates/part.listfolder.php index 63752521d..4266b4814 100644 --- a/templates/part.listfolder.php +++ b/templates/part.listfolder.php @@ -30,7 +30,8 @@ class="svg action delete-icon" title="<?php p($l->t('Delete folder')); ?>"></button> - <span class="unread-counter"> + <span class="unread-counter" + ng-show="folderBusinessLayer.getUnreadCount(folder.id) > 0"> {{ folderBusinessLayer.getUnreadCount(folder.id) }} </span> diff --git a/tests/unit/controller/ItemControllerTest.php b/tests/unit/controller/ItemControllerTest.php index ca72ee330..953330855 100644 --- a/tests/unit/controller/ItemControllerTest.php +++ b/tests/unit/controller/ItemControllerTest.php @@ -232,9 +232,8 @@ class ItemControllerTest extends ControllerTestUtility { private function itemsApiExpects($id, $type){ $this->api->expects($this->once()) ->method('getUserValue') - ->with($this->equalTo($this->user), - $this->equalTo('showAll')) - ->will($this->returnValue('true')); + ->with($this->equalTo('showAll')) + ->will($this->returnValue('1')); $this->api->expects($this->once()) ->method('getUserId') ->will($this->returnValue($this->user)); diff --git a/tests/unit/db/ItemMapperTest.php b/tests/unit/db/ItemMapperTest.php index 56cfe62ab..a7fead8f0 100644 --- a/tests/unit/db/ItemMapperTest.php +++ b/tests/unit/db/ItemMapperTest.php @@ -175,7 +175,7 @@ class ItemMapperTest extends \OCA\AppFramework\Utility\MapperTestUtility { public function testFindAllFeed(){ $sql = 'AND `items`.`feed_id` = ? ' . - 'AND `items`.`id` > ? ' . + 'AND `items`.`id` < ? ' . 'ORDER BY `items`.`id` DESC '; $sql = $this->makeSelectQueryStatus($sql, $this->status); $params = array($this->user, $this->id, $this->offset); @@ -202,7 +202,7 @@ class ItemMapperTest extends \OCA\AppFramework\Utility\MapperTestUtility { public function testFindAllFolder(){ $sql = 'AND `feeds`.`folder_id` = ? ' . - 'AND `items`.`id` > ? ' . + 'AND `items`.`id` < ? ' . 'ORDER BY `items`.`id` DESC '; $sql = $this->makeSelectQueryStatus($sql, $this->status); $params = array($this->user, $this->id, @@ -229,7 +229,7 @@ class ItemMapperTest extends \OCA\AppFramework\Utility\MapperTestUtility { public function testFindAll(){ - $sql = 'AND `items`.`id` > ? ' . + $sql = 'AND `items`.`id` < ? ' . 'ORDER BY `items`.`id` DESC '; $sql = $this->makeSelectQueryStatus($sql, $this->status); $params = array($this->user, $this->offset); |