Improve pull-request for unread count for each feed

This commit is contained in:
Frédéric Guillot 2014-02-22 19:45:02 -05:00
parent 8422e62abc
commit 754dcdd274
5 changed files with 103 additions and 74 deletions

10
assets/js/all.min.js vendored
View File

@ -1,12 +1,12 @@
var Miniflux={};Miniflux.App=function(){return{Run:function(){Miniflux.Event.ListenKeyboardEvents();Miniflux.Event.ListenMouseEvents()},MozillaAuth:function(e){navigator.id.watch({onlogin:function(a){var b=new XMLHttpRequest;b.open("POST","?action="+e+"&token="+a,!0);b.setRequestHeader("Connection","close");b.onload=function(){window.location.href=this.responseText};b.send("token="+a)},onlogout:function(){}});navigator.id.request()}}}(); var Miniflux={};Miniflux.App=function(){return{Run:function(){Miniflux.Event.ListenKeyboardEvents();Miniflux.Event.ListenMouseEvents()},MozillaAuth:function(e){navigator.id.watch({onlogin:function(a){var b=new XMLHttpRequest;b.open("POST","?action="+e+"&token="+a,!0);b.setRequestHeader("Connection","close");b.onload=function(){window.location.href=this.responseText};b.send("token="+a)},onlogout:function(){}});navigator.id.request()}}}();
Miniflux.Feed=function(){function e(b){if(b=document.getElementById("loading-feed-"+b)){b.innerHTML="";var a=document.createElement("img");a.src="assets/img/refresh.gif";b.appendChild(a)}}function a(){for(var a=document.getElementsByTagName("a"),d=0,e=a.length;d<e;d++){var c=a[d].getAttribute("data-feed-id");c&&b.push(parseInt(c))}}var b=[],d=[];return{Update:function(b,a){e(b);var d=new XMLHttpRequest;d.onload=function(){var c;try{c=JSON.parse(this.responseText)}catch(h){}var m="(0/0)";!1!==c.result&& Miniflux.Feed=function(){function e(b){if(b=document.getElementById("loading-feed-"+b)){var a=document.createElement("img");a.src="assets/img/refresh.gif";b.appendChild(a)}}function a(){for(var a=document.getElementsByTagName("a"),d=0,e=a.length;d<e;d++){var c=a[d].getAttribute("data-feed-id");c&&b.push(parseInt(c))}}var b=[],d=[];return{Update:function(b,a){e(b);var d=new XMLHttpRequest;d.onload=function(){var c=document.getElementById("loading-feed-"+b);c&&(c.innerHTML="");if(c=document.getElementById("last-checked-feed-"+
(m="("+String(c.result.items_unread)+"/"+String(c.result.items_total)+")");var d=document.getElementById("loading-feed-"+b);d&&(d.innerHTML=m);if(d=document.getElementById("last-checked-feed-"+b))d.innerHTML=d.getAttribute("data-after-update");a&&a(c)};d.open("POST","?action=refresh-feed&feed_id="+b,!0);d.send()},UpdateAll:function(){a();var e=setInterval(function(){for(;0<b.length&&5>d.length;){var a=b.shift();d.push(a);Miniflux.Feed.Update(a,function(a){a=d.indexOf(a.feed_id);0<=a&&d.splice(a,1); b))c.innerHTML=c.getAttribute("data-after-update");try{var g=JSON.parse(this.responseText);if(g.result){var m=g.items_count,d=document.getElementById("items-count-"+b);d&&(d.innerHTML="("+m.items_unread+"/"+m.items_total+")")}a&&a(g)}catch(e){}};d.open("POST","?action=refresh-feed&feed_id="+b,!0);d.send()},UpdateAll:function(){a();var e=setInterval(function(){for(;0<b.length&&5>d.length;){var a=b.shift();d.push(a);Miniflux.Feed.Update(a,function(a){a=d.indexOf(a.feed_id);0<=a&&d.splice(a,1);0==b.length&&
0==b.length&&0==d.length&&(clearInterval(e),window.location.href="?action=unread")})}},100)}}}(); 0==d.length&&(clearInterval(e),window.location.href="?action=unread")})}},100)}}}();
Miniflux.Item=function(){function e(c){var b=document.getElementById("item-"+c);b||(b=document.getElementById("current-item"),b.getAttribute("data-item-id")!=c&&(b=!1));return b}function a(c){if((c=document.getElementById("bookmark-"+c))&&c.getAttribute("data-reverse-label")){var b=c.innerHTML;c.innerHTML=c.getAttribute("data-reverse-label");c.setAttribute("data-reverse-label",b)}}function b(c){if(c=document.getElementById("status-"+c)){var b=c.innerHTML;c.innerHTML=c.getAttribute("data-reverse-label"); Miniflux.Item=function(){function e(c){var b=document.getElementById("item-"+c);b||(b=document.getElementById("current-item"),b.getAttribute("data-item-id")!=c&&(b=!1));return b}function a(c){if((c=document.getElementById("bookmark-"+c))&&c.getAttribute("data-reverse-label")){var b=c.innerHTML;c.innerHTML=c.getAttribute("data-reverse-label");c.setAttribute("data-reverse-label",b)}}function b(c){if(c=document.getElementById("status-"+c)){var b=c.innerHTML;c.innerHTML=c.getAttribute("data-reverse-label");
c.setAttribute("data-reverse-label",b)}}function d(c){"mouse"!=Miniflux.Event.lastEventType&&Miniflux.Nav.SelectNextItem();c.parentNode.removeChild(c);if(c=document.getElementById("page-counter"))counter=parseInt(c.textContent.trim(),10)-1,0==counter?window.location="?action=unread":(c.textContent=counter+" ",document.title="miniflux ("+counter+")",document.getElementById("nav-counter").textContent="("+counter+")")}function f(c){var a=new XMLHttpRequest;a.onload=function(){if(Miniflux.Nav.IsListing()){var a= c.setAttribute("data-reverse-label",b)}}function d(c){"mouse"!=Miniflux.Event.lastEventType&&Miniflux.Nav.SelectNextItem();c.parentNode.removeChild(c);if(c=document.getElementById("page-counter"))counter=parseInt(c.textContent.trim(),10)-1,0==counter?window.location="?action=unread":(c.textContent=counter+" ",document.title="miniflux ("+counter+")",document.getElementById("nav-counter").textContent="("+counter+")")}function f(c){var a=new XMLHttpRequest;a.onload=function(){if(Miniflux.Nav.IsListing()){var a=
e(c);if(a)if(a.getAttribute("data-hide"))d(a);else{a.setAttribute("data-item-status","read");b(c);if(a=document.getElementById("show-"+c)){a.className="read";var h=document.createElement("span");h.id="read-icon-"+c;h.appendChild(document.createTextNode("\u2611 "));a.parentNode.insertBefore(h,a)}(a=document.getElementById("status-"+c))&&a.setAttribute("data-action","mark-unread")}}};a.open("POST","?action=mark-item-read&id="+c,!0);a.send()}function k(c){var a=new XMLHttpRequest;a.onload=function(){if(Miniflux.Nav.IsListing()){var a= e(c);if(a)if(a.getAttribute("data-hide"))d(a);else{a.setAttribute("data-item-status","read");b(c);if(a=document.getElementById("show-"+c)){a.className="read";var g=document.createElement("span");g.id="read-icon-"+c;g.appendChild(document.createTextNode("\u2611 "));a.parentNode.insertBefore(g,a)}(a=document.getElementById("status-"+c))&&a.setAttribute("data-action","mark-unread")}}};a.open("POST","?action=mark-item-read&id="+c,!0);a.send()}function k(c){var a=new XMLHttpRequest;a.onload=function(){if(Miniflux.Nav.IsListing()){var a=
e(c);if(a)if(a.getAttribute("data-hide"))d(a);else{a.setAttribute("data-item-status","unread");b(c);if(a=document.getElementById("show-"+c))a.className="";(a=document.getElementById("read-icon-"+c))&&a.parentNode.removeChild(a);(a=document.getElementById("status-"+c))&&a.setAttribute("data-action","mark-read")}}};a.open("POST","?action=mark-item-unread&id="+c,!0);a.send()}function l(c,b){var d=c.getAttribute("data-item-id"),e=new XMLHttpRequest;e.onload=function(){c.setAttribute("data-item-bookmark", e(c);if(a)if(a.getAttribute("data-hide"))d(a);else{a.setAttribute("data-item-status","unread");b(c);if(a=document.getElementById("show-"+c))a.className="";(a=document.getElementById("read-icon-"+c))&&a.parentNode.removeChild(a);(a=document.getElementById("status-"+c))&&a.setAttribute("data-action","mark-read")}}};a.open("POST","?action=mark-item-unread&id="+c,!0);a.send()}function l(c,b){var d=c.getAttribute("data-item-id"),e=new XMLHttpRequest;e.onload=function(){c.setAttribute("data-item-bookmark",
b);if(b)if(Miniflux.Nav.IsListing()){if(g=document.getElementById("show-"+d)){var e=document.createElement("span");e.id="bookmark-icon-"+d;e.appendChild(document.createTextNode("\u2605 "));g.parentNode.insertBefore(e,g)}a(d)}else{var g=document.getElementById("bookmark-"+d);g&&(g.innerHTML="\u2605")}else if(Miniflux.Nav.IsListing())(e=document.getElementById("bookmark-icon-"+d))&&e.parentNode.removeChild(e),a(d);else if(e=document.getElementById("bookmark-"+d))e.innerHTML="\u2606"};e.open("POST", b);if(b)if(Miniflux.Nav.IsListing()){if(h=document.getElementById("show-"+d)){var e=document.createElement("span");e.id="bookmark-icon-"+d;e.appendChild(document.createTextNode("\u2605 "));h.parentNode.insertBefore(e,h)}a(d)}else{var h=document.getElementById("bookmark-"+d);h&&(h.innerHTML="\u2605")}else if(Miniflux.Nav.IsListing())(e=document.getElementById("bookmark-icon-"+d))&&e.parentNode.removeChild(e),a(d);else if(e=document.getElementById("bookmark-"+d))e.innerHTML="\u2606"};e.open("POST",
"?action=bookmark&id="+d+"&value="+b,!0);e.send()}return{Get:e,MarkAsRead:f,MarkAsUnread:k,SwitchBookmark:function(c){"1"==c.getAttribute("data-item-bookmark")?l(c,0):l(c,1)},SwitchStatus:function(c){var a=c.getAttribute("data-item-id");c=c.getAttribute("data-item-status");"read"==c?k(a):"unread"==c&&f(a)},ChangeStatus:function(c,a){switch(a){case "read":f(c);break;case "unread":k(c)}},Show:function(c){(c=document.getElementById("show-"+c))&&c.click()},OpenOriginal:function(c){var a=document.getElementById("original-"+ "?action=bookmark&id="+d+"&value="+b,!0);e.send()}return{Get:e,MarkAsRead:f,MarkAsUnread:k,SwitchBookmark:function(c){"1"==c.getAttribute("data-item-bookmark")?l(c,0):l(c,1)},SwitchStatus:function(c){var a=c.getAttribute("data-item-id");c=c.getAttribute("data-item-status");"read"==c?k(a):"unread"==c&&f(a)},ChangeStatus:function(c,a){switch(a){case "read":f(c);break;case "unread":k(c)}},Show:function(c){(c=document.getElementById("show-"+c))&&c.click()},OpenOriginal:function(c){var a=document.getElementById("original-"+
c);a&&("unread"==e(c).getAttribute("data-item-status")&&f(c),a.removeAttribute("data-action"),a.click())},DownloadContent:function(){var a=document.getElementById("download-item");if(a){var b=a.getAttribute("data-item-id"),d=a.getAttribute("data-before-message"),e=document.createElement("img");e.src="./assets/img/refresh.gif";a.innerHTML="";a.className="downloading";a.appendChild(e);a.appendChild(document.createTextNode(" "+d));var f=new XMLHttpRequest;f.onload=function(){var b=JSON.parse(f.responseText); c);a&&("unread"==e(c).getAttribute("data-item-status")&&f(c),a.removeAttribute("data-action"),a.click())},DownloadContent:function(){var a=document.getElementById("download-item");if(a){var b=a.getAttribute("data-item-id"),d=a.getAttribute("data-before-message"),e=document.createElement("img");e.src="./assets/img/refresh.gif";a.innerHTML="";a.className="downloading";a.appendChild(e);a.appendChild(document.createTextNode(" "+d));var f=new XMLHttpRequest;f.onload=function(){var b=JSON.parse(f.responseText);
if(b.result){var d=document.getElementById("item-content");d&&(d.innerHTML=b.content);a&&(b=a.getAttribute("data-after-message"),a.innerHTML="",a.appendChild(document.createTextNode(" "+b)))}else a&&(b=a.getAttribute("data-failure-message"),a.innerHTML="",a.appendChild(document.createTextNode(" "+b)))};f.open("POST","?action=download-item&id="+b,!0);f.send()}},MarkListingAsRead:function(a){for(var b=document.getElementsByTagName("article"),d=[],e=0,f=b.length;e<f;e++)d.push(b[e].getAttribute("data-item-id")); if(b.result){var d=document.getElementById("item-content");d&&(d.innerHTML=b.content);a&&(b=a.getAttribute("data-after-message"),a.innerHTML="",a.appendChild(document.createTextNode(" "+b)))}else a&&(b=a.getAttribute("data-failure-message"),a.innerHTML="",a.appendChild(document.createTextNode(" "+b)))};f.open("POST","?action=download-item&id="+b,!0);f.send()}},MarkListingAsRead:function(a){for(var b=document.getElementsByTagName("article"),d=[],e=0,f=b.length;e<f;e++)d.push(b[e].getAttribute("data-item-id"));

View File

@ -15,7 +15,6 @@ Miniflux.Feed = (function() {
var container = document.getElementById("loading-feed-" + feed_id); var container = document.getElementById("loading-feed-" + feed_id);
if (container) { if (container) {
container.innerHTML = "";
var img = document.createElement("img"); var img = document.createElement("img");
img.src = "assets/img/refresh.gif"; img.src = "assets/img/refresh.gif";
container.appendChild(img); container.appendChild(img);
@ -23,10 +22,10 @@ Miniflux.Feed = (function() {
} }
// Hide the refresh icon after update // Hide the refresh icon after update
function hideRefreshIcon(feed_id, replace_text) function hideRefreshIcon(feed_id)
{ {
var container = document.getElementById("loading-feed-" + feed_id); var container = document.getElementById("loading-feed-" + feed_id);
if (container) container.innerHTML = replace_text; if (container) container.innerHTML = "";
var container = document.getElementById("last-checked-feed-" + feed_id); var container = document.getElementById("last-checked-feed-" + feed_id);
if (container) container.innerHTML = container.getAttribute("data-after-update"); if (container) container.innerHTML = container.getAttribute("data-after-update");
@ -43,6 +42,13 @@ Miniflux.Feed = (function() {
} }
} }
// Update the items unread/total count for the feed
function updateItemsCounter(feed_id, counts)
{
var container = document.getElementById("items-count-" + feed_id);
if (container) container.innerHTML = "(" + counts["items_unread"] + "/" + counts['items_total'] + ")";
}
return { return {
Update: function(feed_id, callback) { Update: function(feed_id, callback) {
@ -52,24 +58,16 @@ Miniflux.Feed = (function() {
request.onload = function() { request.onload = function() {
var response; hideRefreshIcon(feed_id);
try { try {
response = JSON.parse(this.responseText);
var response = JSON.parse(this.responseText);
if (response.result) updateItemsCounter(feed_id, response.items_count);
if (callback) callback(response);
} }
catch (e) {} catch (e) {}
var unread_ratio = String("(0/0)");
if (response.result !== false) {
unread_ratio = "(" + String(response.result.items_unread) + "/" + String(response.result.items_total) + ")";
// document.getElementById("xmlhttpresp").innerHTML=this.responseText + " " + unread_ratio;
}
hideRefreshIcon(feed_id, unread_ratio);
if (callback) {
callback(response);
}
}; };
request.open("POST", "?action=refresh-feed&feed_id=" + feed_id, true); request.open("POST", "?action=refresh-feed&feed_id=" + feed_id, true);

View File

@ -162,10 +162,13 @@ Router\get_action('refresh-feed', function() {
// Ajax call to refresh one feed // Ajax call to refresh one feed
Router\post_action('refresh-feed', function() { Router\post_action('refresh-feed', function() {
$id = Request\int_param('feed_id', 0); $feed_id = Request\int_param('feed_id', 0);
$result = Model\Feed\refresh($id);
Response\json(array('feed_id' => $id, 'result' => $result)); Response\json(array(
'feed_id' => $feed_id,
'result' => Model\Feed\refresh($feed_id),
'items_count' => Model\Feed\count_items($feed_id),
));
}); });
// Display all feeds // Display all feeds
@ -194,7 +197,7 @@ Router\get_action('feeds', function() {
} }
Response\html(Template\layout('feeds', array( Response\html(Template\layout('feeds', array(
'feeds' => Model\Feed\get_all_w_counts(), 'feeds' => Model\Feed\get_all_item_counts(),
'nothing_to_read' => Request\int_param('nothing_to_read'), 'nothing_to_read' => Request\int_param('nothing_to_read'),
'menu' => 'feeds', 'menu' => 'feeds',
'title' => t('Subscriptions') 'title' => t('Subscriptions')

View File

@ -161,7 +161,7 @@ function refresh($feed_id)
if (! $resource->isModified()) { if (! $resource->isModified()) {
update_parsing_error($feed_id, 0); update_parsing_error($feed_id, 0);
\Model\Config\write_debug(); \Model\Config\write_debug();
return get_feed_unread_counts($feed_id); return true;
} }
$parser = $reader->getParser(); $parser = $reader->getParser();
@ -190,7 +190,7 @@ function refresh($feed_id)
\Model\Item\update_all($feed_id, $result->items, $parser->grabber); \Model\Item\update_all($feed_id, $result->items, $parser->grabber);
\Model\Config\write_debug(); \Model\Config\write_debug();
return get_feed_unread_counts($feed_id); return true;
} }
} }
@ -244,56 +244,81 @@ function get_all()
->findAll(); ->findAll();
} }
// Returns item unread and total counts, indexed by feed_id // Get all feeds with the number unread/total items
// setting $feed_id returns counts for just that feed function get_all_item_counts()
function get_feed_unread_counts($feed_id = 0)
{ {
$query = Database::get('db') $counts = Database::get('db')
->table('items') ->table('items')
->columns('feed_id', 'status', 'count(status) AS item_count') ->columns('feed_id', 'status', 'count(*) as item_count')
->in('status', array('read', 'unread')) ->in('status', array('read', 'unread'))
->groupBy('feed_id', 'status'); ->groupBy('feed_id', 'status')
if ($feed_id) $query->eq('feed_id', $feed_id); ->findAll();
$rq = $query->findAll();
$itemcnts = array(); $feeds = Database::get('db')
foreach($rq as $rec) { ->table('feeds')
if (!array_key_exists($rec['feed_id'], $itemcnts)) ->asc('title')
$itemcnts[$rec['feed_id']] = array('items_unread' => 0, 'items_total' => 0); ->findAll();
if ($rec['status'] == 'unread')
$itemcnts[$rec['feed_id']]['items_unread'] = $rec['item_count']; $item_counts = array();
$itemcnts[$rec['feed_id']]['items_total'] += $rec['item_count'];
foreach ($counts as &$count) {
if (! isset($item_counts[$count['feed_id']])) {
$item_counts[$count['feed_id']] = array(
'items_unread' => 0,
'items_total' => 0,
);
} }
if ($feed_id)
// return the counts for the singe feed requested $item_counts[$count['feed_id']]['items_total'] += $count['item_count'];
if(! array_key_exists($feed_id, $itemcnts))
return array('items_unread' => 0, 'items_total' => 0); if ($count['status'] === 'unread') {
else $item_counts[$count['feed_id']]['items_unread'] = $count['item_count'];
return $itemcnts[$feed_id]; }
else }
// return the counts for all feeds
return $itemcnts; foreach ($feeds as &$feed) {
if (isset($item_counts[$feed['id']])) {
$feed += $item_counts[$feed['id']];
}
else {
$feed += array(
'items_unread' => 0,
'items_total' => 0,
);
}
}
return $feeds;
} }
// Get all feeds with counts of items_unread and items_total for each feed // Get unread/total count for one feed
function get_all_w_counts() function count_items($feed_id)
{ {
// get unread and total item counts by feed id $counts = Database::get('db')
$cnts = get_feed_unread_counts(); ->table('items')
// get all feeds ->columns('status', 'count(*) as item_count')
$rq = get_all(); ->in('status', array('read', 'unread'))
// for loop re-processes return array by appending items_unread and items_total fields ->eq('feed_id', $feed_id)
foreach ($rq as $rec_no => $rec) { ->groupBy('status')
// determine if there are item counts for this feed ->findAll();
if (array_key_exists($rec['id'], $cnts)) {
// yes, append unread and total counts to the feed record $result = array(
$rq[$rec_no] = array_merge($rec, $cnts[$rec['id']]); 'items_unread' => 0,
} else { 'items_total' => 0,
// no this is an empty feed, so append unread and total counts = 0 );
$rq[$rec_no] = array_merge($rec, array('items_unread' => 0, 'items_total' => 0));
foreach ($counts as &$count) {
if ($count['status'] === 'unread') {
$result['items_unread'] = (int) $count['item_count'];
} }
$result['items_total'] += $count['item_count'];
} }
return $rq;
return $result;
} }
// Get one feed // Get one feed

View File

@ -25,10 +25,13 @@
<?php if (! $feed['enabled']): ?> <?php if (! $feed['enabled']): ?>
<span title="<?= t('Subscription disabled') ?>"></a> <span title="<?= t('Subscription disabled') ?>"></a>
<?php else: ?> <?php else: ?>
<span id="loading-feed-<?= $feed['id'] ?>">(<?= $feed['items_unread'] .'/' . $feed['items_total'] ?>)</span> <span id="loading-feed-<?= $feed['id'] ?>"></span>
<?php endif ?> <?php endif ?>
<span id="items-count-<?= $feed['id'] ?>">(<?= $feed['items_unread'] .'/' . $feed['items_total'] ?>)</span>
<a href="?action=feed-items&amp;feed_id=<?= $feed['id'] ?>" title="<?= t('Show only this subscription') ?>"><?= Helper\escape($feed['title']) ?></a> <a href="?action=feed-items&amp;feed_id=<?= $feed['id'] ?>" title="<?= t('Show only this subscription') ?>"><?= Helper\escape($feed['title']) ?></a>
<?php if ($feed['enabled']): ?> <?php if ($feed['enabled']): ?>
<br/> <br/>