implement frontend autoupdate
Only the unread counter is updated right know. The AutoUpdate Feature is designed on the premise of don't wasting resources. A distinction is made between updates when Miniflux is visible or hidden. To determine the visibility status, the Page Visibility API is used. The API is available starting with Chrome 33, Firefox 18 and IE10. [https://developer.mozilla.org/en-US/docs/Web/Guide/User_experience/Using_the_Page_Visibility_API] As IE9 returns an undefined, it doesn't break the compatibility at least. If Miniflux is visible, the unread counter on the web page is updated as soon as a mismatch between the counter and the number of unread articles in the database is found. If Miniflux is hidden, the timestamp of the most recent article from each feed is compared with the value from the last run. We have an update If the timestamp of the latest article is greater than the stored one and the latest article is unread. The web page title is updated with a ? symbol to notify the user and the update check pauses till Miniflux gets visible again. If Miniflux gets visible again, the number of unread articles is queried from the database, the unread counter on the web page is updated and finally the ? symbol is removed from the web page title. This way I can use my fever API client to read new articles (or at least the latest article) while Miniflux is hidden and as I've seen the new articles already a new articles notification is prevented. It's intentionally that the page does not reload automatically as long as articles are visible. If I'm in hurry, I only scroll through the articles to spot something interesting. Most of the time I don't reach the last article. If the page is reloaded while I'm away, I would have to scan from the top again. If we're on a nothing_to_read page and have unread articles in the database, a redirect to the unread page will be done. The default update check interval is 10 minutes and can be changed on the settings page. A zero value disables the update check entirely. fixes #213
This commit is contained in:
parent
e0fd734c64
commit
cd13efeabf
@ -163,6 +163,7 @@ input[type="checkbox"] {
|
||||
input[type="email"],
|
||||
input[type="tel"],
|
||||
input[type="password"],
|
||||
input[type="number"],
|
||||
input[type="text"] {
|
||||
border: 1px solid #ccc;
|
||||
padding: 3px;
|
||||
@ -176,6 +177,7 @@ input[type="text"] {
|
||||
input[type="email"]:focus,
|
||||
input[type="tel"]:focus,
|
||||
input[type="password"]:focus,
|
||||
input[type="number"]:focus,
|
||||
input[type="text"]:focus,
|
||||
textarea:focus {
|
||||
color: #000;
|
||||
|
35
assets/js/all.min.js
vendored
35
assets/js/all.min.js
vendored
@ -1,16 +1,19 @@
|
||||
var g=function(){var e=[];return{i:function(d,a){var b=d.querySelector("span.items-count");if(b){var c=d.getAttribute("data-feed-id"),e=d.querySelector("h2:first-of-type");e.className="loading-icon";var l=new XMLHttpRequest;l.onload=function(){e.className="";d.removeAttribute("data-feed-error");var c=d.querySelector(".feed-last-checked");c&&(c.innerHTML=c.getAttribute("data-after-update"));c=JSON.parse(this.responseText);c.result?b.innerHTML=c.items_count.items_unread+"/"+c.items_count.items_total:
|
||||
d.setAttribute("data-feed-error","1");a&&a(c)};l.open("POST","?action=refresh-feed&feed_id="+c,!0);l.send()}},j:function(){var d=Array.prototype.slice.call(document.querySelectorAll("article:not([data-feed-disabled])")),a=setInterval(function(){for(;0<d.length&&5>e.length;){var b=d.shift();e.push(parseInt(b.getAttribute("data-feed-id"),10));g.i(b,function(b){b=e.indexOf(b.feed_id);0<=b&&e.splice(b,1);0===d.length&&0===e.length&&(clearInterval(a),window.location.href="?action=unread")})}},100)}}}(),
|
||||
q=function(){function e(a){return item_id=a.getAttribute("data-item-id")}function d(a){if(a&&a.hasAttribute("data-reverse-label")){var b=a.innerHTML;a.innerHTML=a.getAttribute("data-reverse-label");a.setAttribute("data-reverse-label",b)}}function a(a){"mouse"!==k.b&&m.c();a.parentNode.removeChild(a);p--}function b(){0===p&&window.location.reload();var a=document.getElementById("page-counter");a.textContent=p||"";document.getElementById("nav-counter").textContent=l||"";switch(document.querySelector("section.page").getAttribute("data-item-page")){case "unread":document.title=
|
||||
"Miniflux ("+l+")";break;case "feed-items":document.title="("+p+") "+a.parentNode.firstChild.nodeValue;break;default:document.title=a.parentNode.firstChild.nodeValue+" ("+p+")"}}function c(h){var f=e(h),c=new XMLHttpRequest;c.onload=function(){if(m.a()){if(h.getAttribute("data-hide"))a(h);else{h.setAttribute("data-item-status","read");var c=h.querySelector("a.mark");d(c);(c=h.querySelector("a.mark"))&&c.setAttribute("data-action","mark-unread")}l--;b()}};c.open("POST","?action=mark-item-read&id="+
|
||||
f,!0);c.send()}function t(h){var c=e(h),n=new XMLHttpRequest;n.onload=function(){if(m.a()){if(h.getAttribute("data-hide"))a(h);else{h.setAttribute("data-item-status","unread");var c=h.querySelector("a.mark");d(c);(c=h.querySelector("a.mark"))&&c.setAttribute("data-action","mark-read")}l++;b()}};n.open("POST","?action=mark-item-unread&id="+c,!0);n.send()}var l=function(){var a=document.getElementById("nav-counter");if(a)return counter=parseInt(a.textContent,10)||0}(),p=function(){var a=document.getElementById("page-counter");
|
||||
if(a)return counter=parseInt(a.textContent,10)||0}();return{m:c,o:t,n:function(c){var f=e(c),d=new XMLHttpRequest;d.onload=function(){m.a()&&(a(c),"unread"===c.getAttribute("data-item-status")&&l--,b())};d.open("POST","?action=mark-item-removed&id="+f,!0);d.send()},h:function(c){var f=e(c),n="1"===c.getAttribute("data-item-bookmark")?"0":"1",r=new XMLHttpRequest;r.onload=function(){var f=document.querySelector("section.page");if(m.a()&&"bookmarks"===f.getAttribute("data-item-page"))a(c),b();else if(c.setAttribute("data-item-bookmark",
|
||||
n),m.a())f=c.querySelector("a.bookmark"),d(f);else if((f=c.querySelector("a.bookmark-icon"))&&f.hasAttribute("data-reverse-title")){var e=f.getAttribute("title");f.setAttribute("title",f.getAttribute("data-reverse-title"));f.setAttribute("data-reverse-title",e)}};r.open("POST","?action=bookmark&id="+f+"&value="+n,!0);r.send()},t:function(a){var b=a.getAttribute("data-item-status");"read"===b?t(a):"unread"===b&&c(a)},r:function(a){(a=a.querySelector("a.show"))&&a.click()},f:function(a){var b=a.querySelector("a.original");
|
||||
b&&("unread"===a.getAttribute("data-item-status")&&c(a),b.removeAttribute("data-action"),"mouse"!==k.b&&b.click())},d:function(a){var b=document.getElementById("download-item");if(b){b.innerHTML=" "+b.getAttribute("data-before-message");b.className="loading-icon";var c=new XMLHttpRequest;c.onload=function(){var a=JSON.parse(c.responseText);b.className="";if(a.result){var d=document.getElementById("item-content");d&&(d.innerHTML=a.content);b.innerHTML=b.getAttribute("data-after-message")}else b.innerHTML=
|
||||
b.getAttribute("data-failure-message")};a=e(a);c.open("POST","?action=download-item&id="+a,!0);c.send()}},e:function(a){for(var b=document.getElementsByTagName("article"),c=[],d=0,l=b.length;d<l;d++)c.push(e(b[d]));b=new XMLHttpRequest;b.onload=function(){window.location.href=a};b.open("POST","?action=mark-items-as-read",!0);b.send(JSON.stringify(c))},u:function(){for(var a=["#current-item h1","#item-content","#listing #current-item h2","#listing #current-item .preview"],b=0;b<a.length;b++){var c=
|
||||
document.querySelector(a[b]);c&&(c.dir=""==c.dir?"rtl":"")}}}}(),k=function(){function e(a){if(63!==a.keyCode&&63!==a.which&&(a.ctrlKey||a.shiftKey||a.altKey||a.metaKey))return!0;a=a.target||a.srcElement;return"INPUT"===a.tagName||"TEXTAREA"===a.tagName?!0:!1}var d=[];return{b:"",l:function(){document.onclick=function(a){var b=a.target.getAttribute("data-action");b&&"original-link"!==b&&a.preventDefault()};document.onmouseup=function(a){if(2!==a.button)if("INPUT"===a.target.nodeName&&"auto-select"===
|
||||
a.target.className)a.target.select();else{var b=a.target.getAttribute("data-action");if(b){k.b="mouse";var c;a:{for(element=a.target;element&&element.parentNode;)if(element=element.parentNode,element.tagName&&"article"===element.tagName.toLowerCase()){c=element;break a}c=void 0}switch(b){case "refresh-all":g.j();break;case "refresh-feed":c&&g.i(c);break;case "mark-read":c&&q.m(c);break;case "mark-unread":c&&q.o(c);break;case "mark-removed":c&&q.n(c);break;case "bookmark":c&&q.h(c);break;case "download-item":c&&
|
||||
q.d(c);break;case "original-link":c&&q.f(c);break;case "mark-all-read":q.e("?action=unread");break;case "mark-feed-read":q.e("?action=feed-items&feed_id="+a.target.getAttribute("data-feed-id"))}}}}},k:function(){document.onkeypress=function(a){if(!e(a))if(k.b="keyboard",d.push(a.keyCode||a.which),103===d[0])switch(d[1]){case void 0:break;case 117:window.location.href="?action=unread";d=[];break;case 98:window.location.href="?action=bookmarks";d=[];break;case 104:window.location.href="?action=history";
|
||||
d=[];break;case 115:window.location.href="?action=feeds";d=[];break;case 112:window.location.href="?action=config";d=[];break;default:d=[]}else{d=[];var b=document.getElementById("current-item");switch(a.keyCode||a.which){case 100:b&&q.d(b);break;case 112:case 107:m.g();break;case 110:case 106:m.c();break;case 118:b&&q.f(b);break;case 111:b&&q.r(b);break;case 109:b&&q.t(b);break;case 102:b&&q.h(b);break;case 104:m.q();break;case 108:m.p();break;case 114:g.j();break;case 63:m.s();break;case 122:q.u()}}};
|
||||
document.onkeydown=function(a){if(!e(a))switch(k.b="keyboard",a.keyCode||a.which){case 37:m.g();break;case 39:m.c()}}}}}(),m=function(){function e(a){var b=pageYOffset+document.documentElement.clientHeight;(0>b-(a.offsetTop+a.offsetHeight)||b-a.offsetTop>document.documentElement.clientHeight)&&window.scrollTo(0,a.offsetTop-10)}function d(){return document.getElementById("listing")?!0:!1}return{p:function(){var a=document.getElementById("next-page");a&&a.click()},q:function(){var a=document.getElementById("previous-page");
|
||||
a&&a.click()},c:function(){var a=document.getElementById("next-item");if(a)a.click();else if(d())if(a=document.getElementsByTagName("article"),document.getElementById("current-item"))for(var b=0,c=a.length;b<c;b++){if("current-item"===a[b].id){b+1<c&&(a[b].id="item-"+a[b].getAttribute("data-item-id"),a[b+1].id="current-item",e(a[b+1]));break}}else a[0].id="current-item",e(a[0])},g:function(){var a=document.getElementById("previous-item");if(a)a.click();else if(d())if(a=document.getElementsByTagName("article"),
|
||||
document.getElementById("current-item"))for(var b=a.length-1;0<=b;b--){if("current-item"===a[b].id){0<=b-1&&(a[b].id="item-"+a[b].getAttribute("data-item-id"),a[b-1].id="current-item",e(a[b-1]));break}}else a[a.length-1].id="current-item",e(a[a.length-1])},s:function(){open("?action=show-help","Help","width=320,height=450,location=no,scrollbars=no,status=no,toolbar=no")},a:d}}();k.k();k.l();
|
||||
var g=function(){var e=[];return{j:function(d,a){var b=d.querySelector("span.items-count");if(b){var c=d.getAttribute("data-feed-id"),e=d.querySelector("h2:first-of-type");e.className="loading-icon";var l=new XMLHttpRequest;l.onload=function(){e.className="";d.removeAttribute("data-feed-error");var c=d.querySelector(".feed-last-checked");c&&(c.innerHTML=c.getAttribute("data-after-update"));c=JSON.parse(this.responseText);c.result?b.innerHTML=c.items_count.items_unread+"/"+c.items_count.items_total:
|
||||
d.setAttribute("data-feed-error","1");a&&a(c)};l.open("POST","?action=refresh-feed&feed_id="+c,!0);l.send()}},k:function(){var d=Array.prototype.slice.call(document.querySelectorAll("article:not([data-feed-disabled])")),a=setInterval(function(){for(;0<d.length&&5>e.length;){var b=d.shift();e.push(parseInt(b.getAttribute("data-feed-id"),10));g.j(b,function(b){b=e.indexOf(b.feed_id);0<=b&&e.splice(b,1);0===d.length&&0===e.length&&(clearInterval(a),window.location.href="?action=unread")})}},100)}}}(),
|
||||
t=function(){function e(a){return item_id=a.getAttribute("data-item-id")}function d(a){if(a&&a.hasAttribute("data-reverse-label")){var b=a.innerHTML;a.innerHTML=a.getAttribute("data-reverse-label");a.setAttribute("data-reverse-label",b)}}function a(a){"mouse"!==k.b&&m.d();a.parentNode.removeChild(a);r--}function b(){-1<window.location.href.indexOf("nothing_to_read=1")&&0<n?window.location.href="?action=unread":0===r&&window.location.reload();var a=document.getElementById("page-counter");a&&(a.textContent=
|
||||
r||"");document.getElementById("nav-counter").textContent=n||"";var b=document.querySelector("div.page-header h2:first-of-type");if(b)pageHeading=b.firstChild.nodeValue;else if(b=document.querySelector("article.item h1:first-of-type")){document.title=b.textContent;return}switch(document.querySelector("section.page").getAttribute("data-item-page")){case "unread":document.title="Miniflux ("+n+")";break;case "feed-items":document.title="("+r+") "+pageHeading;break;default:document.title=a?pageHeading+
|
||||
" ("+r+")":pageHeading}}function c(f){var h=e(f),c=new XMLHttpRequest;c.onload=function(){if(m.a()){if(f.getAttribute("data-hide"))a(f);else{f.setAttribute("data-item-status","read");var c=f.querySelector("a.mark");d(c);(c=f.querySelector("a.mark"))&&c.setAttribute("data-action","mark-unread")}n--;b()}};c.open("POST","?action=mark-item-read&id="+h,!0);c.send()}function v(f){var c=e(f),p=new XMLHttpRequest;p.onload=function(){if(m.a()){if(f.getAttribute("data-hide"))a(f);else{f.setAttribute("data-item-status",
|
||||
"unread");var c=f.querySelector("a.mark");d(c);(c=f.querySelector("a.mark"))&&c.setAttribute("data-action","mark-read")}n++;b()}};p.open("POST","?action=mark-item-unread&id="+c,!0);p.send()}var l=[],q=!1,n=function(){var a=document.getElementById("nav-counter");if(a)return counter=parseInt(a.textContent,10)||0}(),r=function(){var a=document.getElementById("page-counter");if(a)return counter=parseInt(a.textContent,10)||0}();return{p:c,r:v,q:function(f){var c=e(f),d=new XMLHttpRequest;d.onload=function(){m.a()&&
|
||||
(a(f),"unread"===f.getAttribute("data-item-status")&&n--,b())};d.open("POST","?action=mark-item-removed&id="+c,!0);d.send()},i:function(c){var h=e(c),p="1"===c.getAttribute("data-item-bookmark")?"0":"1",u=new XMLHttpRequest;u.onload=function(){var h=document.querySelector("section.page");if(m.a()&&"bookmarks"===h.getAttribute("data-item-page"))a(c),b();else if(c.setAttribute("data-item-bookmark",p),m.a())h=c.querySelector("a.bookmark"),d(h);else if((h=c.querySelector("a.bookmark-icon"))&&h.hasAttribute("data-reverse-title")){var e=
|
||||
h.getAttribute("title");h.setAttribute("title",h.getAttribute("data-reverse-title"));h.setAttribute("data-reverse-title",e)}};u.open("POST","?action=bookmark&id="+h+"&value="+p,!0);u.send()},A:function(a){var b=a.getAttribute("data-item-status");"read"===b?v(a):"unread"===b&&c(a)},v:function(a){(a=a.querySelector("a.show"))&&a.click()},g:function(a){var b=a.querySelector("a.original");b&&("unread"===a.getAttribute("data-item-status")&&c(a),b.removeAttribute("data-action"),"mouse"!==k.b&&b.click())},
|
||||
e:function(a){var b=document.getElementById("download-item");if(b){b.innerHTML=" "+b.getAttribute("data-before-message");b.className="loading-icon";var c=new XMLHttpRequest;c.onload=function(){var a=JSON.parse(c.responseText);b.className="";if(a.result){var d=document.getElementById("item-content");d&&(d.innerHTML=a.content);b.innerHTML=b.getAttribute("data-after-message")}else b.innerHTML=b.getAttribute("data-failure-message")};a=e(a);c.open("POST","?action=download-item&id="+a,!0);c.send()}},f:function(a){for(var b=
|
||||
document.getElementsByTagName("article"),c=[],d=0,l=b.length;d<l;d++)c.push(e(b[d]));b=new XMLHttpRequest;b.onload=function(){window.location.href=a};b.open("POST","?action=mark-items-as-read",!0);b.send(JSON.stringify(c))},B:function(){for(var a=["#current-item h1","#item-content","#listing #current-item h2","#listing #current-item .preview"],b=0;b<a.length;b++){var c=document.querySelector(a[b]);c&&(c.dir=""==c.dir?"rtl":"")}},C:function(){return q},c:function(){if(!document.hidden||!q){var a=new XMLHttpRequest;
|
||||
a.onload=function(){var a=0===l.length,c=!1,d=JSON.parse(this.responseText),e;for(e in d.feeds){var f=d.feeds[e];if(!l.hasOwnProperty(e)||f.time>l[e])l[e]=f.time,"unread"===f.status&&(c=!0)}document.hidden||d.nbUnread===n&&!q?document.hidden&&!a&&c&&(q=!0,document.title="\u21bb "+document.title):(q=!1,n=d.nbUnread,b())};a.open("POST","?action=latest-feeds-items",!0);a.send()}}}}(),k=function(){function e(a){if(63!==a.keyCode&&63!==a.which&&(a.ctrlKey||a.shiftKey||a.altKey||a.metaKey))return!0;a=a.target||
|
||||
a.srcElement;return"INPUT"===a.tagName||"TEXTAREA"===a.tagName?!0:!1}var d=[];return{b:"",n:function(){document.onclick=function(a){var b=a.target.getAttribute("data-action");b&&"original-link"!==b&&a.preventDefault()};document.onmouseup=function(a){if(2!==a.button)if("INPUT"===a.target.nodeName&&"auto-select"===a.target.className)a.target.select();else{var b=a.target.getAttribute("data-action");if(b){k.b="mouse";var c;a:{for(element=a.target;element&&element.parentNode;)if(element=element.parentNode,
|
||||
element.tagName&&"article"===element.tagName.toLowerCase()){c=element;break a}c=void 0}switch(b){case "refresh-all":g.k();break;case "refresh-feed":c&&g.j(c);break;case "mark-read":c&&t.p(c);break;case "mark-unread":c&&t.r(c);break;case "mark-removed":c&&t.q(c);break;case "bookmark":c&&t.i(c);break;case "download-item":c&&t.e(c);break;case "original-link":c&&t.g(c);break;case "mark-all-read":t.f("?action=unread");break;case "mark-feed-read":t.f("?action=feed-items&feed_id="+a.target.getAttribute("data-feed-id"))}}}}},
|
||||
m:function(){document.onkeypress=function(a){if(!e(a))if(k.b="keyboard",d.push(a.keyCode||a.which),103===d[0])switch(d[1]){case void 0:break;case 117:window.location.href="?action=unread";d=[];break;case 98:window.location.href="?action=bookmarks";d=[];break;case 104:window.location.href="?action=history";d=[];break;case 115:window.location.href="?action=feeds";d=[];break;case 112:window.location.href="?action=config";d=[];break;default:d=[]}else{d=[];var b=document.getElementById("current-item");
|
||||
switch(a.keyCode||a.which){case 100:b&&t.e(b);break;case 112:case 107:m.h();break;case 110:case 106:m.d();break;case 118:b&&t.g(b);break;case 111:b&&t.v(b);break;case 109:b&&t.A(b);break;case 102:b&&t.i(b);break;case 104:m.t();break;case 108:m.s();break;case 114:g.k();break;case 63:m.w();break;case 122:t.B()}}};document.onkeydown=function(a){if(!e(a))switch(k.b="keyboard",a.keyCode||a.which){case 37:m.h();break;case 39:m.d()}}},o:function(){document.addEventListener("visibilitychange",function(){!document.hidden&&
|
||||
t.C()&&t.c()})}}}(),m=function(){function e(a){var b=pageYOffset+document.documentElement.clientHeight;(0>b-(a.offsetTop+a.offsetHeight)||b-a.offsetTop>document.documentElement.clientHeight)&&window.scrollTo(0,a.offsetTop-10)}function d(){return document.getElementById("listing")?!0:!1}return{s:function(){var a=document.getElementById("next-page");a&&a.click()},t:function(){var a=document.getElementById("previous-page");a&&a.click()},d:function(){var a=document.getElementById("next-item");if(a)a.click();
|
||||
else if(d())if(a=document.getElementsByTagName("article"),document.getElementById("current-item"))for(var b=0,c=a.length;b<c;b++){if("current-item"===a[b].id){b+1<c&&(a[b].id="item-"+a[b].getAttribute("data-item-id"),a[b+1].id="current-item",e(a[b+1]));break}}else a[0].id="current-item",e(a[0])},h:function(){var a=document.getElementById("previous-item");if(a)a.click();else if(d())if(a=document.getElementsByTagName("article"),document.getElementById("current-item"))for(var b=a.length-1;0<=b;b--){if("current-item"===
|
||||
a[b].id){0<=b-1&&(a[b].id="item-"+a[b].getAttribute("data-item-id"),a[b-1].id="current-item",e(a[b-1]));break}}else a[a.length-1].id="current-item",e(a[a.length-1])},w:function(){open("?action=show-help","Help","width=320,height=450,location=no,scrollbars=no,status=no,toolbar=no")},a:d}}();
|
||||
({D:function(){},u:function(){k.m();k.n();k.o();this.l()},l:function(){var e=new XMLHttpRequest;e.onload=function(){var d=JSON.parse(this.responseText);0<d.frontend_updatecheck_interval&&(t.c(),setInterval(function(){t.c()},6E4*d.frontend_updatecheck_interval))};e.open("POST","?action=get-config",!0);e.send(JSON.stringify(["frontend_updatecheck_interval"]))}}).u();
|
@ -1,12 +1,42 @@
|
||||
var Miniflux = {};
|
||||
|
||||
/**
|
||||
* @define {boolean}
|
||||
*/
|
||||
var COMPILED = false;
|
||||
|
||||
Miniflux.App = (function() {
|
||||
|
||||
return {
|
||||
Log: function(message) {
|
||||
if (! COMPILED) {
|
||||
console.log(message);
|
||||
}
|
||||
},
|
||||
Run: function() {
|
||||
Miniflux.Event.ListenKeyboardEvents();
|
||||
Miniflux.Event.ListenMouseEvents();
|
||||
Miniflux.Event.ListenVisibilityEvents();
|
||||
this.FrontendUpdateCheck();
|
||||
},
|
||||
FrontendUpdateCheck: function() {
|
||||
var request = new XMLHttpRequest();
|
||||
request.onload = function() {
|
||||
var response = JSON.parse(this.responseText);
|
||||
|
||||
if (response['frontend_updatecheck_interval'] > 0) {
|
||||
Miniflux.App.Log('Frontend updatecheck interval in minutes: ' + response['frontend_updatecheck_interval']);
|
||||
Miniflux.Item.CheckForUpdates();
|
||||
setInterval(function(){ Miniflux.Item.CheckForUpdates(); }, response['frontend_updatecheck_interval']*60*1000);
|
||||
}
|
||||
else {
|
||||
Miniflux.App.Log('Frontend updatecheck disabled');
|
||||
}
|
||||
};
|
||||
|
||||
request.open("POST", "?action=get-config", true);
|
||||
request.send(JSON.stringify(['frontend_updatecheck_interval']));
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
||||
|
@ -208,8 +208,17 @@ Miniflux.Event = (function() {
|
||||
Miniflux.Nav.SelectNextItem();
|
||||
break;
|
||||
}
|
||||
};
|
||||
},
|
||||
ListenVisibilityEvents: function() {
|
||||
document.addEventListener('visibilitychange', function() {
|
||||
Miniflux.App.Log('document.visibilityState: ' + document.visibilityState);
|
||||
|
||||
if (!document.hidden && Miniflux.Item.hasNewUnread()) {
|
||||
Miniflux.App.Log('Need to update the unread counter with fresh values from the database');
|
||||
Miniflux.Item.CheckForUpdates();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
||||
|
@ -1,5 +1,11 @@
|
||||
Miniflux.Item = (function() {
|
||||
|
||||
// timestamp of the latest item per feed ever seen
|
||||
var latest_feeds_items = [];
|
||||
|
||||
// indicator for new unread items
|
||||
var unreadItems = false;
|
||||
|
||||
var nbUnreadItems = function() {
|
||||
var navCounterElement = document.getElementById("nav-counter");
|
||||
|
||||
@ -93,17 +99,36 @@ Miniflux.Item = (function() {
|
||||
|
||||
function updateCounters()
|
||||
{
|
||||
// imitate special handling within miniflux
|
||||
if (nbPageItems === 0) {
|
||||
// redirect to unread if we're on a nothing to read page
|
||||
if (window.location.href.indexOf('nothing_to_read=1') > -1 && nbUnreadItems > 0) {
|
||||
window.location.href = '?action=unread';
|
||||
} // reload to get a nothing to read page
|
||||
else if (nbPageItems === 0) {
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
var pageCounterElement = document.getElementById("page-counter");
|
||||
pageCounterElement.textContent = nbPageItems || '';
|
||||
if (pageCounterElement) pageCounterElement.textContent = nbPageItems || '';
|
||||
|
||||
var navCounterElement = document.getElementById("nav-counter");
|
||||
navCounterElement.textContent = nbUnreadItems || '';
|
||||
|
||||
var pageHeadingElement = document.querySelector("div.page-header h2:first-of-type");
|
||||
if (pageHeadingElement) {
|
||||
pageHeading = pageHeadingElement.firstChild.nodeValue;
|
||||
}
|
||||
else {
|
||||
// special handling while viewing an article.
|
||||
// 1. The article does not have a page-header element
|
||||
// 2. An article could be opened from any page and has the original
|
||||
// page as data-item-page value
|
||||
var itemHeading = document.querySelector("article.item h1:first-of-type");
|
||||
if (itemHeading) {
|
||||
document.title = itemHeading.textContent;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// pagetitle depends on current page
|
||||
var sectionElement = document.querySelector("section.page");
|
||||
switch (sectionElement.getAttribute("data-item-page")) {
|
||||
@ -111,10 +136,15 @@ Miniflux.Item = (function() {
|
||||
document.title = "Miniflux (" + nbUnreadItems + ")";
|
||||
break;
|
||||
case "feed-items":
|
||||
document.title = "(" + nbPageItems + ") " + pageCounterElement.parentNode.firstChild.nodeValue;
|
||||
document.title = "(" + nbPageItems + ") " + pageHeading;
|
||||
break;
|
||||
default:
|
||||
document.title = pageCounterElement.parentNode.firstChild.nodeValue + " (" + nbPageItems + ")";
|
||||
if (pageCounterElement) {
|
||||
document.title = pageHeading + " (" + nbPageItems + ")";
|
||||
}
|
||||
else {
|
||||
document.title = pageHeading;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -283,6 +313,56 @@ Miniflux.Item = (function() {
|
||||
tag.dir = tag.dir == "" ? "rtl" : "";
|
||||
}
|
||||
}
|
||||
},
|
||||
hasNewUnread: function() {
|
||||
return unreadItems;
|
||||
},
|
||||
CheckForUpdates: function() {
|
||||
if (document.hidden && unreadItems) {
|
||||
Miniflux.App.Log('We already have updates, no need to check again');
|
||||
return;
|
||||
}
|
||||
|
||||
var request = new XMLHttpRequest();
|
||||
request.onload = function() {
|
||||
var first_run = (latest_feeds_items.length === 0);
|
||||
var current_unread = false;
|
||||
var response = JSON.parse(this.responseText);
|
||||
|
||||
for(var feed_id in response['feeds']) {
|
||||
var current_feed = response['feeds'][feed_id];
|
||||
|
||||
if (!latest_feeds_items.hasOwnProperty(feed_id) || current_feed.time > latest_feeds_items[feed_id]) {
|
||||
Miniflux.App.Log('feed ' + feed_id + ': New item(s)');
|
||||
latest_feeds_items[feed_id] = current_feed.time;
|
||||
|
||||
if (current_feed.status === 'unread') {
|
||||
Miniflux.App.Log('feed ' + feed_id + ': New unread item(s)');
|
||||
current_unread = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Miniflux.App.Log('first_run: ' + first_run + ', current_unread: ' + current_unread + ', response.nbUnread: ' + response['nbUnread'] + ', nbUnreadItems: ' + nbUnreadItems);
|
||||
|
||||
if (!document.hidden && (response['nbUnread'] !== nbUnreadItems || unreadItems)) {
|
||||
Miniflux.App.Log('Counter changed! Updating unread counter.');
|
||||
unreadItems = false;
|
||||
nbUnreadItems = response['nbUnread'];
|
||||
updateCounters();
|
||||
}
|
||||
else if (document.hidden && !first_run && current_unread) {
|
||||
Miniflux.App.Log('New Unread! Updating pagetitle.');
|
||||
unreadItems = true;
|
||||
document.title = "↻ " + document.title;
|
||||
} else {
|
||||
Miniflux.App.Log('No update.');
|
||||
}
|
||||
|
||||
Miniflux.App.Log('unreadItems: ' + unreadItems);
|
||||
};
|
||||
request.open("POST", "?action=latest-feeds-items", true);
|
||||
request.send();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -172,6 +172,22 @@ Router\post_action('config', function() {
|
||||
)));
|
||||
});
|
||||
|
||||
Router\post_action('get-config', function() {
|
||||
$return = array();
|
||||
$options = Request\values();
|
||||
|
||||
if (empty($options)) {
|
||||
$return = Model\Config\get_all();
|
||||
}
|
||||
else {
|
||||
foreach ($options as $name) {
|
||||
$return[$name] = Model\Config\get($name);
|
||||
}
|
||||
}
|
||||
|
||||
Response\json($return);
|
||||
});
|
||||
|
||||
// Display help page
|
||||
Router\get_action('help', function() {
|
||||
|
||||
|
@ -200,3 +200,21 @@ Router\get_action('mark-item-removed', function() {
|
||||
|
||||
Response\Redirect('?action='.$redirect.'&offset='.$offset.'&feed_id='.$feed_id);
|
||||
});
|
||||
|
||||
Router\post_action('latest-feeds-items', function() {
|
||||
$items = Model\Item\get_latest_feeds_items();
|
||||
$nb_unread_items = Model\Item\count_by_status('unread');
|
||||
|
||||
$feeds = array_reduce($items, function ($result, $item) {
|
||||
$result[$item['id']] = array(
|
||||
'time' => $item['updated'] ?: 0,
|
||||
'status' => $item['status']
|
||||
);
|
||||
return $result;
|
||||
}, array());
|
||||
|
||||
Response\json(array(
|
||||
'feeds' => $feeds,
|
||||
'nbUnread' => $nb_unread_items
|
||||
));
|
||||
});
|
||||
|
@ -231,4 +231,5 @@ return array(
|
||||
// 'Download favicons' => '',
|
||||
// 'general' => '',
|
||||
// 'An error occurred during the last check. Refresh the feed manually and check the %sconsole%s for errors afterwards!' => '',
|
||||
// 'Frontend updatecheck interval in minutes' => '',
|
||||
);
|
||||
|
@ -231,4 +231,5 @@ return array(
|
||||
'Download favicons' => 'Favicons herunterladen',
|
||||
'general' => 'allgemein',
|
||||
'An error occurred during the last check. Refresh the feed manually and check the %sconsole%s for errors afterwards!' => 'Fehler bei der letzten Aktualisierung. Aktualisiere den Feed manuell und prüfe die %sKonsole%s anschließend auf Fehler!',
|
||||
// 'Frontend updatecheck interval in minutes' => '',
|
||||
);
|
||||
|
@ -231,4 +231,5 @@ return array(
|
||||
// 'Download favicons' => '',
|
||||
// 'general' => '',
|
||||
// 'An error occurred during the last check. Refresh the feed manually and check the %sconsole%s for errors afterwards!' => '',
|
||||
// 'Frontend updatecheck interval in minutes' => '',
|
||||
);
|
||||
|
@ -231,4 +231,5 @@ return array(
|
||||
'Download favicons' => 'Télécharger les icônes des sites web',
|
||||
'general' => 'général',
|
||||
'An error occurred during the last check. Refresh the feed manually and check the %sconsole%s for errors afterwards!' => 'Une erreur est survenue pendant la dernière vérification. Actualisez, le flux manuellement and vérifiez les erreurs dans la %sconsole%s !',
|
||||
'Frontend updatecheck interval in minutes' => 'Frontend updatecheck interval in minutes',
|
||||
);
|
||||
|
@ -231,4 +231,5 @@ return array(
|
||||
// 'Download favicons' => '',
|
||||
// 'general' => '',
|
||||
// 'An error occurred during the last check. Refresh the feed manually and check the %sconsole%s for errors afterwards!' => '',
|
||||
// 'Frontend updatecheck interval in minutes' => '',
|
||||
);
|
||||
|
@ -231,4 +231,5 @@ return array(
|
||||
'Download favicons' => 'Download favicon',
|
||||
// 'general' => '',
|
||||
// 'An error occurred during the last check. Refresh the feed manually and check the %sconsole%s for errors afterwards!' => '',
|
||||
// 'Frontend updatecheck interval in minutes' => '',
|
||||
);
|
||||
|
@ -231,4 +231,5 @@ return array(
|
||||
// 'Download favicons' => '',
|
||||
// 'general' => '',
|
||||
// 'An error occurred during the last check. Refresh the feed manually and check the %sconsole%s for errors afterwards!' => '',
|
||||
// 'Frontend updatecheck interval in minutes' => '',
|
||||
);
|
||||
|
@ -282,6 +282,7 @@ function validate_modification(array $values)
|
||||
new Validators\Required('items_per_page', t('Value required')),
|
||||
new Validators\Integer('items_per_page', t('Must be an integer')),
|
||||
new Validators\Required('theme', t('Value required')),
|
||||
new Validators\Integer('frontend_updatecheck_interval', t('Must be an integer')),
|
||||
);
|
||||
|
||||
if (ENABLE_AUTO_UPDATE) {
|
||||
|
@ -64,6 +64,21 @@ function get_everything_since($timestamp)
|
||||
->findAll();
|
||||
}
|
||||
|
||||
function get_latest_feeds_items()
|
||||
{
|
||||
return Database::get('db')
|
||||
->table('feeds')
|
||||
->columns(
|
||||
'feeds.id',
|
||||
'MAX(items.updated) as updated',
|
||||
'items.status'
|
||||
)
|
||||
->join('items', 'feed_id', 'id')
|
||||
->groupBy('feeds.id')
|
||||
->orderBy('feeds.id')
|
||||
->findAll();
|
||||
}
|
||||
|
||||
// Get a list of [item_id => status,...]
|
||||
function get_all_status()
|
||||
{
|
||||
|
@ -5,7 +5,12 @@ namespace Schema;
|
||||
use PDO;
|
||||
use Model\Config;
|
||||
|
||||
const VERSION = 35;
|
||||
const VERSION = 36;
|
||||
|
||||
function version_36($pdo)
|
||||
{
|
||||
$pdo->exec('INSERT INTO settings ("key", "value") VALUES ("frontend_updatecheck_interval", 10)');
|
||||
}
|
||||
|
||||
function version_35($pdo)
|
||||
{
|
||||
|
@ -35,6 +35,9 @@
|
||||
<?= Helper\form_label(t('Theme'), 'theme') ?>
|
||||
<?= Helper\form_select('theme', $theme_options, $values, $errors) ?><br/>
|
||||
|
||||
<?= Helper\form_label(t('Frontend updatecheck interval in minutes'), 'frontend_updatecheck_interval') ?>
|
||||
<?= Helper\form_number('frontend_updatecheck_interval', $values, $errors, array('min="0"')) ?><br/>
|
||||
|
||||
<?php if (ENABLE_AUTO_UPDATE): ?>
|
||||
<?= Helper\form_label(t('Auto-Update URL'), 'auto_update_url') ?>
|
||||
<?= Helper\form_text('auto_update_url', $values, $errors, array('required')) ?><br/>
|
||||
|
Loading…
Reference in New Issue
Block a user