aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Alexandre Alapetite <alexandre@alapetite.fr> 2018-12-25 01:30:28 +0100
committerGravatar Alexandre Alapetite <alexandre@alapetite.fr> 2018-12-25 01:30:28 +0100
commit4888f919f104b2d170302565e481a0b731eb4145 (patch)
treeff2a452f726686a259b565fc2fa036fab695d040
parent06ea2626e804dd24c2d84fa26c8febf29ef3d357 (diff)
Prepare for batch mark as read
-rwxr-xr-xapp/Controllers/entryController.php13
-rw-r--r--app/Models/EntryDAO.php2
-rw-r--r--app/Models/TagDAO.php48
-rwxr-xr-xapp/views/entry/read.phtml10
-rw-r--r--app/views/helpers/javascript_vars.phtml3
-rw-r--r--p/scripts/main.js62
6 files changed, 76 insertions, 62 deletions
diff --git a/app/Controllers/entryController.php b/app/Controllers/entryController.php
index fc0af0639..9c5ee2616 100755
--- a/app/Controllers/entryController.php
+++ b/app/Controllers/entryController.php
@@ -97,14 +97,15 @@ class FreshRSS_entry_Controller extends Minz_ActionController {
}
}
} else {
- $entryDAO->markRead($id, $is_read);
-
+ $ids = is_array($id) ? $id : array($id);
+ $entryDAO->markRead($ids, $is_read);
$tagDAO = FreshRSS_Factory::createTagDao();
- foreach ($tagDAO->getTagsForEntry($id) as $tag) {
- if (!empty($tag['checked'])) {
- $this->view->tags[] = $tag['id'];
- }
+ $tagsForEntries = $tagDAO->getTagsForEntries($ids);
+ $tags = array();
+ foreach ($tagsForEntries as $line) {
+ $tags['t_' . $line['id_tag']][] = $line['id_entry'];
}
+ $this->view->tags = $tags;
}
if (!$this->ajax) {
diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php
index 6d77a33cd..9ae1ed797 100644
--- a/app/Models/EntryDAO.php
+++ b/app/Models/EntryDAO.php
@@ -383,7 +383,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
*/
public function markRead($ids, $is_read = true) {
FreshRSS_UserDAO::touch();
- if (is_array($ids)) { //Many IDs at once (used by API)
+ if (is_array($ids)) { //Many IDs at once
if (count($ids) < 6) { //Speed heuristics
$affected = 0;
foreach ($ids as $id) {
diff --git a/app/Models/TagDAO.php b/app/Models/TagDAO.php
index b55d2b35d..0b4428f17 100644
--- a/app/Models/TagDAO.php
+++ b/app/Models/TagDAO.php
@@ -256,42 +256,56 @@ class FreshRSS_TagDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
}
}
- //For API
- public function getEntryIdsTagNames($entries) {
- $sql = 'SELECT et.id_entry, t.name '
+ public function getTagsForEntries($entries) {
+ $sql = 'SELECT et.id_entry, et.id_tag, t.name '
. 'FROM `' . $this->prefix . 'tag` t '
. 'INNER JOIN `' . $this->prefix . 'entrytag` et ON et.id_tag = t.id';
$values = array();
if (is_array($entries) && count($entries) > 0) {
$sql .= ' AND et.id_entry IN (' . str_repeat('?,', count($entries) - 1). '?)';
- foreach ($entries as $entry) {
- $values[] = is_array($entry) ? $entry['id'] : $entry->id();
+ if (is_array($entries[0])) {
+ foreach ($entries as $entry) {
+ $values[] = $entry['id'];
+ }
+ } elseif (is_object($entries[0])) {
+ foreach ($entries as $entry) {
+ $values[] = $entry->id();
+ }
+ } else {
+ foreach ($entries as $entry) {
+ $values[] = $entry;
+ }
}
}
$stm = $this->bd->prepare($sql);
if ($stm && $stm->execute($values)) {
- $result = array();
- foreach ($stm->fetchAll(PDO::FETCH_ASSOC) as $line) {
- $entryId = 'e_' . $line['id_entry'];
- $tagName = $line['name'];
- if (empty($result[$entryId])) {
- $result[$entryId] = array();
- }
- $result[$entryId][] = $tagName;
- }
- return $result;
+ return $stm->fetchAll(PDO::FETCH_ASSOC);
} else {
$info = $stm == null ? array(0 => '', 1 => '', 2 => 'syntax error') : $stm->errorInfo();
if ($this->autoUpdateDb($info)) {
- return $this->getTagNamesEntryIds($id_entry);
+ return $this->getTagsForEntries($entries);
}
- Minz_Log::error('SQL error getTagNamesEntryIds: ' . $info[2]);
+ Minz_Log::error('SQL error getTagsForEntries: ' . $info[2]);
return false;
}
}
+ //For API
+ public function getEntryIdsTagNames($entries) {
+ $result = array();
+ foreach ($this->getTagsForEntries($entries) as $line) {
+ $entryId = 'e_' . $line['id_entry'];
+ $tagName = $line['name'];
+ if (empty($result[$entryId])) {
+ $result[$entryId] = array();
+ }
+ $result[$entryId][] = $tagName;
+ }
+ return $result;
+ }
+
public static function daoToTag($listDAO) {
$list = array();
if (!is_array($listDAO)) {
diff --git a/app/views/entry/read.phtml b/app/views/entry/read.phtml
index fb9e129f2..44193da9c 100755
--- a/app/views/entry/read.phtml
+++ b/app/views/entry/read.phtml
@@ -1,17 +1,7 @@
<?php
header('Content-Type: application/json; charset=UTF-8');
-$url = array(
- 'c' => Minz_Request::controllerName(),
- 'a' => Minz_Request::actionName(),
- 'params' => Minz_Request::fetchGET(),
-);
-
-$url['params']['is_read'] = Minz_Request::param('is_read', true) ? '0' : '1';
-
FreshRSS::loadStylesAndScripts();
echo json_encode(array(
- 'url' => str_ireplace('&amp;', '&', Minz_Url::display($url)),
- 'icon' => _i($url['params']['is_read'] === '1' ? 'unread' : 'read'),
'tags' => $this->tags,
));
diff --git a/app/views/helpers/javascript_vars.phtml b/app/views/helpers/javascript_vars.phtml
index a434a04a3..b950b6a1f 100644
--- a/app/views/helpers/javascript_vars.phtml
+++ b/app/views/helpers/javascript_vars.phtml
@@ -56,6 +56,7 @@ echo htmlspecialchars(json_encode(array(
'category_empty' => _t('gen.js.category_empty'),
),
'icons' => array(
- 'close' => _i('close'),
+ 'read' => rawurlencode(_i('read')),
+ 'unread' => rawurlencode(_i('unread')),
),
), JSON_UNESCAPED_UNICODE), ENT_NOQUOTES, 'UTF-8');
diff --git a/p/scripts/main.js b/p/scripts/main.js
index 4ba329dc1..65310fe9f 100644
--- a/p/scripts/main.js
+++ b/p/scripts/main.js
@@ -7,6 +7,8 @@ var $stream = null,
shares = 0,
ajax_loading = false;
+if (!NodeList.prototype.forEach) { NodeList.prototype.forEach = Array.prototype.forEach; } //IE11
+
function redirect(url, new_tab) {
if (url) {
if (new_tab) {
@@ -89,7 +91,7 @@ function incUnreadsFeed(article, feed_id, nb) {
}
//Update unread: favourites
- if (article && article.closest('div').hasClass('favorite')) {
+ if (article && $(article).closest('div').hasClass('favorite')) {
elem = $('#aside_feed .favorites .title').get(0);
if (elem) {
feed_unreads = elem ? str2int(elem.getAttribute('data-unread')) : 0;
@@ -115,7 +117,7 @@ function incUnreadsFeed(article, feed_id, nb) {
}
function incUnreadsTag(tag_id, nb) {
- var $t = $('#t_' + tag_id);
+ var $t = $('#' + tag_id);
var unreads = str2int($t.attr('data-unread'));
$t.attr('data-unread', unreads + nb)
.children('.item-title').attr('data-unread', numberFormat(unreads + nb));
@@ -126,20 +128,20 @@ function incUnreadsTag(tag_id, nb) {
}
var pending_entries = {};
-function mark_read(active, only_not_read) {
- if ((active.length === 0) || (!active.attr('id')) ||
- context.anonymous ||
- (only_not_read && !active.hasClass("not_read"))) {
+function mark_read($active, only_not_read) {
+ let div = $active ? $active[0] : null;
+ if (!div || !div.id || context.anonymous ||
+ (only_not_read && !div.classList.contains('not_read'))) {
return false;
}
- if (pending_entries[active.attr('id')]) {
+ if (pending_entries[div.id]) {
return false;
}
- pending_entries[active.attr('id')] = true;
+ pending_entries[div.id] = true;
- var url = '.?c=entry&a=read&id=' + active.attr('id').replace(/^flux_/, '') +
- (active.hasClass('not_read') ? '' : '&is_read=0');
+ let url = '.?c=entry&a=read&id=' + div.id.replace(/^flux_/, '') +
+ (div.classList.contains('not_read') ? '' : '&is_read=0');
$.ajax({
type: 'POST',
@@ -149,35 +151,39 @@ function mark_read(active, only_not_read) {
_csrf: context.csrf,
},
}).done(function (data) {
- var $r = active.find("a.read").attr("href", data.url),
- inc = 0;
- if (active.hasClass("not_read")) {
- active.removeClass("not_read");
+ let inc = 0;
+ if (div.classList.contains('not_read')) {
+ div.classList.remove('not_read');
+ div.querySelectorAll('a.read').forEach(function (a) { a.setAttribute('href', a.getAttribute('href').replace('&is_read=0', '') + '&is_read=1'); });
+ div.querySelectorAll('a.read > .icon').forEach(function (img) { img.outerHTML = icons.read; });
inc--;
} else {
- active.addClass("not_read");
- active.addClass("keep_unread");
+ div.classList.add('not_read', 'keep_unread');
+ div.querySelectorAll('a.read').forEach(function (a) { a.setAttribute('href', a.getAttribute('href').replace('&is_read=1', '')); });
+ div.querySelectorAll('a.read > .icon').forEach(function (img) { img.outerHTML = icons.unread; });
inc++;
}
- $r.find('.icon').replaceWith(data.icon);
- var feed_url = active.find(".website>a").attr("href");
- if (feed_url) {
- var feed_id = feed_url.substr(feed_url.lastIndexOf('f_'));
- incUnreadsFeed(active, feed_id, inc);
+ let feed_link = div.querySelector('.website > a');
+ if (feed_link) {
+ let feed_url = feed_link.getAttribute('href');
+ let feed_id = feed_url.substr(feed_url.lastIndexOf('f_'));
+ incUnreadsFeed(div, feed_id, inc);
}
faviconNbUnread();
if (data.tags) {
- for (var i = data.tags.length - 1; i >= 0; i--) {
- incUnreadsTag(data.tags[i], inc);
+ let tagIds = Object.keys(data.tags);
+ for (let i = tagIds.length - 1; i >= 0; i--) {
+ let tagId = tagIds[i];
+ incUnreadsTag(tagId, inc * data.tags[tagId].length);
}
}
- delete pending_entries[active.attr('id')];
+ delete pending_entries[div.id];
}).fail(function (data) {
openNotification(i18n.notif_request_failed, 'bad');
- delete pending_entries[active.attr('id')];
+ delete pending_entries[div.id];
});
}
@@ -911,7 +917,7 @@ function init_dynamic_tags() {
})
.done(function () {
if ($entry.hasClass('not_read')) {
- incUnreadsTag(tagId, isChecked ? 1 : -1);
+ incUnreadsTag('t_' + tagId, isChecked ? 1 : -1);
}
})
.fail(function () {
@@ -1079,7 +1085,7 @@ function notifs_html5_show(nb) {
var notification = new window.Notification(i18n.notif_title_articles, {
icon: "../themes/icons/favicon-256.png",
body: i18n.notif_body_articles.replace('%d', nb),
- tag: "freshRssNewArticles"
+ tag: 'freshRssNewArticles',
});
notification.onclick = function() {
@@ -1491,6 +1497,8 @@ function parseJsonVars() {
window.url = json.url;
window.i18n = json.i18n;
window.icons = json.icons;
+ icons.read = decodeURIComponent(icons.read);
+ icons.unread = decodeURIComponent(icons.unread);
}
function init_normal() {