This commit is contained in:
Martin Rotter 2022-09-15 14:28:48 +02:00
parent 810dd30412
commit 94983a3b41
14 changed files with 150 additions and 134 deletions

View File

@ -2,8 +2,10 @@
<qresource prefix="/"> <qresource prefix="/">
<file>sql/db_init_mysql.sql</file> <file>sql/db_init_mysql.sql</file>
<file>sql/db_update_mysql_1_2.sql</file> <file>sql/db_update_mysql_1_2.sql</file>
<file>sql/db_update_mysql_2_3.sql</file>
<file>sql/db_init_sqlite.sql</file> <file>sql/db_init_sqlite.sql</file>
<file>sql/db_update_sqlite_1_2.sql</file> <file>sql/db_update_sqlite_1_2.sql</file>
<file>sql/db_update_sqlite_2_3.sql</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@ -40,7 +40,7 @@ CREATE TABLE Feeds (
category INTEGER NOT NULL CHECK (category >= -1), /* Physical category ID, also root feeds contain -1 here. */ category INTEGER NOT NULL CHECK (category >= -1), /* Physical category ID, also root feeds contain -1 here. */
source TEXT, source TEXT,
update_type INTEGER NOT NULL CHECK (update_type >= 0), update_type INTEGER NOT NULL CHECK (update_type >= 0),
update_interval INTEGER NOT NULL DEFAULT 15 CHECK (update_interval >= 1), update_interval INTEGER NOT NULL DEFAULT 900 CHECK (update_interval >= 1),
is_off INTEGER NOT NULL DEFAULT 0 CHECK (is_off >= 0 AND is_off <= 1), is_off INTEGER NOT NULL DEFAULT 0 CHECK (is_off >= 0 AND is_off <= 1),
open_articles INTEGER NOT NULL DEFAULT 0 CHECK (open_articles >= 0 AND open_articles <= 1), open_articles INTEGER NOT NULL DEFAULT 0 CHECK (open_articles >= 0 AND open_articles <= 1),
account_id INTEGER NOT NULL, account_id INTEGER NOT NULL,

View File

@ -69,6 +69,8 @@ void FeedDownloader::updateFeeds(const QList<Feed*>& feeds) {
m_feedsOriginalCount = m_feeds.size(); m_feedsOriginalCount = m_feeds.size();
m_feedsUpdated = 0; m_feedsUpdated = 0;
const QDateTime update_time = QDateTime::currentDateTimeUtc();
if (feeds.isEmpty()) { if (feeds.isEmpty()) {
qDebugNN << LOGSEC_FEEDDOWNLOADER << "No feeds to update in worker thread, aborting update."; qDebugNN << LOGSEC_FEEDDOWNLOADER << "No feeds to update in worker thread, aborting update.";
} }
@ -157,6 +159,8 @@ void FeedDownloader::updateFeeds(const QList<Feed*>& feeds) {
else { else {
updateOneFeed(n_r, n_f, stated_messages.value(n_r).value(n_f->customId()), tagged_messages.value(n_r)); updateOneFeed(n_r, n_f, stated_messages.value(n_r).value(n_f->customId()), tagged_messages.value(n_r));
} }
n_f->setLastUpdated(QDateTime::currentDateTimeUtc());
} }
} }

View File

