diff options
Diffstat (limited to 'js')
-rw-r--r-- | js/Article.js | 99 | ||||
-rw-r--r-- | js/ArticleCache.js | 29 | ||||
-rw-r--r-- | js/Feeds.js | 7 | ||||
-rwxr-xr-x | js/Headlines.js | 175 | ||||
-rwxr-xr-x | js/common.js | 19 | ||||
-rw-r--r-- | js/tt-rss.js | 7 |
6 files changed, 236 insertions, 100 deletions
diff --git a/js/Article.js b/js/Article.js index 46769223e..b91501faf 100644 --- a/js/Article.js +++ b/js/Article.js @@ -137,58 +137,65 @@ define(["dojo/_base/declare"], function (declare) { } catch (e) { } }, - view: function (id, noexpand) { - this.setActive(id); - - if (!noexpand) { - console.log("loading article", id); + formatComments: function(hl) { + let comments = ""; - const cids = []; - - /* only request uncached articles */ - - this.getRelativeIds(id).each((n) => { - if (!ArticleCache.get(n)) - cids.push(n); - }); + if (hl.comments) { + let comments_msg = __("comments"); - const cached_article = ArticleCache.get(id); - - if (cached_article) { - console.log('rendering cached', id); - this.render(cached_article); - return false; + if (hl.num_comments > 0) { + comments_msg = hl.num_comments + " " + ngettext("comment", "comments", hl.num_comments) } - xhrPost("backend.php", {op: "article", method: "view", id: id, cids: cids.toString()}, (transport) => { - try { - const reply = App.handleRpcJson(transport); - - if (reply) { - - reply.each(function (article) { - if (Article.getActive() == article['id']) { - Article.render(article['content']); - } - ArticleCache.set(article['id'], article['content']); - }); - - } else { - console.error("Invalid object received: " + transport.responseText); - - Article.render("<div class='whiteBox'>" + - __('Could not display article (invalid object received - see error console for details)') + "</div>"); - } - - //const unread_in_buffer = $$("#headlines-frame > div[id*=RROW][class*=Unread]").length; - //request_counters(unread_in_buffer == 0); + comments = `<a href="${hl.comments}">(${comments_msg})</a>`; + } - Notify.close(); + return comments; + }, + formatOriginallyFrom: function(hl) { + return hl.orig_feed ? `<span> + ${__('Originally from:')} <a target="_blank" rel="noopener noreferrer" href="${hl.orig_feed[1]}">${hl.orig_feed[0]}</a> + </span>` : ""; + }, + view: function (id, noexpand) { + this.setActive(id); - } catch (e) { - App.Error.report(e); - } - }) + if (!noexpand) { + const hl = Headlines.objectById(id); + + if (hl) { + + const comments = this.formatComments(hl); + const originally_from = this.formatOriginallyFrom(hl); + + const article = `<div class="post post-${hl.id}"> + <div class="header"> + <div class="row"> + <div class="title"><a target="_blank" rel="noopener noreferrer" title="${hl.title}" href="${hl.link}">${hl.title}</a></div> + <div class="date">${hl.updated_long}</div> + </div> + <div class="row"> + <div class="buttons left">${hl.buttons_left}</div> + <div class="comments">${comments}</div> + <div class="author">${hl.author}</div> + <i class="material-icons">label_outline</i> + <span id="ATSTR-${hl.id}">${hl.tags_str}</span> + <a title="${__("Edit tags for this article")}" href="#" + onclick="Article.editTags(${hl.id})">(+)</a> + <div class="buttons right">${hl.buttons}</div> + </div> + </div> + <div id="POSTNOTE-${hl.id}">${hl.note}</div> + <div class="content" lang="${hl.lang ? hl.lang : 'en'}"> + ${originally_from} + ${hl.content} + ${hl.enclosures} + </div> + </div>`; + + Headlines.toggleUnread(id, 0); + this.render(article); + } } return false; diff --git a/js/ArticleCache.js b/js/ArticleCache.js deleted file mode 100644 index ce34d00d9..000000000 --- a/js/ArticleCache.js +++ /dev/null @@ -1,29 +0,0 @@ -'use strict' -/* global __, ngettext */ -define(["dojo/_base/declare"], function (declare) { - ArticleCache = { - has_storage: 'sessionStorage' in window && window['sessionStorage'] !== null, - set: function (id, obj) { - if (this.has_storage) - try { - sessionStorage["article:" + id] = obj; - } catch (e) { - sessionStorage.clear(); - } - }, - get: function (id) { - if (this.has_storage) - return sessionStorage["article:" + id]; - }, - clear: function () { - if (this.has_storage) - sessionStorage.clear(); - }, - del: function (id) { - if (this.has_storage) - sessionStorage.removeItem("article:" + id); - }, - } - - return ArticleCache; -}); diff --git a/js/Feeds.js b/js/Feeds.js index 5f7765119..52c1476f7 100644 --- a/js/Feeds.js +++ b/js/Feeds.js @@ -340,11 +340,6 @@ define(["dojo/_base/declare"], function (declare) { if (offset != 0) { query.skip = offset; - - // to prevent duplicate feed titles when showing grouped vfeeds - if (Headlines.vgroup_last_feed != undefined) { - query.vgrlf = Headlines.vgroup_last_feed; - } } else if (!is_cat && feed == this.getActive() && !params.method) { query.m = "ForceUpdate"; } @@ -363,7 +358,7 @@ define(["dojo/_base/declare"], function (declare) { if (viewfeed_debug) { window.open("backend.php?" + dojo.objectToQuery( - Object.assign({debug: 1, csrf_token: App.getInitParam("csrf_token")}, query) + Object.assign({csrf_token: App.getInitParam("csrf_token")}, query) )); } diff --git a/js/Headlines.js b/js/Headlines.js index 2b33ed396..0c8ce5abf 100755 --- a/js/Headlines.js +++ b/js/Headlines.js @@ -4,7 +4,7 @@ define(["dojo/_base/declare"], function (declare) { Headlines = { vgroup_last_feed: undefined, _headlines_scroll_timeout: 0, - loaded_article_ids: [], + headlines: [], current_first_id: 0, catchup_id_batch: [], click: function (event, id, in_body) { @@ -239,6 +239,140 @@ define(["dojo/_base/declare"], function (declare) { } } }, + objectById: function (id){ + return this.headlines[id]; + }, + renderHeadline: function (headlines, hl) { + let row = null; + + let row_class = ""; + + if (hl.marked) row_class += " marked"; + if (hl.published) row_class += " published"; + if (hl.unread) row_class += " Unread"; + + if (headlines.vfeed_group_enabled && hl.feed_title && this.vgroup_last_feed != hl.feed_id) { + let vgrhdr = `<div data-feed-id='${hl.feed_id}' class='feed-title'> + <div style='float : right'>${hl.feed_icon}</div> + <a class="title" href="#" onclick="Feeds.open({feed:${hl.feed_id}})">${hl.feed_title} + <a class="catchup" onclick="Feeds.catchupFeedInGroup(${hl.feed_id})" href="#">${__('mark feed as read')}</a> + </div>` + + const tmp = document.createElement("div"); + tmp.innerHTML = vgrhdr; + + $("headlines-frame").appendChild(tmp.firstChild); + + this.vgroup_last_feed = hl.feed_id; + } + + if (App.isCombinedMode()) { + row_class += App.getInitParam("cdm_expanded") ? " expanded" : " expandable"; + + const comments = Article.formatComments(hl); + const originally_from = Article.formatOriginallyFrom(hl); + + row = `<div class="cdm ${row_class} ${hl.score_class}" id="RROW-${hl.id}" data-article-id="${hl.id}" data-orig-feed-id="${hl.feed_id}" + data-content="${escapeHtml(hl.content)}" onmouseover="Article.mouseIn(${hl.id})" onmouseout="Article.mouseOut(${hl.id})"> + + <div class="header"> + <div class="left"> + <input dojoType="dijit.form.CheckBox" type="checkbox" onclick="Headlines.onRowChecked(this)" class='rchk'> + <i class="marked-pic marked-${hl.id} material-icons" onclick="Headlines.toggleMark(${hl.id})">star</i> + <i class="pub-pic pub-${hl.id} material-icons" onclick="Headlines.togglePub(${hl.id})">rss_feed</i> + </div> + + <span onclick="return Headlines.click(event, ${hl.id});" data-article-id="${hl.id}" class="titleWrap hlMenuAttach"> + <a class="title" title="${hl.title}" target="_blank" rel="noopener noreferrer" href="${hl.link}"> + ${hl.title}</a> + <span class="author">${hl.author}</span> + <span class="HLLCTR-${hl.id}">${hl.labels}</span> + ${hl.cdm_excerpt ? hl.cdm_excerpt : ""} + </span> + + <div class="feed"> + <a href="#" style="background-color: rgba(${hl.favicon_avg_color_rgba})" + onclick="Feeds.open({feed:${hl.feed_id}})">${hl.feed_title}</a> + </div> + + <span class="updated" title="${hl.imported}">${hl.updated}</span> + + <div class="right"> + <i class="material-icons icon-score" title="${hl.score}" data-score="${hl.score}" + onclick="Article.setScore(${hl.id}, this)">${hl.score_pic}</i> + + <span style="cursor : pointer" title="${hl.feed_title}" onclick="Feeds.open({feed:${hl.feed_id}})"> + ${hl.feed_icon}</span> + </div> + + </div> + + <div class="content" onclick="return Headlines.click(event, ${hl.id}, true);"> + <div id="POSTNOTE-${hl.id}">${hl.note}</div> + <div class="content-inner" lang="${hl.lang ? hl.lang : 'en'}"> + <img src="${App.getInitParam('icon_indicator_white')}"> + </div> + <div class="intermediate"> + ${hl.enclosures} + </div> + <div class="footer" onclick="event.stopPropagation()"> + + <div class="left"> + ${hl.buttons_left} + <i class="material-icons">label_outline</i> + <span id="ATSTR-${hl.id}">${hl.tags_str}</span> + <a title="${__("Edit tags for this article")}" href="#" + onclick="Article.editTags(${hl.id})">(+)</a> + ${comments} + </div> + + <div class="right"> + ${originally_from} + ${hl.buttons} + </div> + </div> + </div> + </div>`; + + + } else { + row = `<div class="hl ${row_class} ${hl.score_class}" data-orig-feed-id="${hl.feed_id}" data-article-id="${hl.id}" id="RROW-${hl.id}" + onmouseover="Article.mouseIn(${hl.id})" onmouseout="Article.mouseOut(${hl.id})"> + <div class="left"> + <input dojoType="dijit.form.CheckBox" type="checkbox" onclick="Headlines.onRowChecked(this)" class='rchk'> + <i class="marked-pic marked-${hl.id} material-icons" onclick="Headlines.toggleMark(${hl.id})">star</i> + <i class="pub-pic pub-${hl.id} material-icons" onclick="Headlines.togglePub(${hl.id})">rss_feed</i> + </div> + <div onclick="return Headlines.click(event, ${hl.id})" class="title"> + <span data-article-id="${hl.id}" class="hl-content hlMenuAttach"> + <a class="title" href="${hl.link}">${hl.title} <span class="preview">${hl.content_preview}</span></a> + <!-- <span class="author">${hl.author}</span> --> + <span class="HLLCTR-${hl.id}">${hl.labels}</span> + </span> + </div> + <span class="feed"> + <a style="background : rgba(${hl.favicon_avg_color_rgba})" href="#" onclick="Feeds.open({feed:${hl.feed_id}})">${hl.feed_title}</a> + </span> + <div title="${hl.imported}"> + <span class="updated">${hl.updated}</span> + </div> + <div class="right"> + <i class="material-icons icon-score" title="${hl.score}" data-score="${hl.score}" + onclick="Article.setScore(${hl.id}, this)">${hl.score_pic}</i> + <span onclick="Feeds.open({feed:${hl.feed_id})" style="cursor : pointer" title="${hl.feed_title}">${hl.feed_icon}</span> + </div> + </div> + `; + } + + if (row != null) { + const tmp = document.createElement("div"); + tmp.innerHTML = row; + dojo.parser.parse(tmp); + + $("headlines-frame").appendChild(tmp.firstChild); + } + }, onLoaded: function (transport, offset) { const reply = App.handleRpcJson(transport); @@ -280,19 +414,31 @@ define(["dojo/_base/declare"], function (declare) { console.log('received', headlines_count, 'headlines, infscroll disabled=', Feeds.infscroll_disabled); - this.vgroup_last_feed = reply['headlines-info']['vgroup_last_feed']; + //this.vgroup_last_feed = reply['headlines-info']['vgroup_last_feed']; this.current_first_id = reply['headlines']['first_id']; if (offset == 0) { - this.loaded_article_ids = []; + //this.headlines = []; + this.vgroup_last_feed = undefined; dojo.html.set($("toolbar-headlines"), reply['headlines']['toolbar'], {parseContent: true}); - $("headlines-frame").innerHTML = ''; + if (typeof reply['headlines']['content'] == 'string') { + $("headlines-frame").innerHTML = reply['headlines']['content']; + } else { + $("headlines-frame").innerHTML = ''; + + for (let i = 0; i < reply['headlines']['content'].length; i++) { + const hl = reply['headlines']['content'][i]; + + this.renderHeadline(reply['headlines'], hl); + this.headlines[parseInt(hl.id)] = hl; + } + } - let tmp = document.createElement("div"); + /* let tmp = document.createElement("div"); tmp.innerHTML = reply['headlines']['content']; dojo.parser.parse(tmp); @@ -304,7 +450,7 @@ define(["dojo/_base/declare"], function (declare) { this.loaded_article_ids.push(row.id); } - } + } */ let hsp = $("headlines-spacer"); @@ -336,7 +482,7 @@ define(["dojo/_base/declare"], function (declare) { if (hsp) c.domNode.removeChild(hsp); - let tmp = document.createElement("div"); + /* let tmp = document.createElement("div"); tmp.innerHTML = reply['headlines']['content']; dojo.parser.parse(tmp); @@ -348,6 +494,17 @@ define(["dojo/_base/declare"], function (declare) { this.loaded_article_ids.push(row.id); } + } */ + + if (typeof reply['headlines']['content'] == 'string') { + $("headlines-frame").innerHTML = reply['headlines']['content']; + } else { + for (let i = 0; i < reply['headlines']['content'].length; i++) { + const hl = reply['headlines']['content'][i]; + + this.renderHeadline(reply['headlines'], hl); + this.headlines[parseInt(hl.id)] = hl; + } } if (!hsp) { @@ -855,10 +1012,6 @@ define(["dojo/_base/declare"], function (declare) { return; } - for (let i = 0; i < rows.length; i++) { - ArticleCache.del(rows[i]); - } - const query = {op: "rpc", method: op, ids: rows.toString()}; xhrPost("backend.php", query, (transport) => { diff --git a/js/common.js b/js/common.js index 5b2812be6..788c159fe 100755 --- a/js/common.js +++ b/js/common.js @@ -326,6 +326,21 @@ function popupOpenArticle(id) { "ttrss_article_popup", "height=900,width=900,resizable=yes,status=no,location=no,menubar=no,directories=no,scrollbars=yes,toolbar=no"); - w.opener = null; - w.location = "backend.php?op=article&method=view&mode=raw&html=1&zoom=1&id=" + id + "&csrf_token=" + App.getInitParam("csrf_token"); + if (w) { + w.opener = null; + w.location = "backend.php?op=article&method=view&mode=raw&html=1&zoom=1&id=" + id + "&csrf_token=" + App.getInitParam("csrf_token"); + } } + +// htmlspecialchars()-alike for headlines data-content attribute +function escapeHtml(text) { + const map = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + }; + + return text.replace(/[&<>"']/g, function(m) { return map[m]; }); +}
\ No newline at end of file diff --git a/js/tt-rss.js b/js/tt-rss.js index 8e5dac811..402160e02 100644 --- a/js/tt-rss.js +++ b/js/tt-rss.js @@ -7,7 +7,6 @@ let Filters; let Feeds; let Headlines; let Article; -let ArticleCache; let PluginHost; const Plugins = {}; @@ -54,7 +53,6 @@ require(["dojo/_base/kernel", "fox/Feeds", "fox/Headlines", "fox/Article", - "fox/ArticleCache", "fox/FeedStoreModel", "fox/FeedTree"], function (dojo, declare, ready, parser, AppBase) { @@ -138,8 +136,6 @@ require(["dojo/_base/kernel", App.setLoadingProgress(50); - ArticleCache.clear(); - this._widescreen_mode = App.getInitParam("widescreen"); this.switchPanelMode(this._widescreen_mode); @@ -162,7 +158,6 @@ require(["dojo/_base/kernel", document.title = tmp; }, onViewModeChanged: function() { - ArticleCache.clear(); return Feeds.reloadCurrent(''); }, isCombinedMode: function() { @@ -184,7 +179,7 @@ require(["dojo/_base/kernel", } }, switchPanelMode: function(wide) { - if (App.isCombinedMode()) return; + //if (App.isCombinedMode()) return; const article_id = Article.getActive(); |