From 37c03d3a420853b61348de87150a69a567c00146 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 31 Aug 2011 15:25:42 +0400 Subject: [PATCH] implement automatic precaching for next normal and unread feeds allow opening cached next_unread_feed on f-q if available tweak getRelativeArticles() to scan forward only reenable prefetch_old server requests --- backend.php | 2 +- feedlist.js | 158 +++++++++++++++++++++++----------------- functions.php | 4 +- modules/backend-rpc.php | 5 +- tt-rss.js | 4 +- viewfeed.js | 84 +++++++++++++-------- 6 files changed, 154 insertions(+), 103 deletions(-) diff --git a/backend.php b/backend.php index d039740b3..e3260739f 100644 --- a/backend.php +++ b/backend.php @@ -257,7 +257,7 @@ $subop = db_escape_string($_REQUEST["subop"]); $view_mode = db_escape_string($_REQUEST["view_mode"]); $limit = (int) get_pref($link, "DEFAULT_ARTICLE_LIMIT"); - @$cat_view = db_escape_string($_REQUEST["cat"]); + @$cat_view = db_escape_string($_REQUEST["cat"]) == "true"; @$next_unread_feed = db_escape_string($_REQUEST["nuf"]); @$offset = db_escape_string($_REQUEST["skip"]); @$vgroup_last_feed = db_escape_string($_REQUEST["vgrlf"]); diff --git a/feedlist.js b/feedlist.js index ab21a30fb..fc3bcd9c7 100644 --- a/feedlist.js +++ b/feedlist.js @@ -48,7 +48,7 @@ function loadMoreHeadlines() { } -function viewfeed(feed, subop, is_cat, offset) { +function viewfeed(feed, subop, is_cat, offset, background) { try { if (is_cat == undefined) is_cat = false; @@ -57,6 +57,7 @@ function viewfeed(feed, subop, is_cat, offset) { if (subop == undefined) subop = ''; if (offset == undefined) offset = 0; + if (background == undefined) background = false; last_requested_article = 0; @@ -72,101 +73,112 @@ function viewfeed(feed, subop, is_cat, offset) { var force_nocache = false; - if (getActiveFeedId() != feed || offset == 0) { - active_post_id = 0; - _infscroll_disable = 0; - } - - if (!offset && !subop && cached_headlines) { - try { - render_local_headlines(feed, is_cat, JSON.parse(cached_headlines)); - return; - } catch (e) { - console.warn("render_local_headlines failed: " + e); - } - } - - if (offset != 0 && !subop) { - var date = new Date(); - var timestamp = Math.round(date.getTime() / 1000); - - if (_infscroll_request_sent && _infscroll_request_sent + 30 > timestamp) { - //console.log("infscroll request in progress, aborting"); - return; + if (!background) { + if (getActiveFeedId() != feed || offset == 0) { + active_post_id = 0; + _infscroll_disable = 0; } - _infscroll_request_sent = timestamp; - } + if (!offset && !subop && cached_headlines && !background) { + try { + render_local_headlines(feed, is_cat, JSON.parse(cached_headlines)); + return; + } catch (e) { + console.warn("render_local_headlines failed: " + e); + } + } - hideAuxDlg(); + if (offset != 0 && !subop) { + var date = new Date(); + var timestamp = Math.round(date.getTime() / 1000); + + if (_infscroll_request_sent && _infscroll_request_sent + 30 > timestamp) { + //console.log("infscroll request in progress, aborting"); + return; + } + + _infscroll_request_sent = timestamp; + } + + hideAuxDlg(); + } Form.enable("main_toolbar_form"); - var toolbar_form = document.forms["main_toolbar_form"]; var toolbar_query = Form.serialize("main_toolbar_form"); - if (toolbar_form.query) { - if (toolbar_form.query.value != "") { - force_nocache = true; - } - toolbar_form.query.value = ""; - } - var query = "?op=viewfeed&feed=" + feed + "&" + toolbar_query + "&subop=" + param_escape(subop); - if (_search_query) { - force_nocache = true; - query = query + "&" + _search_query; - _search_query = false; - } + if (!background) { + if (_search_query) { + force_nocache = true; + query = query + "&" + _search_query; + _search_query = false; + } - if (subop == "MarkAllRead") { + if (subop == "MarkAllRead") { - var show_next_feed = getInitParam("on_catchup_show_next_feed") == "1"; + var show_next_feed = getInitParam("on_catchup_show_next_feed") == "1"; - if (show_next_feed) { - var tree = dijit.byId("feedTree"); - var nuf = tree.model.getNextUnreadFeed(feed, is_cat); + if (show_next_feed) { + var nuf = getNextUnreadFeed(feed, is_cat); - if (nuf) { - var nuf_id = tree.model.store.getValue(nuf, 'bare_id'); + if (nuf) { + var cached_nuf = cache_get("feed:" + nuf + ":false"); - query = query + "&nuf=" + param_escape(nuf_id); + if (cached_nuf) { + + render_local_headlines(nuf, false, JSON.parse(cached_nuf)); + + var catchup_query = "?op=rpc&subop=catchupFeed&feed_id=" + + feed + "&is_cat=" + is_cat; + + console.log(catchup_query); + + new Ajax.Request("backend.php", { + parameters: catchup_query, + onComplete: function(transport) { + handle_rpc_json(transport); + } }); + + return; + } else { + query += "&nuf=" + param_escape(nuf); + } + } } } - } - if (is_cat) { - query = query + "&cat=1"; - } + if (offset != 0) { + query = query + "&skip=" + offset; - if (offset != 0) { - query = query + "&skip=" + offset; - - // to prevent duplicate feed titles when showing grouped vfeeds - if (vgroup_last_feed) { - query = query + "&vgrlf=" + param_escape(vgroup_last_feed); + // to prevent duplicate feed titles when showing grouped vfeeds + if (vgroup_last_feed) { + query = query + "&vgrlf=" + param_escape(vgroup_last_feed); + } } + + Form.enable("main_toolbar_form"); + + if (!offset) + if (!is_cat) { + if (!setFeedExpandoIcon(feed, is_cat, 'images/indicator_white.gif')) + notify_progress("Loading, please wait...", true); + } else { + notify_progress("Loading, please wait...", true); + } } - Form.enable("main_toolbar_form"); + query += "&cat=" + is_cat; console.log(query); - if (!offset) - if (!is_cat) { - if (!setFeedExpandoIcon(feed, is_cat, 'images/indicator_white.gif')) - notify_progress("Loading, please wait...", true); - } else { - notify_progress("Loading, please wait...", true); - } - new Ajax.Request("backend.php", { parameters: query, onComplete: function(transport) { setFeedExpandoIcon(feed, is_cat, 'images/blank_icon.gif'); - headlines_callback2(transport, offset); + headlines_callback2(transport, offset, background); } }); } catch (e) { @@ -428,3 +440,17 @@ function setFeedExpandoIcon(feed, is_cat, src) { } return false; } + +function getNextUnreadFeed(feed, is_cat) { + try { + var tree = dijit.byId("feedTree"); + var nuf = tree.model.getNextUnreadFeed(feed, is_cat); + + if (nuf) + return tree.model.store.getValue(nuf, 'bare_id'); + + } catch (e) { + exception_error("getNextUnreadFeed", e); + } +} + diff --git a/functions.php b/functions.php index bb6164d7b..f0691f209 100644 --- a/functions.php +++ b/functions.php @@ -2494,8 +2494,9 @@ if (!$owner_uid) $owner_uid = $_SESSION['uid']; - if (preg_match("/^-?[0-9][0-9]*$/", $feed) != false) { + //if (preg_match("/^-?[0-9][0-9]*$/", $feed) != false) { + if (is_numeric($feed)) { if ($cat_view) { if ($feed >= 0) { @@ -2519,7 +2520,6 @@ } } else if ($feed == -2) { - db_query($link, "UPDATE ttrss_user_entries SET unread = false,last_read = NOW() WHERE (SELECT COUNT(*) FROM ttrss_user_labels2 WHERE article_id = ref_id) > 0 diff --git a/modules/backend-rpc.php b/modules/backend-rpc.php index a89c5ee81..dff12aa5a 100644 --- a/modules/backend-rpc.php +++ b/modules/backend-rpc.php @@ -657,10 +657,11 @@ if ($subop == "catchupFeed") { $feed_id = db_escape_string($_REQUEST['feed_id']); - $is_cat = db_escape_string($_REQUEST['is_cat']); + $is_cat = db_escape_string($_REQUEST['is_cat']) == "true"; catchup_feed($link, $feed_id, $is_cat); + print json_encode(array("message" => "UPDATE_COUNTERS")); return; } @@ -758,7 +759,7 @@ if ($subop == "regenFeedKey") { $feed_id = db_escape_string($_REQUEST['id']); - $is_cat = (bool) db_escape_string($_REQUEST['is_cat']); + $is_cat = db_escape_string($_REQUEST['is_cat']) == "true"; $new_key = update_feed_access_key($link, $feed_id, $is_cat); diff --git a/tt-rss.js b/tt-rss.js index c8f4ab83e..3e0e95919 100644 --- a/tt-rss.js +++ b/tt-rss.js @@ -330,7 +330,9 @@ function init_second_stage() { loading_set_progress(30); - cache_clear(); + // can't use cache_clear() here because viewfeed might not have initialized yet + if ('sessionStorage' in window && window['sessionStorage'] !== null) + sessionStorage.clear(); console.log("second stage ok"); diff --git a/viewfeed.js b/viewfeed.js index c41c49a96..b040dc155 100644 --- a/viewfeed.js +++ b/viewfeed.js @@ -11,16 +11,19 @@ var last_requested_article = false; var catchup_id_batch = []; var catchup_timeout_id = false; +var feed_precache_timeout_id = false; + +var cids_requested = []; var has_storage = 'sessionStorage' in window && window['sessionStorage'] !== null; -function headlines_callback2(transport, offset) { +function headlines_callback2(transport, offset, background) { try { handle_rpc_json(transport); loading_set_progress(25); - console.log("headlines_callback2 [offset=" + offset + "]"); + console.log("headlines_callback2 [offset=" + offset + "] B:" + background); var is_cat = false; var feed_id = false; @@ -38,12 +41,13 @@ function headlines_callback2(transport, offset) { is_cat = reply['headlines']['is_cat']; feed_id = reply['headlines']['id']; + if (background) { + cache_headlines(feed_id, is_cat, reply['headlines']['toolbar'], reply['headlines']['content']); + return; + } + setActiveFeedId(feed_id, is_cat); - var update_btn = document.forms["main_toolbar_form"].update; - - update_btn.disabled = !(feed_id >= 0 && !is_cat); - try { if (offset == 0) { $("headlines-frame").scrollTop = 0; @@ -74,10 +78,6 @@ function headlines_callback2(transport, offset) { var hsp = $("headlines-spacer"); if (!hsp) hsp = new Element("DIV", {"id": "headlines-spacer"}); -/* if (!_infscroll_disable) - hsp.innerHTML = " " + - __("Loading, please wait..."); */ - dijit.byId('headlines-frame').domNode.appendChild(hsp); initHeadlinesMenu(); @@ -103,10 +103,6 @@ function headlines_callback2(transport, offset) { if (!hsp) hsp = new Element("DIV", {"id": "headlines-spacer"}); -/* if (!_infscroll_disable) - hsp.innerHTML = " " + - __("Loading, please wait..."); */ - fixHeadlinesOrder(getLoadedArticleIds()); c.domNode.appendChild(hsp); @@ -134,7 +130,8 @@ function headlines_callback2(transport, offset) { } } - cache_headlines(feed_id, is_cat, reply['headlines']['toolbar'], $("headlines-frame").innerHTML); + if (headlines_count > 0) + cache_headlines(feed_id, is_cat, reply['headlines']['toolbar'], $("headlines-frame").innerHTML); if (articles) { for (var i = 0; i < articles.length; i++) { @@ -247,22 +244,25 @@ function article_callback2(transport, id) { var reply = JSON.parse(transport.responseText); if (reply) { + var upic = $('FUPDPIC-' + id); if (upic) upic.src = 'images/blank_icon.gif'; - if (id != last_requested_article) { - console.log("requested article id is out of sequence, aborting"); - return; - } - reply.each(function(article) { if (active_post_id == article['id']) { render_article(article['content']); } + cids_requested.remove(article['id']); + cache_set("article:" + article['id'], article['content']); }); +// if (id != last_requested_article) { +// console.log("requested article id is out of sequence, aborting"); +// return; +// } + } else { console.warn("article_callback: returned invalid data"); @@ -293,16 +293,18 @@ function view(id) { var query = "?op=view&id=" + param_escape(id); - var neighbor_ids = getRelativePostIds(active_post_id); + var neighbor_ids = getRelativePostIds(id); /* only request uncached articles */ - var cids_to_request = Array(); + var cids_to_request = []; for (var i = 0; i < neighbor_ids.length; i++) { - if (!cache_get("article:" + neighbor_ids[i])) { - cids_to_request.push(neighbor_ids[i]); - } + if (cids_requested.indexOf(neighbor_ids[i]) == -1) + if (!cache_get("article:" + neighbor_ids[i])) { + cids_to_request.push(neighbor_ids[i]); + cids_requested.push(neighbor_ids[i]); + } } console.log("additional ids: " + cids_to_request.toString()); @@ -315,6 +317,24 @@ function view(id) { active_post_id = id; showArticleInHeadlines(id); + if (!feed_precache_timeout_id) { + feed_precache_timeout_id = window.setTimeout(function() { + var nuf = getNextUnreadFeed(getActiveFeedId(), activeFeedIsCat()); + var nf = dijit.byId("feedTree").getNextFeed(getActiveFeedId(), activeFeedIsCat()); + + if (nuf && !cache_get("feed:" + nuf + ":" + activeFeedIsCat())) + viewfeed(nuf, '', activeFeedIsCat(), 0, true); + + if (nf && !cache_get("feed:" + nf[0] + ":" + nf[1])) + viewfeed(nf[0], '', nf[1], 0, true); + + window.setTimeout(function() { + feed_precache_timeout_id = false; + }, 3000); + + }, 1000); + } + if (!cached_article) { var upic = $('FUPDPIC-' + id); @@ -334,8 +354,10 @@ function view(id) { query = query + "&mode=prefetch_old"; render_article(cached_article); - return; // do not do prefetch_old request - + // if we don't need to request any relative ids, we might as well skip + // the server roundtrip altogether + if (cids_to_request.length == 0) + return; } last_requested_article = id; @@ -1083,7 +1105,7 @@ function headlines_scroll_handler(e) { } } catch (e) { - exception_error("headlines_scroll_handler", e); + console.warn("headlines_scroll_handler: " + e); } } @@ -1734,14 +1756,14 @@ function getRelativePostIds(id, limit) { try { - if (!limit) limit = 3; + if (!limit) limit = 6; //3 var ids = getVisibleArticleIds(); for (var i = 0; i < ids.length; i++) { if (ids[i] == id) { for (var k = 1; k <= limit; k++) { - if (i > k-1) tmp.push(ids[i-k]); + //if (i > k-1) tmp.push(ids[i-k]); if (i < ids.length-k) tmp.push(ids[i+k]); } break; @@ -2029,7 +2051,7 @@ function player(elem) { } function cache_set(id, obj) { - console.log("cache_set: " + id); + //console.log("cache_set: " + id); if (has_storage) try { sessionStorage[id] = obj;