@ -260,18 +260,8 @@ QList<Feed*> FeedsModel::feedsForScheduledUpdate(bool auto_update_now) {
case Feed::AutoUpdateType::SpecificAutoUpdate: case Feed::AutoUpdateType::SpecificAutoUpdate:
default: default:
int remaining_interval = feed->autoUpdateRemainingInterval(); if (feed->lastUpdated().addSecs(feed->autoUpdateInterval()) < QDateTime::currentDateTimeUtc()) {
if (--remaining_interval <= 0) {
// Interval of this feed passed, include this feed in the output list
// and reset the interval.
feeds_for_update.append(feed); feeds_for_update.append(feed);
feed->setAutoUpdateRemainingInterval(feed->autoUpdateInitialInterval());
}
else {
// Interval did not pass, set new decremented interval and do NOT
// include this feed in the output list.
feed->setAutoUpdateRemainingInterval(remaining_interval);
} }
break; break;

View File

@ -2068,7 +2068,7 @@ void DatabaseQueries::createOverwriteFeed(const QSqlDatabase& db, Feed* feed, in
q.bindValue(QSL(":category"), new_parent_id); q.bindValue(QSL(":category"), new_parent_id);
q.bindValue(QSL(":source"), feed->source()); q.bindValue(QSL(":source"), feed->source());
q.bindValue(QSL(":update_type"), int(feed->autoUpdateType())); q.bindValue(QSL(":update_type"), int(feed->autoUpdateType()));
q.bindValue(QSL(":update_interval"), feed->autoUpdateInitialInterval()); q.bindValue(QSL(":update_interval"), feed->autoUpdateInterval());
q.bindValue(QSL(":account_id"), account_id); q.bindValue(QSL(":account_id"), account_id);
q.bindValue(QSL(":custom_id"), feed->customId()); q.bindValue(QSL(":custom_id"), feed->customId());
q.bindValue(QSL(":id"), feed->id()); q.bindValue(QSL(":id"), feed->id());

View File

@ -304,7 +304,7 @@ Assignment DatabaseQueries::getFeeds(const QSqlDatabase& db,
feed->setCreationDate(TextFactory::parseDateTime(query.value(FDS_DB_DCREATED_INDEX).value<qint64>())); feed->setCreationDate(TextFactory::parseDateTime(query.value(FDS_DB_DCREATED_INDEX).value<qint64>()));
feed->setIcon(qApp->icons()->fromByteArray(query.value(FDS_DB_ICON_INDEX).toByteArray())); feed->setIcon(qApp->icons()->fromByteArray(query.value(FDS_DB_ICON_INDEX).toByteArray()));
feed->setAutoUpdateType(static_cast<Feed::AutoUpdateType>(query.value(FDS_DB_UPDATE_TYPE_INDEX).toInt())); feed->setAutoUpdateType(static_cast<Feed::AutoUpdateType>(query.value(FDS_DB_UPDATE_TYPE_INDEX).toInt()));
feed->setAutoUpdateInitialInterval(query.value(FDS_DB_UPDATE_INTERVAL_INDEX).toInt()); feed->setAutoUpdateInterval(query.value(FDS_DB_UPDATE_INTERVAL_INDEX).toInt());
feed->setIsSwitchedOff(query.value(FDS_DB_IS_OFF_INDEX).toBool()); feed->setIsSwitchedOff(query.value(FDS_DB_IS_OFF_INDEX).toBool());
feed->setOpenArticlesDirectly(query.value(FDS_DB_OPEN_ARTICLES_INDEX).toBool()); feed->setOpenArticlesDirectly(query.value(FDS_DB_OPEN_ARTICLES_INDEX).toBool());

View File

@ -73,9 +73,9 @@
#define FEEDS_VIEW_COLUMN_COUNT 2 #define FEEDS_VIEW_COLUMN_COUNT 2
#define DEFAULT_DAYS_TO_DELETE_MSG 14 #define DEFAULT_DAYS_TO_DELETE_MSG 14
#define ELLIPSIS_LENGTH 3 #define ELLIPSIS_LENGTH 3
#define DEFAULT_AUTO_UPDATE_INTERVAL 15 #define DEFAULT_AUTO_UPDATE_INTERVAL 900 // In seconds.
#define AUTO_UPDATE_INTERVAL 60000 #define AUTO_UPDATE_INTERVAL 10 // In seconds.
#define STARTUP_UPDATE_DELAY 15.0 // In seconds. #define STARTUP_UPDATE_DELAY 15.0 // In seconds.
#define TIMEZONE_OFFSET_LIMIT 6 #define TIMEZONE_OFFSET_LIMIT 6
#define CHANGE_EVENT_DELAY 250 #define CHANGE_EVENT_DELAY 250
#define FLAG_ICON_SUBFOLDER "flags" #define FLAG_ICON_SUBFOLDER "flags"
@ -370,6 +370,7 @@
#endif #endif
#define NONQUOTE_W_SPACE_DOT(x) " " << (x) << "." #define NONQUOTE_W_SPACE_DOT(x) " " << (x) << "."
#define NONQUOTE_W_SPACE(x) " " << (x) << " "
#define QUOTE_W_SPACE_DOT(x) " '" << (x) << "'." #define QUOTE_W_SPACE_DOT(x) " '" << (x) << "'."
#define QUOTE_W_SPACE_COMMA(x) " '" << (x) << "'," #define QUOTE_W_SPACE_COMMA(x) " '" << (x) << "',"
#define QUOTE_W_SPACE(x) " '" << (x) << "' " #define QUOTE_W_SPACE(x) " '" << (x) << "' "

View File

@ -20,6 +20,7 @@ SettingsFeedsMessages::SettingsFeedsMessages(Settings* settings, QWidget* parent
: SettingsPanel(settings, parent), m_ui(new Ui::SettingsFeedsMessages) { : SettingsPanel(settings, parent), m_ui(new Ui::SettingsFeedsMessages) {
m_ui->setupUi(this); m_ui->setupUi(this);
m_ui->m_spinAutoUpdateInterval->setMode(TimeSpinBox::Mode::MinutesSeconds);
m_ui->m_spinStartupUpdateDelay->setMode(TimeSpinBox::Mode::MinutesSeconds); m_ui->m_spinStartupUpdateDelay->setMode(TimeSpinBox::Mode::MinutesSeconds);
initializeMessageDateFormats(); initializeMessageDateFormats();

View File

@ -27,25 +27,26 @@
#include <QTimer> #include <QTimer>
FeedReader::FeedReader(QObject* parent) FeedReader::FeedReader(QObject* parent)
: QObject(parent), : QObject(parent), m_autoUpdateTimer(new QTimer(this)), m_feedDownloader(nullptr) {
m_autoUpdateTimer(new QTimer(this)), m_feedDownloader(nullptr) {
m_feedsModel = new FeedsModel(this); m_feedsModel = new FeedsModel(this);
m_feedsProxyModel = new FeedsProxyModel(m_feedsModel, this); m_feedsProxyModel = new FeedsProxyModel(m_feedsModel, this);
m_messagesModel = new MessagesModel(this); m_messagesModel = new MessagesModel(this);
m_messagesProxyModel = new MessagesProxyModel(m_messagesModel, this); m_messagesProxyModel = new MessagesProxyModel(m_messagesModel, this);
connect(m_autoUpdateTimer, &QTimer::timeout, this, &FeedReader::executeNextAutoUpdate);
updateAutoUpdateStatus(); updateAutoUpdateStatus();
initializeFeedDownloader(); initializeFeedDownloader();
if (qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::FeedsUpdateOnStartup)).toBool()) { if (qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::FeedsUpdateOnStartup)).toBool()) {
qDebugNN << LOGSEC_CORE qDebugNN << LOGSEC_CORE << "Requesting update for all feeds on application startup.";
<< "Requesting update for all feeds on application startup.";
QTimer::singleShot(qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::FeedsUpdateStartupDelay)).toDouble() * 1000, QTimer::singleShot(qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::FeedsUpdateStartupDelay)).toDouble() * 1000,
this, this,
[this]() { [this]() {
updateFeeds(m_feedsModel->rootItem()->getSubAutoFetchingEnabledFeeds()); updateFeeds(m_feedsModel->rootItem()->getSubAutoFetchingEnabledFeeds());
}); connect(m_autoUpdateTimer, &QTimer::timeout, this, &FeedReader::executeNextAutoUpdate);
});
}
else {
connect(m_autoUpdateTimer, &QTimer::timeout, this, &FeedReader::executeNextAutoUpdate);
} }
} }
@ -85,20 +86,22 @@ void FeedReader::updateFeeds(const QList<Feed*>& feeds) {
} }
if (!qApp->feedUpdateLock()->tryLock()) { if (!qApp->feedUpdateLock()->tryLock()) {
qApp->showGuiMessage(Notification::Event::GeneralEvent, { qApp->showGuiMessage(Notification::Event::GeneralEvent,
tr("Cannot fetch articles at this point"), {tr("Cannot fetch articles at this point"),
tr("You cannot fetch new articles now because another critical operation is ongoing."), tr("You cannot fetch new articles now because another critical operation is ongoing."),
QSystemTrayIcon::MessageIcon::Warning }); QSystemTrayIcon::MessageIcon::Warning});
return; return;
} }
QMetaObject::invokeMethod(m_feedDownloader, "updateFeeds", QMetaObject::invokeMethod(m_feedDownloader,
"updateFeeds",
Qt::ConnectionType::QueuedConnection, Qt::ConnectionType::QueuedConnection,
Q_ARG(QList<Feed*>, my_feeds)); Q_ARG(QList<Feed*>, my_feeds));
} }
void FeedReader::synchronizeMessageData(const QList<CacheForServiceRoot*>& caches) { void FeedReader::synchronizeMessageData(const QList<CacheForServiceRoot*>& caches) {
QMetaObject::invokeMethod(m_feedDownloader, "synchronizeAccountCaches", QMetaObject::invokeMethod(m_feedDownloader,
"synchronizeAccountCaches",
Qt::ConnectionType::QueuedConnection, Qt::ConnectionType::QueuedConnection,
Q_ARG(QList<CacheForServiceRoot*>, caches), Q_ARG(QList<CacheForServiceRoot*>, caches),
Q_ARG(bool, true)); Q_ARG(bool, true));
@ -128,6 +131,10 @@ void FeedReader::initializeFeedDownloader() {
} }
} }
QDateTime FeedReader::lastAutoUpdate() const {
return m_lastAutoUpdate;
}
void FeedReader::showMessageFiltersManager() { void FeedReader::showMessageFiltersManager() {
FormMessageFiltersManager manager(qApp->feedReader(), FormMessageFiltersManager manager(qApp->feedReader(),
qApp->feedReader()->feedsModel()->serviceRoots(), qApp->feedReader()->feedsModel()->serviceRoots(),
@ -141,21 +148,24 @@ void FeedReader::showMessageFiltersManager() {
void FeedReader::updateAutoUpdateStatus() { void FeedReader::updateAutoUpdateStatus() {
// Restore global intervals. // Restore global intervals.
// NOTE: Specific per-feed interval are left intact. // NOTE: Specific per-feed interval are left intact.
m_globalAutoUpdateInitialInterval = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::AutoUpdateInterval)).toInt(); m_globalAutoUpdateInterval = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::AutoUpdateInterval)).toInt();
m_globalAutoUpdateRemainingInterval = m_globalAutoUpdateInitialInterval;
if (m_lastAutoUpdate.isNull()) {
m_lastAutoUpdate = QDateTime::currentDateTimeUtc();
}
m_globalAutoUpdateEnabled = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::AutoUpdateEnabled)).toBool(); m_globalAutoUpdateEnabled = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::AutoUpdateEnabled)).toBool();
m_globalAutoUpdateOnlyUnfocused = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::AutoUpdateOnlyUnfocused)).toBool(); m_globalAutoUpdateOnlyUnfocused =
qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::AutoUpdateOnlyUnfocused)).toBool();
// Start global auto-update timer if it is not running yet. // Start global auto-update timer if it is not running yet.
// NOTE: The timer must run even if global auto-update // NOTE: The timer must run even if global auto-update
// is not enabled because user can still enable auto-update // is not enabled because user can still enable auto-update
// for individual feeds. // for individual feeds.
if (!m_autoUpdateTimer->isActive()) { if (!m_autoUpdateTimer->isActive()) {
m_autoUpdateTimer->setInterval(AUTO_UPDATE_INTERVAL); m_autoUpdateTimer->setInterval(AUTO_UPDATE_INTERVAL * 1000);
m_autoUpdateTimer->start(); m_autoUpdateTimer->start();
qDebugNN << LOGSEC_CORE << "Auto-download timer started with interval " qDebugNN << LOGSEC_CORE << "Auto-download timer started with interval " << m_autoUpdateTimer->interval() << " ms.";
<< m_autoUpdateTimer->interval()
<< " ms.";
} }
else { else {
qDebugNN << LOGSEC_CORE << "Auto-download timer is already running."; qDebugNN << LOGSEC_CORE << "Auto-download timer is already running.";
@ -166,19 +176,16 @@ bool FeedReader::autoUpdateEnabled() const {
return m_globalAutoUpdateEnabled; return m_globalAutoUpdateEnabled;
} }
int FeedReader::autoUpdateRemainingInterval() const { int FeedReader::autoUpdateInterval() const {
return m_globalAutoUpdateRemainingInterval; return m_globalAutoUpdateInterval;
}
int FeedReader::autoUpdateInitialInterval() const {
return m_globalAutoUpdateInitialInterval;
} }
void FeedReader::loadSavedMessageFilters() { void FeedReader::loadSavedMessageFilters() {
// Load all message filters from database. // Load all message filters from database.
// All plugin services will hook active filters to // All plugin services will hook active filters to
// all feeds. // all feeds.
m_messageFilters = DatabaseQueries::getMessageFilters(qApp->database()->driver()->connection(metaObject()->className())); m_messageFilters =
DatabaseQueries::getMessageFilters(qApp->database()->driver()->connection(metaObject()->className()));
for (auto* filter : qAsConst(m_messageFilters)) { for (auto* filter : qAsConst(m_messageFilters)) {
filter->setParent(this); filter->setParent(this);
@ -186,7 +193,8 @@ void FeedReader::loadSavedMessageFilters() {
} }
MessageFilter* FeedReader::addMessageFilter(const QString& title, const QString& script) { MessageFilter* FeedReader::addMessageFilter(const QString& title, const QString& script) {
auto* fltr = DatabaseQueries::addMessageFilter(qApp->database()->driver()->connection(metaObject()->className()), title, script); auto* fltr =
DatabaseQueries::addMessageFilter(qApp->database()->driver()->connection(metaObject()->className()), title, script);
m_messageFilters.append(fltr); m_messageFilters.append(fltr);
return fltr; return fltr;
@ -203,7 +211,8 @@ void FeedReader::removeMessageFilter(MessageFilter* filter) {
} }
// Remove from DB. // Remove from DB.
DatabaseQueries::removeMessageFilterAssignments(qApp->database()->driver()->connection(metaObject()->className()), filter->id()); DatabaseQueries::removeMessageFilterAssignments(qApp->database()->driver()->connection(metaObject()->className()),
filter->id());
DatabaseQueries::removeMessageFilter(qApp->database()->driver()->connection(metaObject()->className()), filter->id()); DatabaseQueries::removeMessageFilter(qApp->database()->driver()->connection(metaObject()->className()), filter->id());
// Free from memory as last step. // Free from memory as last step.
@ -264,25 +273,25 @@ void FeedReader::executeNextAutoUpdate() {
bool disable_update_with_window = qApp->mainFormWidget()->isActiveWindow() && m_globalAutoUpdateOnlyUnfocused; bool disable_update_with_window = qApp->mainFormWidget()->isActiveWindow() && m_globalAutoUpdateOnlyUnfocused;
auto roots = qApp->feedReader()->feedsModel()->serviceRoots(); auto roots = qApp->feedReader()->feedsModel()->serviceRoots();
std::list<CacheForServiceRoot*> full_caches = boolinq::from(roots) std::list<CacheForServiceRoot*> full_caches = boolinq::from(roots)
.select([](ServiceRoot* root) -> CacheForServiceRoot* { .select([](ServiceRoot* root) -> CacheForServiceRoot* {
auto* cache = root->toCache(); auto* cache = root->toCache();
if (cache != nullptr) { if (cache != nullptr) {
return cache; return cache;
} }
else { else {
return nullptr; return nullptr;
} }
}) })
.where([](CacheForServiceRoot* cache) { .where([](CacheForServiceRoot* cache) {
return cache != nullptr && !cache->isEmpty(); return cache != nullptr && !cache->isEmpty();
}).toStdList(); })
.toStdList();
// Skip this round of auto-updating, but only if user disabled it when main window is active // Skip this round of auto-updating, but only if user disabled it when main window is active
// and there are no caches to synchronize. // and there are no caches to synchronize.
if (disable_update_with_window && full_caches.empty()) { if (disable_update_with_window && full_caches.empty()) {
qDebugNN << LOGSEC_CORE qDebugNN << LOGSEC_CORE << "Delaying scheduled feed auto-download for some time since window "
<< "Delaying scheduled feed auto-download for one minute since window "
<< "is focused and updates while focused are disabled by the " << "is focused and updates while focused are disabled by the "
<< "user and all account caches are empty."; << "user and all account caches are empty.";
@ -291,26 +300,13 @@ void FeedReader::executeNextAutoUpdate() {
} }
if (!qApp->feedUpdateLock()->tryLock()) { if (!qApp->feedUpdateLock()->tryLock()) {
qDebugNN << LOGSEC_CORE qDebugNN << LOGSEC_CORE << "Delaying scheduled feed auto-downloads and message state synchronization for "
<< "Delaying scheduled feed auto-downloads and message state synchronization for "
<< "one minute due to another running update."; << "one minute due to another running update.";
// Cannot update, quit. // Cannot update, quit.
return; return;
} }
// If global auto-update is enabled and its interval counter reached zero,
// then we need to restore it.
if (m_globalAutoUpdateEnabled && --m_globalAutoUpdateRemainingInterval < 0) {
// We should start next auto-update interval.
m_globalAutoUpdateRemainingInterval = m_globalAutoUpdateInitialInterval - 1;
}
qDebugNN << LOGSEC_CORE
<< "Starting auto-download event, remaining "
<< m_globalAutoUpdateRemainingInterval << " minutes out of "
<< m_globalAutoUpdateInitialInterval << " total minutes to next global feed update.";
qApp->feedUpdateLock()->unlock(); qApp->feedUpdateLock()->unlock();
// Resynchronize caches. // Resynchronize caches.
@ -322,18 +318,29 @@ void FeedReader::executeNextAutoUpdate() {
// Pass needed interval data and lets the model decide which feeds // Pass needed interval data and lets the model decide which feeds
// should be updated in this pass. // should be updated in this pass.
QList<Feed*> feeds_for_update = m_feedsModel->feedsForScheduledUpdate(m_globalAutoUpdateEnabled && QDateTime current_time = QDateTime::currentDateTimeUtc();
m_globalAutoUpdateRemainingInterval == 0); bool auto_update_now =
m_globalAutoUpdateEnabled && m_lastAutoUpdate.addSecs(m_globalAutoUpdateInterval) < current_time;
if (auto_update_now) {
qDebugNN << LOGSEC_CORE << "Now it's time to auto-fetch articles because last auto-fetch was on"
<< QUOTE_W_SPACE(m_lastAutoUpdate) << "and next should be in"
<< NONQUOTE_W_SPACE(m_globalAutoUpdateInterval) << "seconds.";
m_lastAutoUpdate = current_time;
}
QList<Feed*> feeds_for_update = m_feedsModel->feedsForScheduledUpdate(auto_update_now);
if (!feeds_for_update.isEmpty()) { if (!feeds_for_update.isEmpty()) {
// Request update for given feeds. // Request update for given feeds.
updateFeeds(feeds_for_update); updateFeeds(feeds_for_update);
// NOTE: OSD/bubble informing about performing of scheduled update can be shown now. // NOTE: OSD/bubble informing about performing of scheduled update can be shown now.
qApp->showGuiMessage(Notification::Event::ArticlesFetchingStarted, { qApp->showGuiMessage(Notification::Event::ArticlesFetchingStarted,
tr("Starting auto-download of some feeds' articles"), {tr("Starting auto-download of some feeds' articles"),
tr("I will auto-download new articles for %n feed(s).", nullptr, feeds_for_update.size()), tr("I will auto-download new articles for %n feed(s).", nullptr, feeds_for_update.size()),
QSystemTrayIcon::MessageIcon::Information }); QSystemTrayIcon::MessageIcon::Information});
} }
} }

View File

@ -21,7 +21,7 @@ class QTimer;
class QThread; class QThread;
class RSSGUARD_DLLSPEC FeedReader : public QObject { class RSSGUARD_DLLSPEC FeedReader : public QObject {
Q_OBJECT Q_OBJECT
public: public:
explicit FeedReader(QObject* parent = nullptr); explicit FeedReader(QObject* parent = nullptr);
@ -53,8 +53,8 @@ class RSSGUARD_DLLSPEC FeedReader : public QObject {
void updateAutoUpdateStatus(); void updateAutoUpdateStatus();
bool autoUpdateEnabled() const; bool autoUpdateEnabled() const;
int autoUpdateRemainingInterval() const; int autoUpdateInterval() const;
int autoUpdateInitialInterval() const; QDateTime lastAutoUpdate() const;
void loadSavedMessageFilters(); void loadSavedMessageFilters();
QList<MessageFilter*> messageFilters() const; QList<MessageFilter*> messageFilters() const;
@ -93,8 +93,8 @@ class RSSGUARD_DLLSPEC FeedReader : public QObject {
QTimer* m_autoUpdateTimer; QTimer* m_autoUpdateTimer;
bool m_globalAutoUpdateEnabled{}; bool m_globalAutoUpdateEnabled{};
bool m_globalAutoUpdateOnlyUnfocused{}; bool m_globalAutoUpdateOnlyUnfocused{};
int m_globalAutoUpdateInitialInterval{}; int m_globalAutoUpdateInterval{}; // In seconds.
int m_globalAutoUpdateRemainingInterval{}; QDateTime m_lastAutoUpdate;
QThread* m_feedDownloaderThread; QThread* m_feedDownloaderThread;
FeedDownloader* m_feedDownloader; FeedDownloader* m_feedDownloader;
}; };

View File

@ -21,8 +21,8 @@
Feed::Feed(RootItem* parent) Feed::Feed(RootItem* parent)
: RootItem(parent), m_source(QString()), m_status(Status::Normal), m_statusString(QString()), : RootItem(parent), m_source(QString()), m_status(Status::Normal), m_statusString(QString()),
m_autoUpdateType(AutoUpdateType::DefaultAutoUpdate), m_autoUpdateInitialInterval(DEFAULT_AUTO_UPDATE_INTERVAL), m_autoUpdateType(AutoUpdateType::DefaultAutoUpdate), m_autoUpdateInterval(DEFAULT_AUTO_UPDATE_INTERVAL),
m_autoUpdateRemainingInterval(DEFAULT_AUTO_UPDATE_INTERVAL), m_isSwitchedOff(false), m_openArticlesDirectly(false), m_lastUpdated(QDateTime::currentDateTimeUtc()), m_isSwitchedOff(false), m_openArticlesDirectly(false),
m_messageFilters(QList<QPointer<MessageFilter>>()) { m_messageFilters(QList<QPointer<MessageFilter>>()) {
setKind(RootItem::Kind::Feed); setKind(RootItem::Kind::Feed);
} }
@ -41,8 +41,8 @@ Feed::Feed(const Feed& other) : RootItem(other) {
setSource(other.source()); setSource(other.source());
setStatus(other.status(), other.statusString()); setStatus(other.status(), other.statusString());
setAutoUpdateType(other.autoUpdateType()); setAutoUpdateType(other.autoUpdateType());
setAutoUpdateInitialInterval(other.autoUpdateInitialInterval()); setAutoUpdateInterval(other.autoUpdateInterval());
setAutoUpdateRemainingInterval(other.autoUpdateRemainingInterval()); setLastUpdated(other.lastUpdated());
setMessageFilters(other.messageFilters()); setMessageFilters(other.messageFilters());
setOpenArticlesDirectly(other.openArticlesDirectly()); setOpenArticlesDirectly(other.openArticlesDirectly());
setIsSwitchedOff(other.isSwitchedOff()); setIsSwitchedOff(other.isSwitchedOff());
@ -91,8 +91,8 @@ QVariant Feed::data(int column, int role) const {
} }
} }
int Feed::autoUpdateInitialInterval() const { int Feed::autoUpdateInterval() const {
return m_autoUpdateInitialInterval; return m_autoUpdateInterval;
} }
int Feed::countOfAllMessages() const { int Feed::countOfAllMessages() const {
@ -134,11 +134,11 @@ bool Feed::editViaGui() {
return false; return false;
} }
void Feed::setAutoUpdateInitialInterval(int auto_update_interval) { void Feed::setAutoUpdateInterval(int auto_update_interval) {
// If new initial auto-update interval is set, then // If new initial auto-update interval is set, then
// we should reset time that remains to the next auto-update. // we should reset time that remains to the next auto-update.
m_autoUpdateInitialInterval = auto_update_interval; m_autoUpdateInterval = auto_update_interval;
m_autoUpdateRemainingInterval = auto_update_interval; m_lastUpdated = QDateTime::currentDateTimeUtc();
} }
Feed::AutoUpdateType Feed::autoUpdateType() const { Feed::AutoUpdateType Feed::autoUpdateType() const {
@ -149,14 +149,6 @@ void Feed::setAutoUpdateType(Feed::AutoUpdateType auto_update_type) {
m_autoUpdateType = auto_update_type; m_autoUpdateType = auto_update_type;
} }
int Feed::autoUpdateRemainingInterval() const {
return m_autoUpdateRemainingInterval;
}
void Feed::setAutoUpdateRemainingInterval(int auto_update_remaining_interval) {
m_autoUpdateRemainingInterval = auto_update_remaining_interval;
}
Feed::Status Feed::status() const { Feed::Status Feed::status() const {
return m_status; return m_status;
} }
@ -219,28 +211,34 @@ QString Feed::getAutoUpdateStatusDescription() const {
switch (autoUpdateType()) { switch (autoUpdateType()) {
case AutoUpdateType::DontAutoUpdate: case AutoUpdateType::DontAutoUpdate:
//: Describes feed auto-update status. //: Describes feed auto-update status.
auto_update_string = tr("does not use auto-fetching of articles"); auto_update_string = tr("does not use auto-fetching of articles");
break; break;
case AutoUpdateType::DefaultAutoUpdate: case AutoUpdateType::DefaultAutoUpdate:
//: Describes feed auto-update status. //: Describes feed auto-update status.
auto_update_string = qApp->feedReader()->autoUpdateEnabled() if (qApp->feedReader()->autoUpdateEnabled()) {
? tr("uses global settings (%n minute(s) to next auto-fetch of articles)", int secs_to_next =
nullptr, QDateTime::currentDateTimeUtc()
qApp->feedReader()->autoUpdateRemainingInterval()) .secsTo(qApp->feedReader()->lastAutoUpdate().addSecs(qApp->feedReader()->autoUpdateInterval()));
: tr("uses global settings (global auto-fetching of articles is disabled)");
auto_update_string =
tr("uses global settings (%n minute(s) to next auto-fetch of articles)", nullptr, int(secs_to_next / 60.0));
}
else {
auto_update_string = tr("uses global settings, but global auto-fetching of articles is disabled");
}
break; break;
case AutoUpdateType::SpecificAutoUpdate: case AutoUpdateType::SpecificAutoUpdate:
default: default:
int secs_to_next = QDateTime::currentDateTimeUtc().secsTo(lastUpdated().addSecs(autoUpdateInterval()));
//: Describes feed auto-update status. //: Describes feed auto-update status.
auto_update_string = tr("uses specific settings (%n minute(s) to next auto-fetching of new articles)", auto_update_string = tr("uses specific settings (%n minute(s) to next auto-fetching of new articles)",
nullptr, nullptr,
autoUpdateRemainingInterval()); int(secs_to_next / 60.0));
break; break;
} }
@ -269,6 +267,14 @@ QString Feed::getStatusDescription() const {
} }
} }
QDateTime Feed::lastUpdated() const {
return m_lastUpdated;
}
void Feed::setLastUpdated(const QDateTime& last_updated) {
m_lastUpdated = last_updated;
}
bool Feed::isSwitchedOff() const { bool Feed::isSwitchedOff() const {
return m_isSwitchedOff; return m_isSwitchedOff;
} }

View File

@ -53,15 +53,12 @@ class Feed : public RootItem {
void setCountOfAllMessages(int count_all_messages); void setCountOfAllMessages(int count_all_messages);
void setCountOfUnreadMessages(int count_unread_messages); void setCountOfUnreadMessages(int count_unread_messages);
int autoUpdateInitialInterval() const; int autoUpdateInterval() const;
void setAutoUpdateInitialInterval(int auto_update_interval); void setAutoUpdateInterval(int auto_update_interval);
AutoUpdateType autoUpdateType() const; AutoUpdateType autoUpdateType() const;
void setAutoUpdateType(AutoUpdateType auto_update_type); void setAutoUpdateType(AutoUpdateType auto_update_type);
int autoUpdateRemainingInterval() const;
void setAutoUpdateRemainingInterval(int auto_update_remaining_interval);
Status status() const; Status status() const;
QString statusString() const; QString statusString() const;
void setStatus(Feed::Status status, const QString& status_text = {}); void setStatus(Feed::Status status, const QString& status_text = {});
@ -80,6 +77,9 @@ class Feed : public RootItem {
void setMessageFilters(const QList<QPointer<MessageFilter>>& messageFilters); void setMessageFilters(const QList<QPointer<MessageFilter>>& messageFilters);
void removeMessageFilter(MessageFilter* filter); void removeMessageFilter(MessageFilter* filter);
QDateTime lastUpdated() const;
void setLastUpdated(const QDateTime& last_updated);
public slots: public slots:
virtual void updateCounts(bool including_total_count); virtual void updateCounts(bool including_total_count);
@ -92,8 +92,8 @@ class Feed : public RootItem {
Status m_status; Status m_status;
QString m_statusString; QString m_statusString;
AutoUpdateType m_autoUpdateType; AutoUpdateType m_autoUpdateType;
int m_autoUpdateInitialInterval{}; int m_autoUpdateInterval{}; // In seconds.
int m_autoUpdateRemainingInterval{}; QDateTime m_lastUpdated;
bool m_isSwitchedOff; bool m_isSwitchedOff;
bool m_openArticlesDirectly; bool m_openArticlesDirectly;
int m_totalCount{}; int m_totalCount{};

View File

@ -44,9 +44,10 @@ void FormFeedDetails::insertCustomTab(QWidget* custom_tab, const QString& title,
void FormFeedDetails::apply() { void FormFeedDetails::apply() {
// Setup common data for the feed. // Setup common data for the feed.
m_feed->setAutoUpdateType(static_cast<Feed::AutoUpdateType>(m_ui->m_cmbAutoUpdateType->itemData( m_feed->setAutoUpdateType(static_cast<Feed::AutoUpdateType>(m_ui->m_cmbAutoUpdateType
m_ui->m_cmbAutoUpdateType->currentIndex()).toInt())); ->itemData(m_ui->m_cmbAutoUpdateType->currentIndex())
m_feed->setAutoUpdateInitialInterval(int(m_ui->m_spinAutoUpdateInterval->value())); .toInt()));
m_feed->setAutoUpdateInterval(int(m_ui->m_spinAutoUpdateInterval->value()));
m_feed->setOpenArticlesDirectly(m_ui->m_cbOpenArticlesAutomatically->isChecked()); m_feed->setOpenArticlesDirectly(m_ui->m_cbOpenArticlesAutomatically->isChecked());
m_feed->setIsSwitchedOff(m_ui->m_cbDisableFeed->isChecked()); m_feed->setIsSwitchedOff(m_ui->m_cbDisableFeed->isChecked());
@ -59,7 +60,8 @@ void FormFeedDetails::apply() {
} }
void FormFeedDetails::onAutoUpdateTypeChanged(int new_index) { void FormFeedDetails::onAutoUpdateTypeChanged(int new_index) {
Feed::AutoUpdateType auto_update_type = static_cast<Feed::AutoUpdateType>(m_ui->m_cmbAutoUpdateType->itemData(new_index).toInt()); Feed::AutoUpdateType auto_update_type =
static_cast<Feed::AutoUpdateType>(m_ui->m_cmbAutoUpdateType->itemData(new_index).toInt());
switch (auto_update_type) { switch (auto_update_type) {
case Feed::AutoUpdateType::DontAutoUpdate: case Feed::AutoUpdateType::DontAutoUpdate:
@ -74,7 +76,9 @@ void FormFeedDetails::onAutoUpdateTypeChanged(int new_index) {
void FormFeedDetails::createConnections() { void FormFeedDetails::createConnections() {
connect(m_ui->m_buttonBox, &QDialogButtonBox::accepted, this, &FormFeedDetails::acceptIfPossible); connect(m_ui->m_buttonBox, &QDialogButtonBox::accepted, this, &FormFeedDetails::acceptIfPossible);
connect(m_ui->m_cmbAutoUpdateType, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, connect(m_ui->m_cmbAutoUpdateType,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
this,
&FormFeedDetails::onAutoUpdateTypeChanged); &FormFeedDetails::onAutoUpdateTypeChanged);
} }
@ -85,13 +89,12 @@ void FormFeedDetails::loadFeedData() {
tr("Add new feed")); tr("Add new feed"));
} }
else { else {
GuiUtilities::applyDialogProperties(*this, GuiUtilities::applyDialogProperties(*this, m_feed->fullIcon(), tr("Edit \"%1\"").arg(m_feed->title()));
m_feed->fullIcon(),
tr("Edit \"%1\"").arg(m_feed->title()));
} }
m_ui->m_cmbAutoUpdateType->setCurrentIndex(m_ui->m_cmbAutoUpdateType->findData(QVariant::fromValue(int(m_feed->autoUpdateType())))); m_ui->m_cmbAutoUpdateType
m_ui->m_spinAutoUpdateInterval->setValue(m_feed->autoUpdateInitialInterval()); ->setCurrentIndex(m_ui->m_cmbAutoUpdateType->findData(QVariant::fromValue(int(m_feed->autoUpdateType()))));
m_ui->m_spinAutoUpdateInterval->setValue(m_feed->autoUpdateInterval());
m_ui->m_cbOpenArticlesAutomatically->setChecked(m_feed->openArticlesDirectly()); m_ui->m_cbOpenArticlesAutomatically->setChecked(m_feed->openArticlesDirectly());
m_ui->m_cbDisableFeed->setChecked(m_feed->isSwitchedOff()); m_ui->m_cbDisableFeed->setChecked(m_feed->isSwitchedOff());
} }
@ -102,11 +105,12 @@ void FormFeedDetails::acceptIfPossible() {
accept(); accept();
} }
catch (const ApplicationException& ex) { catch (const ApplicationException& ex) {
qApp->showGuiMessage(Notification::Event::GeneralEvent, { qApp->showGuiMessage(Notification::Event::GeneralEvent,
tr("Cannot save feed properties"), {tr("Cannot save feed properties"),
tr("Cannot save changes: %1").arg(ex.message()), tr("Cannot save changes: %1").arg(ex.message()),
QSystemTrayIcon::MessageIcon::Critical }, QSystemTrayIcon::MessageIcon::Critical},
{}, {}, {},
{},
this); this);
} }
} }
@ -116,6 +120,7 @@ void FormFeedDetails::initialize() {
m_ui->setupUi(this); m_ui->setupUi(this);
// Setup auto-update options. // Setup auto-update options.
m_ui->m_spinAutoUpdateInterval->setMode(TimeSpinBox::Mode::MinutesSeconds);
m_ui->m_spinAutoUpdateInterval->setValue(DEFAULT_AUTO_UPDATE_INTERVAL); m_ui->m_spinAutoUpdateInterval->setValue(DEFAULT_AUTO_UPDATE_INTERVAL);
m_ui->m_cmbAutoUpdateType->addItem(tr("Fetch articles using global interval"), m_ui->m_cmbAutoUpdateType->addItem(tr("Fetch articles using global interval"),
QVariant::fromValue(int(Feed::AutoUpdateType::DefaultAutoUpdate))); QVariant::fromValue(int(Feed::AutoUpdateType::DefaultAutoUpdate)));

View File

@ -380,7 +380,7 @@ QMap<QString, QVariantMap> ServiceRoot::storeCustomFeedsData() {
// TODO: This could potentially call Feed::customDatabaseData() and append it // TODO: This could potentially call Feed::customDatabaseData() and append it
// to this map and also subsequently restore. // to this map and also subsequently restore.
feed_custom_data.insert(QSL("auto_update_interval"), feed->autoUpdateInitialInterval()); feed_custom_data.insert(QSL("auto_update_interval"), feed->autoUpdateInterval());
feed_custom_data.insert(QSL("auto_update_type"), int(feed->autoUpdateType())); feed_custom_data.insert(QSL("auto_update_type"), int(feed->autoUpdateType()));
feed_custom_data.insert(QSL("msg_filters"), QVariant::fromValue(feed->messageFilters())); feed_custom_data.insert(QSL("msg_filters"), QVariant::fromValue(feed->messageFilters()));
feed_custom_data.insert(QSL("is_off"), feed->isSwitchedOff()); feed_custom_data.insert(QSL("is_off"), feed->isSwitchedOff());
@ -403,7 +403,7 @@ void ServiceRoot::restoreCustomFeedsData(const QMap<QString, QVariantMap>& data,
Feed* feed = feeds.value(custom_id); Feed* feed = feeds.value(custom_id);
QVariantMap feed_custom_data = i.value(); QVariantMap feed_custom_data = i.value();
feed->setAutoUpdateInitialInterval(feed_custom_data.value(QSL("auto_update_interval")).toInt()); feed->setAutoUpdateInterval(feed_custom_data.value(QSL("auto_update_interval")).toInt());
feed feed
->setAutoUpdateType(static_cast<Feed::AutoUpdateType>(feed_custom_data.value(QSL("auto_update_type")).toInt())); ->setAutoUpdateType(static_cast<Feed::AutoUpdateType>(feed_custom_data.value(QSL("auto_update_type")).toInt()));
feed->setMessageFilters(feed_custom_data.value(QSL("msg_filters")).value<QList<QPointer<MessageFilter>>>()); feed->setMessageFilters(feed_custom_data.value(QSL("msg_filters")).value<QList<QPointer<MessageFilter>>>());