diff options
author | Bernhard Posselt <dev@bernhard-posselt.com> | 2014-05-30 15:14:07 +0200 |
---|---|---|
committer | Bernhard Posselt <dev@bernhard-posselt.com> | 2014-05-30 15:14:07 +0200 |
commit | 88279961c5e2f2bd0711fc4200d58b93b425199e (patch) | |
tree | 1e07557fdbd6f037b143fd49f454199737d12f45 | |
parent | abd5ef4c4c6ad3cf8e879f6c4b9181b077165952 (diff) |
fix autopaging and marking read, render items
-rw-r--r-- | CHANGELOG.md | 1 | ||||
-rw-r--r-- | css/content.css | 230 | ||||
-rw-r--r-- | js/build/app.js | 185 | ||||
-rw-r--r-- | js/controller/ContentController.js | 7 | ||||
-rw-r--r-- | js/directive/NewsAudio.js | 39 | ||||
-rw-r--r-- | js/directive/NewsScroll.js | 47 | ||||
-rw-r--r-- | js/filter/TrustUrl.js | 16 | ||||
-rw-r--r-- | js/service/FeedResource.js | 8 | ||||
-rw-r--r-- | js/service/SettingsResource.js | 7 | ||||
-rw-r--r-- | js/tests/unit/controller/ContentControllerSpec.js | 6 | ||||
-rw-r--r-- | js/tests/unit/service/FeedResourceSpec.js | 7 | ||||
-rw-r--r-- | templates/main.php | 11 | ||||
-rw-r--r-- | templates/part.content.php | 51 | ||||
-rw-r--r-- | templates/part.items.php | 17 | ||||
-rw-r--r-- | templates/part.settings.php | 16 |
15 files changed, 391 insertions, 257 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index f3b1cfb96..c37f54191 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,6 @@ owncloud-news (3.001) * Order by publication date rather than by id * Add clientside routing * When importing OPML use the feed title if given -* Export OPML with feed title * Animate folder/feed disappearance if marking read * Design changes * Port clientside code to JavaScript (ES6) diff --git a/css/content.css b/css/content.css index 3382f22fa..084b26609 100644 --- a/css/content.css +++ b/css/content.css @@ -40,7 +40,7 @@ } -#app-content.loading > ul { +#app-content.icon-loading > ul { display: none; } @@ -48,115 +48,69 @@ /** * Rules for a single feed item */ -.feed_item { +.item { border-top: 1px solid #ccc; min-height: 120px; - background-image: linear-gradient(top, rgb(248,248,248) 0, rgb(255,255,255) 84px); - padding: 40px 5px 10px 5px; + background-image: linear-gradient(top, #F8F8F8 0, #FFF 84px); + padding: 35px 5px 20px 5px; cursor: default; } -.feed_item.viewed { +.item.viewed { border-right: 5px solid #F28627; } -.feed_item:first-child { +.item:first-child { border-top: 0; } /** * Utils panel */ - .feed_item .item_utils { - height: 28px; - margin: 0 0 0 8px; + .item .star { + height: 25px; + width: 25px; + margin: 2px 0 0 6px; float: left; + border: 0; + background-color: #FFF; + background-size: 100%; + background-repeat: no-repeat; + background-image: url('../img/inactive_star.svg'); } - /** - * Primary feed utils which are always shown - */ - .feed_item .primary_item_utils { - float: left; + .item .starred, + .item .star:hover { + background-image: url('../img/active_star.svg'); } - .feed_item .primary_item_utils li { - display: inline-block; - line-height: 21px; - font-size: 14px; - color: #aaa; - float: left; - margin-left: ; - cursor: default; - } - - .feed_item .primary_item_utils li:first-child { margin: 0} - .feed_item .star, - .feed_item .external { - background: none; - background-repeat: no-repeat; - background-position: center center; - background-size: 24px 24px; - width: 24px; - height: 24px; - border: none; - display: block; - margin: 0; - padding: 0; - } - - .feed_item .star { - background-image: url('../img/inactive_star.svg'); - } - - .feed_item .external { - background-image: url('../img/link.svg'); - opacity: 0.4; - } - - .feed_item .external:hover { - opacity: 1; - } - - .feed_item .star.important, - .feed_item .star:hover { - background-image: url('../img/active_star.svg'); - } - .feed_item .star.important:hover { - opacity:1; - } - .feed_item .star:hover { - opacity:0.5; - } - /** * Heading for compact view */ - .feed_item.compact .item_body, - .feed_item.compact .item_title, - .feed_item.compact .item_author, - .feed_item.compact .item_date, - .feed_item.compact .item_utils, - .feed_item.compact .item_bottom_utils, - .feed_item .item_heading, - .feed_item .item_link { + .compact .item .body, + .compact .item .title, + .compact .item .author, + .compact .item .date, + .compact .item .bottom-utils, + .item .heading, + .item .link { display: none; } - .feed_item.compact.open .item_body, - .feed_item.compact.open .item_author, - .feed_item.compact.open .item_bottom_utils, - .feed_item.compact .item_heading, - .feed_item .item_link { + .compact .item.open .body, + .compact .item.open .author, + .compact .item.open .bottom-utils, + .compact .item .heading, + .item .link { display: block; } - .item_heading { + .heading { padding: 5px 20px; background-color: #fafafa; } - .item_heading h1 a { + .heading h1 a { font-weight: bold; font-size: 13pt; padding: 0 5px; @@ -164,35 +118,35 @@ line-height: 40px; } - .read .item_heading h1 a { + .read .heading h1 a { color: #888; } - .feed_item.compact { + .compact .item { padding: 0 !important; min-height: 0; } - .feed_item.compact .item_body { + .compact .item .body { padding-bottom: 40px; } - .feed_item.compact.open .item_heading { + .compact .item.open .heading { margin-bottom: 15px; border-bottom: 1px dashed #ddd; } - .item_heading .star { + .heading .star { float: left; margin: 7px 12px 0 0; } - .item_heading .external { + .heading .external { float: left; margin: 8px 12px 0 0; } - .item_heading .timeago { + .heading .timeago { display: block; float: right; line-height: 40px; @@ -202,17 +156,17 @@ /** * Feed title */ - .feed_item .item_title { + .item .title { font-size: 19px; - margin: 0 182px 10px 42px; + margin: 4px 182px 4px 42px; font-weight: bold; word-wrap: break-word; } - .feed_item .item_title a { + .item .title a { color: #222; } - .feed_item .item_title a:hover { + .item .title a:hover { color: #222; text-decoration: underline; } @@ -220,37 +174,28 @@ /** * Feed title when read */ - .feed_item.read .item_title { + .item.read .title { font-weight: normal; } - .feed_item.read .item_title a { + .item.read .title a { color: #888; } /** - * Title of the feed to which the item belongs - */ - .feed_item .item_feed_title { - color: #aaa; - font-size: 15px; - margin-left: 42px; - } - - /** * Author of the item */ - .feed_item .item_author { + .item .author { color: #aaa; font-size: 15px; margin: 3px 0 5px 42px; } - .feed_item .item_author a { + .item .author a { color: inherit; } - .feed_item .item_author a:hover { + .item .author a:hover { text-decoration: underline; } @@ -264,18 +209,18 @@ .timeago { float: right; color: #aaa; - margin: 0 17px 0 0; + margin: 4px 17px 0 0; font-size: 15px; } /** * Body of the feed item */ - .feed_item .enclosure { + .item .enclosure { padding: 14px 42px 0 49px; } - .feed_item .item_body { + .item .body { padding: 10px 42px 0 42px; max-width: 770px; font-size: 14px; @@ -285,16 +230,16 @@ height: 80px;*/ } - .feed_item .item_body.opened { + .item .body.opened { } - .feed_item .item_body p { + .item .body p { line-height: 1.5; margin: 7px 0 14px 0; } - .feed_item .item_body img, .feed_item .item_body table { + .item .body img, .item .body table { max-width: 100%; height: auto; overflow: auto; @@ -302,20 +247,20 @@ white-space:normal; } - .feed_item .item_body > img:first-child, - .feed_item .item_body > div:first-child > img:first-child { + .item .body > img:first-child, + .item .body > div:first-child > img:first-child { padding: 0 14px 0 0; float: left; } - .feed_item .item_body h1, .feed_item .item_body h2 { + .item .body h1, .item .body h2 { font-size: 17px; font-weight: bold; color: #222; margin: 21px 0 0 0; } - .feed_item .item_body h3 { + .item .body h3 { font-size: 15px; font-weight: bold; color: #222; @@ -323,57 +268,57 @@ text-decoration: underline; } - .feed_item .item_body > div { + .item .body > div { margin: 7px 0; } - .feed_item .item_body a { + .item .body a { color: #0000ff; text-decoration: underline; } - .feed_item .item_body ul { + .item .body ul { margin: 7px 0; padding-left: 14px; list-style-type: disc; } - .feed_item .item_body ol { + .item .body ol { margin: 7px 0; padding-left: 21px; } - .feed_item .item_body ul li { + .item .body ul li { cursor: default; line-height: 21px; } - .feed_item .item_body pre { + .item .body pre { padding: 7px 7px 7px 14px; background-color: #dadada; border: 1px solid #ccc; margin: 7px 0 14px 0; overflow-y: auto; } - .feed_item .item_body pre::-webkit-scrollbar { + .item .body pre::-webkit-scrollbar { height: 10px; } - .feed_item .item_body pre::-webkit-scrollbar-thumb { + .item .body pre::-webkit-scrollbar-thumb { background: #333; } - .feed_item .item_body pre::-webkit-scrollbar-track-piece { + .item .body pre::-webkit-scrollbar-track-piece { background: #ddd; } - .feed_item .item_body code, - .feed_item .item_body pre { + .item .body code, + .item .body pre { font-family: monospace; font-size: 16px; } - .feed_item .item_body blockquote { + .item .body blockquote { border-left: 1px solid rgb(210,210,210); margin: 25px 0; padding: 0 0 0 15px; @@ -384,7 +329,7 @@ /** * Line with utils at the bottom */ -.item_bottom_utils { +.item .bottom-utils { width: 100%; height: 35px; margin: 0; @@ -393,46 +338,25 @@ -moz-box-sizing: border-box; } - /** - * Secondary feed items which are only shown on hover - */ - .feed_item .secondary_item_utils { + .item .bottom-utils ul { float: right; margin: 0 17px; } - .feed_item .secondary_item_utils, .feed_item .secondary_item_utils a { + .item .bottom-utils, + .item .bottom-utils a { color: #000; } - .feed_item:hover .secondary_item_utils li { + .item:hover .bottom-utils li { display: inline-block; } - .feed_item .secondary_item_utils li { + .item .bottom-utils li { padding-left: 25px; - display: none; - } - - /** - * Secondary feed items which are only shown on hover - */ - .feed_item .show_keep_unread .keep_unread{ - display: block; - } - - .feed_item li.share_link { - background-image: url('../img/share.svg'); - background-repeat: no-repeat; - background-position: right center; - padding-right: 20px; - } - - .feed_item:hover .secondary_item_utils li.keep_unread { - cursor: pointer; } - .feed_item .secondary_item_utils li input[type=checkbox]{ + .item .bottom-utils li input[type=checkbox]{ margin-left: 5px; } diff --git a/js/build/app.js b/js/build/app.js index 27cd1ba6f..a8119dc82 100644 --- a/js/build/app.js +++ b/js/build/app.js @@ -235,6 +235,7 @@ var $__build_47_app__ = function () { }; this.scrollRead = function (itemIds) { var ids = []; + var feedIds = []; for (var $__3 = itemIds[$traceurRuntime.toProperty(Symbol.iterator)](), $__4; !($__4 = $__3.next()).done;) { try { throw undefined; @@ -247,12 +248,13 @@ var $__build_47_app__ = function () { item = ItemResource.get(itemId); if (!item.keepUnread) { ids.push(itemId); - FeedResource.markItemOfFeedRead(item.feedId); + feedIds.push(item.feedId); } } } } } + FeedResource.markItemsOfFeedsRead(feedIds); ItemResource.markItemsRead(ids); }; this.autoPage = function () { @@ -272,9 +274,14 @@ var $__build_47_app__ = function () { if (timestamp !== undefined && timestamp !== '') { try { throw undefined; - } catch (languageCode) { - languageCode = SettingsResource.get('language'); - return moment.unix(timestamp).lang(languageCode).fromNow(); + } catch (date) { + try { + throw undefined; + } catch (languageCode) { + languageCode = SettingsResource.get('language'); + date = moment.unix(timestamp).lang(languageCode).fromNow() + ''; + return date; + } } } else { return ''; @@ -384,6 +391,15 @@ var $__build_47_app__ = function () { }; } ]); + app.filter('trustUrl', [ + '$sce', + function ($sce) { + 'use strict'; + return function (url) { + return $sce.trustAsResourceUrl(url); + }; + } + ]); app.factory('FeedResource', [ 'Resource', '$http', @@ -475,6 +491,19 @@ var $__build_47_app__ = function () { this.ids[$traceurRuntime.toProperty(feedId)].unreadCount -= 1; this.updateUnreadCache(); }, + markItemsOfFeedsRead: function (feedIds) { + for (var $__3 = feedIds[$traceurRuntime.toProperty(Symbol.iterator)](), $__4; !($__4 = $__3.next()).done;) { + try { + throw undefined; + } catch (feedId) { + feedId = $__4.value; + { + this.ids[$traceurRuntime.toProperty(feedId)].unreadCount -= 1; + } + } + } + this.updateUnreadCache(); + }, markItemOfFeedUnread: function (feedId) { this.ids[$traceurRuntime.toProperty(feedId)].unreadCount += 1; this.updateUnreadCache(); @@ -810,7 +839,12 @@ var $__build_47_app__ = function () { function ($http, BASE_URL) { 'use strict'; var $__0 = this; - this.settings = {}; + this.settings = { + language: 'en', + showAll: false, + compact: false, + oldestFirst: false + }; this.defaultLanguageCode = 'en'; this.supportedLanguageCodes = [ 'ar-ma', @@ -1308,6 +1342,30 @@ var $__build_47_app__ = function () { writable: true }), $__2; }; + app.directive('newsAudio', function () { + 'use strict'; + return { + restrict: 'E', + scope: { + src: '@', + type: '@' + }, + transclude: true, + template: '' + '<audio controls="controls" preload="none" ng-hide="cantPlay()">' + '<source ng-src="{{ src|trustUrl }}">' + '</audio>' + '<a ng-href="{{ src|trustUrl }}" class="button" ng-show="cantPlay()" ' + 'ng-transclude></a>', + link: function (scope, elm) { + var source = elm.children().children('source')[0]; + var cantPlay = false; + source.addEventListener('error', function () { + scope.$apply(function () { + cantPlay = true; + }); + }); + scope.cantPlay = function () { + return cantPlay; + }; + } + }; + }); app.directive('newsAutoFocus', function () { 'use strict'; return function (scope, elem, attrs) { @@ -1341,55 +1399,92 @@ var $__build_47_app__ = function () { '$timeout', function ($timeout) { 'use strict'; - var autoPage = function (enabled, limit, callback, elem) { + var autoPage = function (enabled, limit, elem, scope) { if (enabled) { try { throw undefined; - } catch (counter) { - counter = 0; - for (var $__3 = reverse(elem.find('.feed_item'))[$traceurRuntime.toProperty(Symbol.iterator)](), $__4; !($__4 = $__3.next()).done;) { - try { - throw undefined; - } catch (item) { - item = $__4.value; - { - item = $(item); - if (counter >= limit) { - break; - } - if (item.position().top < 0) { - callback(); - break; + } catch (articles) { + try { + throw undefined; + } catch (counter) { + counter = 0; + articles = elem.find('.item'); + { + try { + throw undefined; + } catch ($i) { + $i = articles.length - 1; + for (; $i >= 0; $i -= 1) { + try { + throw undefined; + } catch (i) { + i = $i; + try { + try { + throw undefined; + } catch (item) { + item = $(articles[$traceurRuntime.toProperty(i)]); + if (counter >= limit) { + break; + } + if (item.position().top < 0) { + scope.$apply(scope.newsScrollAutoPage); + break; + } + counter += 1; + } + } finally { + $i = i; + } + } } - counter += 1; } } } } } }; - var markRead = function (enabled, callback, elem) { + var markRead = function (enabled, elem, scope) { if (enabled) { try { throw undefined; - } catch (ids) { - ids = []; - for (var $__3 = elem.find('.feed_item:not(.read)')[$traceurRuntime.toProperty(Symbol.iterator)](), $__4; !($__4 = $__3.next()).done;) { - try { - throw undefined; - } catch (item) { - item = $__4.value; - { - item = $(item); - if (item.position().top <= -50) { - ids.push(parseInt(item.data('id'), 10)); - } else { - break; + } catch (articles) { + try { + throw undefined; + } catch (ids) { + ids = []; + articles = elem.find('.item:not(.read)'); + { + try { + throw undefined; + } catch ($i) { + $i = 0; + for (; $i < articles.length; $i += 1) { + try { + throw undefined; + } catch (i) { + i = $i; + try { + try { + throw undefined; + } catch (item) { + item = $(articles[$traceurRuntime.toProperty(i)]); + if (item.position().top <= -50) { + ids.push(parseInt(item.data('id'), 10)); + } else { + break; + } + } + } finally { + $i = i; + } + } } } } + scope.itemIds = ids; + scope.$apply(scope.newsScrollMarkRead); } - callback(ids); } } }; @@ -1399,26 +1494,26 @@ var $__build_47_app__ = function () { 'newsScrollAutoPage': '&', 'newsScrollMarkRead': '&', 'newsScrollEnabledMarkRead': '=', - 'newsScrollEnableAutoPage': '=', + 'newsScrollEnabledAutoPage': '=', 'newsScrollMarkReadTimeout': '@', 'newsScrollTimeout': '@', 'newsScrollAutoPageWhenLeft': '@' }, link: function (scope, elem) { var allowScroll = true; - scope.newsScrollTimeout = scope.newsScrollTimeout || 1; - scope.newsScrollMarkReadTimeout = scope.newsScrollMarkReadTimeout || 1; - scope.newsScrollAutoPageWhenLeft = scope.newsScrollAutoPageWhenLeft || 50; + var scrollTimeout = scope.newsScrollTimeout || 1; + var markReadTimeout = scope.newsScrollMarkReadTimeout || 1; + var autoPageLimit = scope.newsScrollAutoPageWhenLeft || 50; var scrollHandler = function () { if (allowScroll) { allowScroll = false; $timeout(function () { allowScroll = true; - }, scope.newsScrollTimeout * 1000); - autoPage(scope.newsScrollEnableAutoPage, scope.newsScrollAutoPageWhenLeft, scope.newsScrollAutoPage, elem); + }, scrollTimeout * 1000); + autoPage(scope.newsScrollEnabledAutoPage, autoPageLimit, elem, scope); $timeout(function () { - markRead(scope.newsScrollEnabledMarkRead, scope.newsScrollMarkRead, elem); - }, scope.newsScrollMarkReadTimeout * 1000); + markRead(scope.newsScrollEnabledMarkRead, elem, scope); + }, markReadTimeout * 1000); } }; elem.on('scroll', scrollHandler); diff --git a/js/controller/ContentController.js b/js/controller/ContentController.js index 1af22ea5c..a42ff04f6 100644 --- a/js/controller/ContentController.js +++ b/js/controller/ContentController.js @@ -74,15 +74,17 @@ function (Publisher, FeedResource, ItemResource, SettingsResource, data, this.scrollRead = (itemIds) => { let ids = []; + let feedIds = []; for (let itemId of itemIds) { let item = ItemResource.get(itemId); if (!item.keepUnread) { ids.push(itemId); - FeedResource.markItemOfFeedRead(item.feedId); + feedIds.push(item.feedId); } } + FeedResource.markItemsOfFeedsRead(feedIds); ItemResource.markItemsRead(ids); }; @@ -106,7 +108,8 @@ function (Publisher, FeedResource, ItemResource, SettingsResource, data, this.getRelativeDate = (timestamp) => { if (timestamp !== undefined && timestamp !== '') { let languageCode = SettingsResource.get('language'); - return moment.unix(timestamp).lang(languageCode).fromNow(); + let date = moment.unix(timestamp).lang(languageCode).fromNow() + ''; + return date; } else { return ''; } diff --git a/js/directive/NewsAudio.js b/js/directive/NewsAudio.js new file mode 100644 index 000000000..efcf5dfec --- /dev/null +++ b/js/directive/NewsAudio.js @@ -0,0 +1,39 @@ +/** + * 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 2014 + */ +app.directive('newsAudio', () => { + 'use strict'; + return { + restrict: 'E', + scope: { + src: '@', + type: '@' + }, + transclude: true, + template: '' + + '<audio controls="controls" preload="none" ng-hide="cantPlay()">' + + '<source ng-src="{{ src|trustUrl }}">' + + '</audio>' + + '<a ng-href="{{ src|trustUrl }}" class="button" ng-show="cantPlay()" ' + + 'ng-transclude></a>', + link: (scope, elm) => { + let source = elm.children().children('source')[0]; + let cantPlay = false; + source.addEventListener('error', () => { + scope.$apply(() => { + cantPlay = true; + }); + }); + + scope.cantPlay = () => { + return cantPlay; + }; + } + }; +});
\ No newline at end of file diff --git a/js/directive/NewsScroll.js b/js/directive/NewsScroll.js index 126c79b11..3d566aebe 100644 --- a/js/directive/NewsScroll.js +++ b/js/directive/NewsScroll.js @@ -11,11 +11,13 @@ app.directive('newsScroll', ($timeout) => { 'use strict'; // autopaging - let autoPage = (enabled, limit, callback, elem) => { + let autoPage = (enabled, limit, elem, scope) => { if (enabled) { let counter = 0; - for (let item of reverse(elem.find('.feed_item'))) { - item = $(item); + let articles = elem.find('.item'); + + for (let i = articles.length - 1; i >= 0; i -= 1) { + let item = $(articles[i]); // if the counter is higher than the size it means @@ -29,7 +31,7 @@ app.directive('newsScroll', ($timeout) => { // below the top and we didnt hit the factor yet so // autopage and break if (item.position().top < 0) { - |