aboutsummaryrefslogtreecommitdiff
path: root/p/scripts
diff options
context:
space:
mode:
authorGravatar Alexandre Alapetite <alexandre@alapetite.fr> 2022-07-04 09:53:26 +0200
committerGravatar GitHub <noreply@github.com> 2022-07-04 09:53:26 +0200
commit509c8cae6381ec46af7c8303eb92fda6ce496a4a (patch)
tree653f7f44df842f9d7135decd89467879a0098c50 /p/scripts
parent57d571230eeb2d3ede57e640b640f17c7a2298a2 (diff)
Dynamic OPML (#4407)
* Dynamic OPML draft #fix https://github.com/FreshRSS/FreshRSS/issues/4191 * Export dynamic OPML http://opml.org/spec2.opml#1629043127000 * Restart with simpler approach * Minor revert * Export dynamic OPML also for single feeds * Special category type for importing dynamic OPML * Parameter for excludeMutedFeeds * Details * More draft * i18n * Fix update * Draft manual import working * Working manual refresh * Draft automatic update * Working Web refresh + fixes * Import/export dynamic OPML settings * Annoying numerous lines in SQL logs * Fix minor JavaScript error * Fix auto adding new columns * Add require * Add missing 🗲 * Missing space * Disable adding new feeds to dynamic categories * Link from import * i18n typo * Improve theme icon function * Fix pink-dark
Diffstat (limited to 'p/scripts')
-rw-r--r--p/scripts/extra.js7
-rw-r--r--p/scripts/feed.js21
-rw-r--r--p/scripts/main.js131
3 files changed, 122 insertions, 37 deletions
diff --git a/p/scripts/extra.js b/p/scripts/extra.js
index 39f9d049a..7be235aa4 100644
--- a/p/scripts/extra.js
+++ b/p/scripts/extra.js
@@ -202,8 +202,8 @@ function updateHref(ev) {
}
// set event listener on "show url" buttons
-function init_url_observers() {
- document.querySelectorAll('.open-url').forEach(function (btn) {
+function init_url_observers(parent) {
+ parent.querySelectorAll('.open-url').forEach(function (btn) {
btn.addEventListener('mouseover', updateHref);
btn.addEventListener('click', updateHref);
});
@@ -276,7 +276,6 @@ function init_extra_afterDOM() {
if (!['normal', 'global', 'reader'].includes(context.current_view)) {
init_crypto_form();
init_password_observers(document.body);
- init_url_observers();
init_select_observers();
init_configuration_alert();
@@ -284,8 +283,10 @@ function init_extra_afterDOM() {
if (slider) {
init_slider(slider);
init_archiving(slider);
+ init_url_observers(slider);
} else {
init_archiving(document.body);
+ init_url_observers(document.body);
}
}
diff --git a/p/scripts/feed.js b/p/scripts/feed.js
index 2a213b422..a5e43c614 100644
--- a/p/scripts/feed.js
+++ b/p/scripts/feed.js
@@ -1,6 +1,6 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0
'use strict';
-/* globals init_archiving, init_configuration_alert, init_password_observers, init_slider */
+/* globals init_archiving, init_configuration_alert, init_password_observers, init_slider, init_url_observers */
// <popup>
let popup = null;
@@ -67,6 +67,22 @@ function init_popup_preview_selector() {
/**
* Allow a <select class="select-show"> to hide/show elements defined by <option data-show="elem-id"></option>
*/
+function init_disable_elements_on_update(parent) {
+ const inputs = parent.querySelectorAll('input[data-disable-update]');
+ for (const input of inputs) {
+ input.addEventListener('input', (e) => {
+ const elem = document.getElementById(e.target.dataset.disableUpdate);
+ if (elem) {
+ elem.disabled = true;
+ elem.remove();
+ }
+ });
+ }
+}
+
+/**
+ * Allow a <select class="select-show"> to hide/show elements defined by <option data-show="elem-id"></option>
+ */
function init_select_show(parent) {
const listener = (select) => {
const options = select.querySelectorAll('option[data-show]');
@@ -120,7 +136,9 @@ function init_feed_afterDOM() {
init_popup();
init_popup_preview_selector();
init_select_show(slider);
+ init_disable_elements_on_update(slider);
init_password_observers(slider);
+ init_url_observers(slider);
init_valid_xpath(slider);
});
init_slider(slider);
@@ -130,6 +148,7 @@ function init_feed_afterDOM() {
init_popup();
init_popup_preview_selector();
init_select_show(document.body);
+ init_disable_elements_on_update(document.body);
init_password_observers(document.body);
init_valid_xpath(document.body);
}
diff --git a/p/scripts/main.js b/p/scripts/main.js
index 1eb8a1ff8..461dc1b10 100644
--- a/p/scripts/main.js
+++ b/p/scripts/main.js
@@ -115,9 +115,10 @@ function incUnreadsFeed(article, feed_id, nb) {
}
// Update unread: category
- elem = document.getElementById(feed_id).closest('.category');
- feed_unreads = elem ? str2int(elem.getAttribute('data-unread')) : 0;
+ elem = document.getElementById(feed_id);
+ elem = elem ? elem.closest('.category') : null;
if (elem) {
+ feed_unreads = str2int(elem.getAttribute('data-unread'));
elem.setAttribute('data-unread', feed_unreads + nb);
elem = elem.querySelector('.title');
if (elem) {
@@ -147,7 +148,7 @@ function incUnreadsFeed(article, feed_id, nb) {
// Update unread: title
document.title = document.title.replace(/^((?:\([\s0-9]+\) )?)/, function (m, p1) {
const feed = document.getElementById(feed_id);
- if (article || feed.closest('.active')) {
+ if (article || (feed && feed.closest('.active'))) {
isCurrentView = true;
return incLabel(p1, nb, true);
} else if (document.querySelector('.all.active')) {
@@ -1287,9 +1288,11 @@ function loadDynamicTags(div) {
}
// <actualize>
-let feed_processed = 0;
+let feeds_processed = 0;
+let categories_processed = 0;
+let to_process = 0;
-function updateFeed(feeds, feeds_count) {
+function refreshFeed(feeds, feeds_count) {
const feed = feeds.pop();
if (!feed) {
return;
@@ -1297,14 +1300,15 @@ function updateFeed(feeds, feeds_count) {
const req = new XMLHttpRequest();
req.open('POST', feed.url, true);
req.onloadend = function (e) {
+ feeds_processed++;
if (this.status != 200) {
- return badAjax(false);
+ badAjax(false);
+ } else {
+ const div = document.getElementById('actualizeProgress');
+ div.querySelector('.progress').innerHTML = (categories_processed + feeds_processed) + ' / ' + to_process;
+ div.querySelector('.title').innerHTML = feed.title;
}
- feed_processed++;
- const div = document.getElementById('actualizeProgress');
- div.querySelector('.progress').innerHTML = feed_processed + ' / ' + feeds_count;
- div.querySelector('.title').innerHTML = feed.title;
- if (feed_processed === feeds_count) {
+ if (feeds_processed === feeds_count) {
// Empty request to commit new articles
const req2 = new XMLHttpRequest();
req2.open('POST', './?c=feed&a=actualize&id=-1&ajax=1', true);
@@ -1317,7 +1321,7 @@ function updateFeed(feeds, feeds_count) {
noCommit: 0,
}));
} else {
- updateFeed(feeds, feeds_count);
+ refreshFeed(feeds, feeds_count);
}
};
req.setRequestHeader('Content-Type', 'application/json');
@@ -1327,8 +1331,73 @@ function updateFeed(feeds, feeds_count) {
}));
}
+function refreshFeeds(json) {
+ feeds_processed = 0;
+ if (!json.feeds || json.feeds.length === 0) {
+ // Empty request to commit new articles
+ const req2 = new XMLHttpRequest();
+ req2.open('POST', './?c=feed&a=actualize&id=-1&ajax=1', true);
+ req2.onloadend = function (e) {
+ context.ajax_loading = false;
+ };
+ req2.setRequestHeader('Content-Type', 'application/json');
+ req2.send(JSON.stringify({
+ _csrf: context.csrf,
+ noCommit: 0,
+ }));
+ } else {
+ const feeds_count = json.feeds.length;
+ for (let i = 10; i > 0; i--) {
+ refreshFeed(json.feeds, feeds_count);
+ }
+ }
+}
+
+function refreshDynamicOpml(categories, categories_count, next) {
+ const category = categories.pop();
+ if (!category) {
+ return;
+ }
+ const req = new XMLHttpRequest();
+ req.open('POST', category.url, true);
+ req.onloadend = function (e) {
+ categories_processed++;
+ if (this.status != 200) {
+ badAjax(false);
+ } else {
+ const div = document.getElementById('actualizeProgress');
+ div.querySelector('.progress').innerHTML = (categories_processed + feeds_processed) + ' / ' + to_process;
+ div.querySelector('.title').innerHTML = category.title;
+ }
+ if (categories_processed === categories_count) {
+ if (next) { next(); }
+ } else {
+ refreshDynamicOpml(categories, categories_count, next);
+ }
+ };
+ req.setRequestHeader('Content-Type', 'application/json');
+ req.send(JSON.stringify({
+ _csrf: context.csrf,
+ noCommit: 1,
+ }));
+}
+
+function refreshDynamicOpmls(json, next) {
+ categories_processed = 0;
+ if (json.categories && json.categories.length > 0) {
+ const categories_count = json.categories.length;
+ for (let i = 10; i > 0; i--) {
+ refreshDynamicOpml(json.categories, categories_count, next);
+ }
+ } else {
+ if (next) { next(); }
+ }
+}
+
function init_actualize() {
let auto = false;
+ let nbCategoriesFirstRound = 0;
+ let skipCategories = false;
const actualize = document.getElementById('actualize');
if (!actualize) {
@@ -1352,33 +1421,29 @@ function init_actualize() {
if (!json) {
return badAjax(false);
}
- if (auto && json.feeds.length < 1) {
+ if (auto && json.categories.length < 1 && json.feeds.length < 1) {
auto = false;
context.ajax_loading = false;
return false;
}
- if (json.feeds.length === 0) {
+ to_process = json.categories.length + json.feeds.length + nbCategoriesFirstRound;
+ if (json.categories.length + json.feeds.length > 0 && !document.getElementById('actualizeProgress')) {
+ document.body.insertAdjacentHTML('beforeend', '<div id="actualizeProgress" class="notification good">' +
+ json.feedback_actualize + '<br /><span class="title">/</span><br /><span class="progress">0 / ' +
+ to_process + '</span></div>');
+ } else {
openNotification(json.feedback_no_refresh, 'good');
- // Empty request to commit new articles
- const req2 = new XMLHttpRequest();
- req2.open('POST', './?c=feed&a=actualize&id=-1&ajax=1', true);
- req2.onloadend = function (e) {
- context.ajax_loading = false;
- };
- req2.setRequestHeader('Content-Type', 'application/json');
- req2.send(JSON.stringify({
- _csrf: context.csrf,
- noCommit: 0,
- }));
- return;
}
- // Progress bar
- const feeds_count = json.feeds.length;
- document.body.insertAdjacentHTML('beforeend', '<div id="actualizeProgress" class="notification good">' +
- json.feedback_actualize + '<br /><span class="title">/</span><br /><span class="progress">0 / ' +
- feeds_count + '</span></div>');
- for (let i = 10; i > 0; i--) {
- updateFeed(json.feeds, feeds_count);
+ if (json.categories.length > 0 && !skipCategories) {
+ skipCategories = true; // To avoid risk of infinite loop
+ nbCategoriesFirstRound = json.categories.length;
+ // If some dynamic OPML categories are refreshed, need to reload the list of feeds before updating them
+ refreshDynamicOpmls(json, () => {
+ context.ajax_loading = false;
+ actualize.click();
+ });
+ } else {
+ refreshFeeds(json);
}
};
req.setRequestHeader('Content-Type', 'application/json');