diff --git a/backend.php b/backend.php index a71b649e4..393246732 100644 --- a/backend.php +++ b/backend.php @@ -43,7 +43,7 @@ $print_exec_time = false; - if ((!$op || $op == "rpc" || $op == "rss" || $op == "digestSend" || + if ((!$op || $op == "rpc" || $op == "rss" || $op == "view" || $op == "digestSend" || $op == "globalUpdateFeeds") && !$_REQUEST["noxml"]) { header("Content-Type: application/xml; charset=utf-8"); } else { @@ -141,10 +141,9 @@ } - if ($op == "view") { + function outputArticleXML($link, $id, $feed_id, $mark_as_read = true) { - $id = db_escape_string($_GET["id"]); - $feed_id = db_escape_string($_GET["feed"]); + print "
"; } + + print "]]>
"; + + } + + if ($op == "view") { + + $id = db_escape_string($_GET["id"]); + $feed_id = db_escape_string($_GET["feed"]); + $cids = split(",", db_escape_string($_GET["cids"])); + $mode = db_escape_string($_GET["mode"]); + + print ""; + + // in prefetch mode we only output requested cids, main article + // just gets marked as read (it already exists in client cache) + + if ($mode != "prefetch") { + outputArticleXML($link, $id, $feed_id); + } else { + catchupArticleById($link, $id, 0); + } + + foreach ($cids as $cid) { + if ($cid) { + outputArticleXML($link, $cid, $feed_id, false); + } + } + + print ""; } if ($op == "viewfeed") { diff --git a/feedlist.js b/feedlist.js index 77a31302f..f5a675772 100644 --- a/feedlist.js +++ b/feedlist.js @@ -46,10 +46,10 @@ function viewfeed(feed, subop, is_cat, subop_param, skip_history, offset) { enableHotkeys(); - if (!skip_history) { +/* if (!skip_history) { history_push('FEED:' + feed + ':' + subop + ':' + is_cat + ':' + subop_param); - } + } */ var toolbar_query = Form.serialize("main_toolbar_form"); var toolbar_form = document.forms["main_toolbar_form"]; diff --git a/functions.js b/functions.js index 3a67b1837..09f430096 100644 --- a/functions.js +++ b/functions.js @@ -1667,3 +1667,34 @@ function logoutUser() { exception_error("logoutUser", e); } } + +// this only searches loaded headlines list, not in CDM +function getRelativePostIds(id) { + + debug("getRelativePostIds: " + id); + + var ids = new Array(); + var container = document.getElementById("headlinesList"); + + if (container) { + var rows = container.rows; + + for (var i = 0; i < rows.length; i++) { + var r_id = rows[i].id.replace("RROW-", ""); + + if (r_id == id) { + if (i > 0) ids.push(rows[i-1].id.replace("RROW-", "")); + if (i > 1) ids.push(rows[i-2].id.replace("RROW-", "")); + if (i > 2) ids.push(rows[i-3].id.replace("RROW-", "")); + + if (i < rows.length) ids.push(rows[i+1].id.replace("RROW-", "")); + if (i < rows.length-1) ids.push(rows[i+2].id.replace("RROW-", "")); + if (i < rows.length-2) ids.push(rows[i+3].id.replace("RROW-", "")); + + return ids; + } + } + } + + return false; +} diff --git a/functions.php b/functions.php index cdf592f70..6188e38d9 100644 --- a/functions.php +++ b/functions.php @@ -2811,6 +2811,23 @@ } } + function catchupArticleById($link, $id, $cmode) { + + if ($cmode == 0) { + db_query($link, "UPDATE ttrss_user_entries SET + unread = false,last_read = NOW() + WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]); + } else if ($cmode == 1) { + db_query($link, "UPDATE ttrss_user_entries SET + unread = true + WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]); + } else { + db_query($link, "UPDATE ttrss_user_entries SET + unread = NOT unread,last_read = NOW() + WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]); + } + } + function escape_for_form($s) { return htmlspecialchars(db_unescape_string($s)); } diff --git a/tt-rss.js b/tt-rss.js index b46c859ac..d22aa1fba 100644 --- a/tt-rss.js +++ b/tt-rss.js @@ -21,7 +21,7 @@ var xmlhttp_ctr = Ajax.getTransport(); var init_params = new Object(); -var op_history = new Array(); +//var op_history = new Array(); function tagsAreDisplayed() { return display_tags; @@ -696,7 +696,7 @@ function feedEditSave() { exception_error("feedEditSave (main)", e); } } - +/* function localHotkeyHandler(e) { var keycode; @@ -756,7 +756,7 @@ function localHotkeyHandler(e) { } debug("LKP=" + keycode); -} +} function history_push(op) { debug("history_push: " + op); @@ -776,4 +776,4 @@ function history_pop() { function history_clear() { debug("history_clear"); op_history.clear(); -} +} */ diff --git a/viewfeed.js b/viewfeed.js index 8a3518053..f5791eaf7 100644 --- a/viewfeed.js +++ b/viewfeed.js @@ -16,6 +16,8 @@ var _reload_feedlist_after_view = false; var _cdm_wd_timeout = false; var _cdm_wd_vishist = new Array(); +var article_cache = new Array(); + function catchup_callback() { if (xmlhttp_rpc.readyState == 4) { try { @@ -66,14 +68,49 @@ function headlines_callback() { } } -function article_callback() { - if (xmlhttp.readyState == 4) { - debug("article_callback"); +function render_article(article) { + try { var f = document.getElementById("content-frame"); try { f.scrollTop = 0; } catch (e) { }; - f.innerHTML = xmlhttp.responseText; + + f.innerHTML = article; + + } catch (e) { + exception_error("render_article", e); + } +} + +function article_callback() { + if (xmlhttp.readyState == 4) { + debug("article_callback"); + + try { + if (xmlhttp.responseXML) { + var reply = xmlhttp.responseXML.firstChild.firstChild; + + var articles = xmlhttp.responseXML.getElementsByTagName("article"); + + for (var i = 0; i < articles.length; i++) { + var a_id = articles[i].getAttribute("id"); + + debug("found id: " + a_id); + + if (a_id == active_post_id) { + debug("active article, rendering..."); + render_article(articles[i].firstChild.nodeValue); + } + + cache_inject(a_id, articles[i].firstChild.nodeValue); + } + + } else { + debug("article_callback: returned no XML object"); + } + } catch (e) { + exception_error("article_callback", e); + } var date = new Date(); last_article_view = date.getTime() / 1000; @@ -100,9 +137,13 @@ function view(id, feed_id, skip_history) { active_real_feed_id = feed_id; - if (!skip_history) { + var cached_article = cache_find(id); + + debug("cache check result: " + (cached_article != false)); + +/* if (!skip_history) { history_push("ARTICLE:" + id + ":" + feed_id); - } + } */ enableHotkeys(); @@ -119,11 +160,15 @@ function view(id, feed_id, skip_history) { xmlhttp.abort(); } - if (xmlhttp_ready(xmlhttp)) { + if (cached_article || xmlhttp_ready(xmlhttp)) { cleanSelected("headlinesList"); var crow = document.getElementById("RROW-" + active_post_id); + + var article_is_unread = crow.className.match("Unread"); + debug("article is unread: " + article_is_unread); + crow.className = crow.className.replace("Unread", ""); var upd_img_pic = document.getElementById("FUPDPIC-" + active_post_id); @@ -135,15 +180,55 @@ function view(id, feed_id, skip_history) { selectTableRowsByIdPrefix('headlinesList', 'RROW-', 'RCHK-', false); markHeadline(active_post_id); + var neighbor_ids = getRelativePostIds(active_post_id); + + /* only request uncached articles */ + + var cids_to_request = Array(); + + for (var i = 0; i < neighbor_ids.length; i++) { + if (!cache_check(neighbor_ids[i])) { + cids_to_request.push(neighbor_ids[i]); + } + } + + debug("additional ids: " + cids_to_request.toString()); + var date = new Date(); var timestamp = Math.round(date.getTime() / 1000); query = query + "&ts=" + timestamp; - notify_progress("Loading, please wait..."); + query = query + "&cids=" + cids_to_request.toString(); + + if (!cached_article) { + + notify_progress("Loading, please wait..."); + + debug(query); + + xmlhttp.open("GET", query, true); + xmlhttp.onreadystatechange=article_callback; + xmlhttp.send(null); + } else if (cached_article && article_is_unread) { + + query = query + "&mode=prefetch"; + + debug(query); + + xmlhttp.open("GET", query, true); + xmlhttp.onreadystatechange=article_callback; + xmlhttp.send(null); + + render_article(cached_article); + + } else if (cached_article) { + + render_article(cached_article); + + } + + cache_expire(); - xmlhttp.open("GET", query, true); - xmlhttp.onreadystatechange=article_callback; - xmlhttp.send(null); } else { debug("xmlhttp busy (@view)"); printLockingError(); @@ -650,3 +735,37 @@ function cdmWatchdog() { } } + + +function cache_inject(id, article) { + if (!article_cache[id]) { + debug("cache_article: miss: " + id); + + var cache_obj = new Array(); + + var d = new Date(); + cache_obj["entered"] = d.getTime() / 1000; + cache_obj["data"] = article; + + article_cache[id] = cache_obj; + + } else { + debug("cache_article: hit: " + id); + } +} + +function cache_find(id) { + if (typeof article_cache[id] != 'undefined') { + return article_cache[id]["data"]; + } else { + return false; + } +} + +function cache_check(id) { + return typeof article_cache[id] != 'undefined'; +} + +function cache_expire() { + /* TODO */ +}