summaryrefslogtreecommitdiffstats
path: root/js
diff options
context:
space:
mode:
authorBernhard Posselt <dev@bernhard-posselt.com>2016-04-18 21:18:13 +0200
committerBernhard Posselt <dev@bernhard-posselt.com>2016-04-18 21:18:13 +0200
commitd538613b60ea2c5bff4f8e124ecbb8dedb528aea (patch)
treebc80120a7aa578f47f4c41ab6af16153a9ea1731 /js
parent7867824595842ed8078dadbe247b6df4492e0e78 (diff)
Fix #791
Diffstat (limited to 'js')
-rw-r--r--js/.jshintrc3
-rw-r--r--js/controller/ContentController.js334
-rw-r--r--js/directive/NewsOnActive.js23
-rw-r--r--js/gui/KeyboardShortcuts.js107
-rw-r--r--js/tests/unit/controller/ContentControllerSpec.js22
5 files changed, 269 insertions, 220 deletions
diff --git a/js/.jshintrc b/js/.jshintrc
index 19894c5ea..dedacd590 100644
--- a/js/.jshintrc
+++ b/js/.jshintrc
@@ -48,6 +48,7 @@
"t": true,
"url": true,
"navigator": true,
- "oc_requesttoken": true
+ "oc_requesttoken": true,
+ "_": true
}
}
diff --git a/js/controller/ContentController.js b/js/controller/ContentController.js
index dfee837d5..c63fc65e8 100644
--- a/js/controller/ContentController.js
+++ b/js/controller/ContentController.js
@@ -8,197 +8,219 @@
* @copyright Bernhard Posselt 2014
*/
app.controller('ContentController',
-function (Publisher, FeedResource, ItemResource, SettingsResource, data,
- $route, $routeParams, $location, FEED_TYPE, ITEM_AUTO_PAGE_SIZE, Loading) {
- 'use strict';
-
- ItemResource.clear();
-
- // distribute data to models based on key
- Publisher.publishAll(data);
+ function (Publisher, FeedResource, ItemResource, SettingsResource, data,
+ $route, $routeParams, $location, FEED_TYPE, ITEM_AUTO_PAGE_SIZE,
+ Loading, $filter) {
+ 'use strict';
+ var self = this;
+ ItemResource.clear();
- this.isAutoPagingEnabled = true;
+ // distribute data to models based on key
+ Publisher.publishAll(data);
- // the interface should show a hint if there are not enough items sent so
- // it's assumed that theres nothing to autpage
- if (ItemResource.size() >= ITEM_AUTO_PAGE_SIZE) {
- this.isNothingMoreToAutoPage = false;
- } else {
- this.isNothingMoreToAutoPage = true;
- }
+ this.getFirstItem = function () {
+ var orderFilter = $filter('orderBy');
+ var orderedItems = orderFilter(this.getItems(), this.orderBy());
+ var firstItem = orderedItems[0];
+ if (firstItem === undefined) {
+ return undefined;
+ } else {
+ return firstItem.id;
+ }
+ };
- this.getItems = function () {
- return ItemResource.getAll();
- };
- this.toggleStar = function (itemId) {
- ItemResource.toggleStar(itemId);
- };
+ this.isAutoPagingEnabled = true;
+ // the interface should show a hint if there are not enough items sent
+ // it's assumed that theres nothing to autpage
- this.toggleItem = function (item) {
- // TODO: unittest
- if (this.isCompactView()) {
- item.show = !item.show;
+ if (ItemResource.size() >= ITEM_AUTO_PAGE_SIZE) {
+ this.isNothingMoreToAutoPage = false;
+ } else {
+ this.isNothingMoreToAutoPage = true;
}
- };
- this.isShowAll = function () {
- return SettingsResource.get('showAll');
- };
+ this.getItems = function () {
+ return ItemResource.getAll();
+ };
- this.markRead = function (itemId) {
- var item = ItemResource.get(itemId);
+ this.isItemActive = function (id) {
+ return this.activeItem === id;
+ };
- if (!item.keepUnread && item.unread === true) {
- ItemResource.markItemRead(itemId);
- FeedResource.markItemOfFeedRead(item.feedId);
- }
- };
+ this.setItemActive = function (id) {
+ this.activeItem = id;
+ };
- this.getFeed = function (feedId) {
- return FeedResource.getById(feedId);
- };
+ this.toggleStar = function (itemId) {
+ ItemResource.toggleStar(itemId);
+ };
- this.toggleKeepUnread = function (itemId) {
- var item = ItemResource.get(itemId);
- if (!item.unread) {
- FeedResource.markItemOfFeedUnread(item.feedId);
- ItemResource.markItemRead(itemId, false);
- }
+ this.toggleItem = function (item) {
+ // TODO: unittest
+ if (this.isCompactView()) {
+ item.show = !item.show;
+ }
+ };
- item.keepUnread = !item.keepUnread;
- };
+ this.isShowAll = function () {
+ return SettingsResource.get('showAll');
+ };
- var self = this;
- var getOrdering = function () {
- var ordering = SettingsResource.get('oldestFirst');
+ this.markRead = function (itemId) {
+ var item = ItemResource.get(itemId);
- if (self.isFeed()) {
- var feed = FeedResource.getById($routeParams.id);
- if (feed && feed.ordering === 1) {
- ordering = true;
- } else if (feed && feed.ordering === 2) {
- ordering = false;
+ if (!item.keepUnread && item.unread === true) {
+ ItemResource.markItemRead(itemId);
+ FeedResource.markItemOfFeedRead(item.feedId);
}
- }
-
- return ordering;
- };
+ };
- this.orderBy = function () {
- if (getOrdering()) {
- return 'id';
- } else {
- return '-id';
- }
- };
+ this.getFeed = function (feedId) {
+ return FeedResource.getById(feedId);
+ };
- this.isCompactView = function () {
- return SettingsResource.get('compact');
- };
+ this.toggleKeepUnread = function (itemId) {
+ var item = ItemResource.get(itemId);
+ if (!item.unread) {
+ FeedResource.markItemOfFeedUnread(item.feedId);
+ ItemResource.markItemRead(itemId, false);
+ }
- this.isCompactExpand = function () {
- return SettingsResource.get('compactExpand');
- };
+ item.keepUnread = !item.keepUnread;
+ };
- this.autoPagingEnabled = function () {
- return this.isAutoPagingEnabled;
- };
+ var getOrdering = function () {
+ var ordering = SettingsResource.get('oldestFirst');
- this.markReadEnabled = function () {
- return !SettingsResource.get('preventReadOnScroll');
- };
+ if (self.isFeed()) {
+ var feed = FeedResource.getById($routeParams.id);
+ if (feed && feed.ordering === 1) {
+ ordering = true;
+ } else if (feed && feed.ordering === 2) {
+ ordering = false;
+ }
+ }
- this.scrollRead = function (itemIds) {
- var ids = [];
- var feedIds = [];
+ return ordering;
+ };
- itemIds.forEach(function (itemId) {
- var item = ItemResource.get(itemId);
- if (!item.keepUnread) {
- ids.push(itemId);
- feedIds.push(item.feedId);
+ this.orderBy = function () {
+ if (getOrdering()) {
+ return 'id';
+ } else {
+ return '-id';
+ }
+ };
+
+ this.isCompactView = function () {
+ return SettingsResource.get('compact');
+ };
+
+ this.isCompactExpand = function () {
+ return SettingsResource.get('compactExpand');
+ };
+
+ this.autoPagingEnabled = function () {
+ return this.isAutoPagingEnabled;
+ };
+
+ this.markReadEnabled = function () {
+ return !SettingsResource.get('preventReadOnScroll');
+ };
+
+ this.scrollRead = function (itemIds) {
+ var ids = [];
+ var feedIds = [];
+
+ itemIds.forEach(function (itemId) {
+ var item = ItemResource.get(itemId);
+ if (!item.keepUnread) {
+ ids.push(itemId);
+ feedIds.push(item.feedId);
+ }
+ });
+
+ if (ids.length > 0) {
+ FeedResource.markItemsOfFeedsRead(feedIds);
+ ItemResource.markItemsRead(ids);
}
- });
+ };
- if (ids.length > 0) {
- FeedResource.markItemsOfFeedsRead(feedIds);
- ItemResource.markItemsRead(ids);
- }
- };
+ this.isFeed = function () {
+ return $route.current.$$route.type === FEED_TYPE.FEED;
+ };
- this.isFeed = function () {
- return $route.current.$$route.type === FEED_TYPE.FEED;
- };
+ this.autoPage = function () {
+ if (this.isNothingMoreToAutoPage) {
+ return;
+ }
- this.autoPage = function () {
- if (this.isNothingMoreToAutoPage) {
- return;
- }
+ // in case a subsequent autopage request comes in wait until
+ // the current one finished and execute a request immediately
+ // afterwards
+ if (!this.isAutoPagingEnabled) {
+ this.autoPageAgain = true;
+ return;
+ }
- // in case a subsequent autopage request comes in wait until
- // the current one finished and execute a request immediately afterwards
- if (!this.isAutoPagingEnabled) {
- this.autoPageAgain = true;
- return;
- }
+ this.isAutoPagingEnabled = false;
+ this.autoPageAgain = false;
- this.isAutoPagingEnabled = false;
- this.autoPageAgain = false;
+ var type = $route.current.$$route.type;
+ var id = $routeParams.id;
+ var oldestFirst = getOrdering();
+ var showAll = SettingsResource.get('showAll');
+ var self = this;
+ var search = $location.search().search;
- var type = $route.current.$$route.type;
- var id = $routeParams.id;
- var oldestFirst = getOrdering();
- var showAll = SettingsResource.get('showAll');
- var self = this;
- var search = $location.search().search;
+ Loading.setLoading('autopaging', true);
- Loading.setLoading('autopaging', true);
+ ItemResource.autoPage(type, id, oldestFirst, showAll, search)
+ .success(function (data) {
+ Publisher.publishAll(data);
- ItemResource.autoPage(type, id, oldestFirst, showAll, search)
- .success(function (data) {
- Publisher.publishAll(data);
+ if (data.items.length >= ITEM_AUTO_PAGE_SIZE) {
+ self.isAutoPagingEnabled = true;
+ } else {
+ self.isNothingMoreToAutoPage = true;
+ }
- if (data.items.length >= ITEM_AUTO_PAGE_SIZE) {
+ if (self.isAutoPagingEnabled && self.autoPageAgain) {
+ self.autoPage();
+ }
+ }).error(function () {
self.isAutoPagingEnabled = true;
+ }).finally(function () {
+ Loading.setLoading('autopaging', false);
+ });
+ };
+
+ this.getRelativeDate = function (timestamp) {
+ if (timestamp !== undefined && timestamp !== '') {
+ var languageCode = SettingsResource.get('language');
+ var date =
+ moment.unix(timestamp).locale(languageCode).fromNow() + '';
+ return date;
} else {
- self.isNothingMoreToAutoPage = true;
+ return '';
}
+ };
- if (self.isAutoPagingEnabled && self.autoPageAgain) {
- self.autoPage();
- }
- }).error(function () {
- self.isAutoPagingEnabled = true;
- }).finally(function () {
- Loading.setLoading('autopaging', false);
- });
- };
-
- this.getRelativeDate = function (timestamp) {
- if (timestamp !== undefined && timestamp !== '') {
- var languageCode = SettingsResource.get('language');
- var date =
- moment.unix(timestamp).locale(languageCode).fromNow() + '';
- return date;
- } else {
- return '';
- }
- };
-
- this.refresh = function () {
- $route.reload();
- };
-
- this.getMediaType = function (type) {
- if (type && type.indexOf('audio') === 0) {
- return 'audio';
- } else if (type && type.indexOf('video') === 0) {
- return 'video';
- } else {
- return undefined;
- }
- };
+ this.refresh = function () {
+ $route.reload();
+ };
-}); \ No newline at end of file
+ this.getMediaType = function (type) {
+ if (type && type.indexOf('audio') === 0) {
+ return 'audio';
+ } else if (type && type.indexOf('video') === 0) {
+ return 'video';
+ } else {
+ return undefined;
+ }
+ };
+
+ this.activeItem = this.getFirstItem();
+ }); \ No newline at end of file
diff --git a/js/directive/NewsOnActive.js b/js/directive/NewsOnActive.js
new file mode 100644
index 000000000..ba1047c60
--- /dev/null
+++ b/js/directive/NewsOnActive.js
@@ -0,0 +1,23 @@
+/**
+ * ownCloud - News
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Bernhard Posselt <dev@bernhard-posselt.com>
+ * @copyright Bernhard Posselt 2016
+ */
+
+app.directive('newsOnActive', function ($parse) {
+ 'use strict';
+ return {
+ restrict: 'A',
+ link: function (scope, elem, attrs) {
+ elem.on('set-active', function () {
+ var callback = $parse(attrs.newsOnActive);
+ scope.$apply(callback);
+ });
+
+ }
+ };
+}); \ No newline at end of file
diff --git a/js/gui/KeyboardShortcuts.js b/js/gui/KeyboardShortcuts.js
index d05856152..c70510035 100644
--- a/js/gui/KeyboardShortcuts.js
+++ b/js/gui/KeyboardShortcuts.js
@@ -234,20 +234,12 @@
}
};
- var onActiveItem = function (scrollArea, callback) {
- var items = scrollArea.find('.item');
-
- items.each(function (index, item) {
- item = $(item);
-
- // 130px of the item should be visible
- if ((item.height() + item.position().top) > 30) {
- callback(item);
-
- return false;
- }
- });
+ var getActiveElement = function (scrollArea) {
+ return scrollArea.find('.item.active:first');
+ };
+ var onActiveItem = function (scrollArea, callback) {
+ callback(getActiveElement(scrollArea));
};
var toggleUnread = function (scrollArea) {
@@ -275,6 +267,10 @@
});
};
+ var setItemActive = function (element) {
+ element.dispatchEvent(new CustomEvent('set-active'));
+ };
+
var scrollToItem = function (scrollArea, item, expandItemInCompact) {
// if you go to the next article in compact view, it should
// expand the current one
@@ -282,6 +278,8 @@
item.offset().top - scrollArea.offset().top + scrollArea.scrollTop()
);
+ setItemActive(item[0]);
+
if (expandItemInCompact) {
onActiveItem(scrollArea, function (item) {
if (!item.hasClass('open')) {
@@ -292,64 +290,49 @@
};
var scrollToNextItem = function (scrollArea, expandItemInCompact) {
- var items = scrollArea.find('.item');
- var jumped = false;
-
- items.each(function (index, item) {
- item = $(item);
-
- // special treatment for items that have expand enabled:
- // if you click next and the first item has not been expaned and
- // is on the top, it should be expanded instead of the next one
- if ((item.position().top === 0 && expandItemInCompact &&
- !item.hasClass('open')) ||
- item.position().top > 10) {
- scrollToItem(scrollArea, item, expandItemInCompact);
-
- jumped = true;
-
- return false;
- }
- });
-
- // in case this is the last item it should still scroll below the top
- if (!jumped) {
+ var activeElement = getActiveElement(scrollArea);
+ var nextElement = activeElement.next();
+ if (nextElement.length > 0) {
+ scrollToItem(scrollArea, nextElement, expandItemInCompact);
+ } else {
+ // in case this is the last item it should still scroll below the
scrollArea.scrollTop(scrollArea.prop('scrollHeight'));
}
-
};
var scrollToPreviousItem = function (navigationArea, scrollArea,
expandItemInCompact) {
- var items = scrollArea.find('.item');
- var jumped = false;
-
- items.each(function (index, item) {
- item = $(item);
-
- if ((item.position().top + 10) >= 0) {
- var previous = item.prev();
-
- // if there are no items before the current one
- if (previous.length > 0) {
- scrollToItem(scrollArea, previous, expandItemInCompact);
- } else {
- tryReload(navigationArea, scrollArea);
- scrollArea.scrollTop(0);
- }
-
- jumped = true;
-
- return false;
- }
- });
-
- // if there was no jump jump to the last element
- if (!jumped && items.length > 0) {
- scrollToItem(scrollArea, items.last());
+ var activeElement = getActiveElement(scrollArea);
+ var previousElement = activeElement.prev();
+
+ // if the active element has been scrolled, the previous element
+ // should be the active one
+ if (activeElement.position().top + 20 <= 0) {
+ scrollToItem(scrollArea, activeElement, expandItemInCompact);
+ } else if (previousElement.length > 0) {
+ scrollToItem(scrollArea, previousElement, expandItemInCompact);
+ } else {
+ tryReload(navigationArea, scrollArea);
+ scrollArea.scrollTop(0);
}
};
+ // mark current item as active when scrolling
+ $(document).ready(function () {
+ var detectAndSetActiveItem = function () {
+ var items = $('#app-content').find('.item');
+ items.each(function (index, item) {
+ var $item = $(item);
+ var bottom = $item.position().top + $item.outerHeight(true);
+ console.log(bottom);
+ if ((bottom - 20) >= 0){
+ setItemActive(item);
+ return false;
+ }
+ });
+ };
+ $('#app-content').scroll(_.debounce(detectAndSetActiveItem, 250));
+ });
$(document).keyup(function (event) {
var keyCode = event.keyCode;
diff --git a/js/tests/unit/controller/ContentControllerSpec.js b/js/tests/unit/controller/ContentControllerSpec.js
index 9138db0ed..297ed61cc 100644
--- a/js/tests/unit/controller/ContentControllerSpec.js
+++ b/js/tests/unit/controller/ContentControllerSpec.js
@@ -22,6 +22,13 @@ describe('ContentController', function () {
SUBSCRIPTIONS: 3,
SHARED: 4
});
+ $provide.constant('$route', {
+ current: {
+ $$route: {
+ type: 3
+ }
+ }
+ })
}));
@@ -50,7 +57,7 @@ describe('ContentController', function () {
ItemResource.clear = jasmine.createSpy('clear');
$controller('ContentController', {
- data: {},
+ data: {}
});
expect(ItemResource.clear).toHaveBeenCalled();
@@ -395,6 +402,14 @@ describe('ContentController', function () {
}));
+ it ('should toggle active item', function ($controller) {
+ var ctrl = $controller('ContentController');
+ expect(ctrl.isItemActive(3)).toBe(undefined);
+ ctrl.setItemActive(3);
+ expect(ctrl.isItemActive(4)).toBe(false);
+ expect(ctrl.isItemActive(3)).toBe(true);
+ });
+
it('should autopage if more than 0 elements',
inject(function ($controller, ItemResource, Publisher) {
@@ -538,6 +553,11 @@ describe('ContentController', function () {
it('should refresh the page', inject(function ($controller) {
var route = {
+ current: {
+ $$route: {
+ type: 3
+ }
+ },
reload: jasmine.createSpy('reload')
};
var ctrl = $controller('ContentController', {