From 8676bfc42d448055fb750c6906702242b8b184fe Mon Sep 17 00:00:00 2001 From: Martin Rotter Date: Fri, 2 Sep 2022 08:59:09 +0200 Subject: [PATCH] save ttrss work --- .../desktop/com.github.rssguard.appdata.xml | 3 +- .../tt-rss/gui/formeditttrssaccount.cpp | 9 +- .../tt-rss/gui/ttrssaccountdetails.cpp | 71 +- .../tt-rss/gui/ttrssaccountdetails.ui | 50 +- .../services/tt-rss/ttrssnetworkfactory.cpp | 632 +++++++++++------- .../services/tt-rss/ttrssnetworkfactory.h | 72 +- .../services/tt-rss/ttrssserviceroot.cpp | 79 ++- .../services/tt-rss/ttrssserviceroot.h | 6 +- 8 files changed, 608 insertions(+), 314 deletions(-) diff --git a/resources/desktop/com.github.rssguard.appdata.xml b/resources/desktop/com.github.rssguard.appdata.xml index 5df20f7dd..58814dd32 100644 --- a/resources/desktop/com.github.rssguard.appdata.xml +++ b/resources/desktop/com.github.rssguard.appdata.xml @@ -22,11 +22,10 @@ https://github.com/martinrotter/rssguard https://github.com/martinrotter/rssguard/issues - https://www.transifex.com/martinrotter/rssguard https://github.com/sponsors/martinrotter - + none diff --git a/src/librssguard/services/tt-rss/gui/formeditttrssaccount.cpp b/src/librssguard/services/tt-rss/gui/formeditttrssaccount.cpp index 04d644fcf..6ed3d2b1d 100644 --- a/src/librssguard/services/tt-rss/gui/formeditttrssaccount.cpp +++ b/src/librssguard/services/tt-rss/gui/formeditttrssaccount.cpp @@ -32,8 +32,12 @@ void FormEditTtRssAccount::apply() { account()->network()->setAuthUsername(m_details->m_ui.m_txtHttpUsername->lineEdit()->text()); account()->network()->setAuthPassword(m_details->m_ui.m_txtHttpPassword->lineEdit()->text()); account()->network()->setBatchSize(m_details->m_ui.m_spinLimitMessages->value()); - account()->network()->setForceServerSideUpdate(m_details->m_ui.m_checkServerSideUpdate->isChecked()); - account()->network()->setDownloadOnlyUnreadMessages(m_details->m_ui.m_checkDownloadOnlyUnreadMessages->isChecked()); + account()->network()->setIntelligentSynchronization(m_details->m_ui.m_cbNewAlgorithm->isChecked()); + account()->network()->setForceServerSideUpdate(m_details->m_ui.m_checkServerSideUpdate + ->isChecked()); + account() + ->network() + ->setDownloadOnlyUnreadMessages(m_details->m_ui.m_checkDownloadOnlyUnreadMessages->isChecked()); account()->saveAccountDataToDatabase(); accept(); @@ -58,6 +62,7 @@ void FormEditTtRssAccount::loadAccountData() { m_details->m_ui.m_spinLimitMessages->setValue(existing_root->network()->batchSize()); m_details->m_ui.m_checkServerSideUpdate->setChecked(existing_root->network()->forceServerSideUpdate()); m_details->m_ui.m_checkDownloadOnlyUnreadMessages->setChecked(existing_root->network()->downloadOnlyUnreadMessages()); + m_details->m_ui.m_cbNewAlgorithm->setChecked(existing_root->network()->intelligentSynchronization()); } void FormEditTtRssAccount::performTest() { diff --git a/src/librssguard/services/tt-rss/gui/ttrssaccountdetails.cpp b/src/librssguard/services/tt-rss/gui/ttrssaccountdetails.cpp index a65d656da..aac7073af 100644 --- a/src/librssguard/services/tt-rss/gui/ttrssaccountdetails.cpp +++ b/src/librssguard/services/tt-rss/gui/ttrssaccountdetails.cpp @@ -11,9 +11,16 @@ TtRssAccountDetails::TtRssAccountDetails(QWidget* parent) : QWidget(parent) { m_ui.setupUi(this); m_ui.m_lblTestResult->label()->setWordWrap(true); - m_ui.m_lblServerSideUpdateInformation->setHelpText(tr("Leaving this option on causes that updates " - "of feeds will be probably much slower and may time-out often."), - true); + m_ui.m_lblNewAlgorithm->setHelpText(tr("If you select intelligent synchronization, then only not-yet-fetched " + "or updated articles are downloaded. Network usage is greatly reduced and " + "overall synchronization speed is greatly improved, but " + "first feed fetching could be slow anyway if your feed contains " + "huge number of articles."), + false); + m_ui.m_lblServerSideUpdateInformation + ->setHelpText(tr("Leaving this option on causes that updates " + "of feeds will be probably much slower and may time-out often."), + true); m_ui.m_txtHttpUsername->lineEdit()->setPlaceholderText(tr("HTTP authentication username")); m_ui.m_txtHttpPassword->lineEdit()->setPlaceholderText(tr("HTTP authentication password")); m_ui.m_txtPassword->lineEdit()->setPlaceholderText(tr("Password for your TT-RSS account")); @@ -25,7 +32,8 @@ TtRssAccountDetails::TtRssAccountDetails(QWidget* parent) : QWidget(parent) { setTabOrder(m_ui.m_txtUrl->lineEdit(), m_ui.m_checkDownloadOnlyUnreadMessages); setTabOrder(m_ui.m_checkDownloadOnlyUnreadMessages, m_ui.m_spinLimitMessages); - setTabOrder(m_ui.m_spinLimitMessages, m_ui.m_checkServerSideUpdate); + setTabOrder(m_ui.m_spinLimitMessages, m_ui.m_cbNewAlgorithm); + setTabOrder(m_ui.m_cbNewAlgorithm, m_ui.m_checkServerSideUpdate); setTabOrder(m_ui.m_checkServerSideUpdate, m_ui.m_txtUsername->lineEdit()); setTabOrder(m_ui.m_txtUsername->lineEdit(), m_ui.m_txtPassword->lineEdit()); setTabOrder(m_ui.m_txtPassword->lineEdit(), m_ui.m_gbHttpAuthentication); @@ -38,8 +46,14 @@ TtRssAccountDetails::TtRssAccountDetails(QWidget* parent) : QWidget(parent) { connect(m_ui.m_txtPassword->lineEdit(), &BaseLineEdit::textChanged, this, &TtRssAccountDetails::onPasswordChanged); connect(m_ui.m_txtUsername->lineEdit(), &BaseLineEdit::textChanged, this, &TtRssAccountDetails::onUsernameChanged); - connect(m_ui.m_txtHttpPassword->lineEdit(), &BaseLineEdit::textChanged, this, &TtRssAccountDetails::onHttpPasswordChanged); - connect(m_ui.m_txtHttpUsername->lineEdit(), &BaseLineEdit::textChanged, this, &TtRssAccountDetails::onHttpUsernameChanged); + connect(m_ui.m_txtHttpPassword->lineEdit(), + &BaseLineEdit::textChanged, + this, + &TtRssAccountDetails::onHttpPasswordChanged); + connect(m_ui.m_txtHttpUsername->lineEdit(), + &BaseLineEdit::textChanged, + this, + &TtRssAccountDetails::onHttpUsernameChanged); connect(m_ui.m_txtUrl->lineEdit(), &BaseLineEdit::textChanged, this, &TtRssAccountDetails::onUrlChanged); connect(m_ui.m_gbHttpAuthentication, &QGroupBox::toggled, this, &TtRssAccountDetails::onHttpPasswordChanged); connect(m_ui.m_gbHttpAuthentication, &QGroupBox::toggled, this, &TtRssAccountDetails::onHttpUsernameChanged); @@ -87,21 +101,24 @@ void TtRssAccountDetails::performTest(const QNetworkProxy& proxy) { } else if (result.apiLevel() < TTRSS_MINIMAL_API_LEVEL) { m_ui.m_lblTestResult->setStatus(WidgetWithStatus::StatusType::Error, - tr("Installed version: %1, required at least: %2.").arg(QString::number(result.apiLevel()), - QString::number(TTRSS_MINIMAL_API_LEVEL)), + tr("Installed version: %1, required at least: %2.") + .arg(QString::number(result.apiLevel()), + QString::number(TTRSS_MINIMAL_API_LEVEL)), tr("Selected Tiny Tiny RSS server is running unsupported version of API.")); } else { m_ui.m_lblTestResult->setStatus(WidgetWithStatus::StatusType::Ok, - tr("Installed version: %1, required at least: %2.").arg(QString::number(result.apiLevel()), - QString::number(TTRSS_MINIMAL_API_LEVEL)), + tr("Installed version: %1, required at least: %2.") + .arg(QString::number(result.apiLevel()), + QString::number(TTRSS_MINIMAL_API_LEVEL)), tr("Tiny Tiny RSS server is okay.")); } } - else if (factory.lastError() != QNetworkReply::NoError) { - m_ui.m_lblTestResult->setStatus(WidgetWithStatus::StatusType::Error, - tr("Network error: '%1'.").arg(NetworkFactory::networkErrorText(factory.lastError())), - tr("Network error, have you entered correct Tiny Tiny RSS API endpoint and password?")); + else if (factory.lastError() != QNetworkReply::NoError) { + m_ui.m_lblTestResult + ->setStatus(WidgetWithStatus::StatusType::Error, + tr("Network error: '%1'.").arg(NetworkFactory::networkErrorText(factory.lastError())), + tr("Network error, have you entered correct Tiny Tiny RSS API endpoint and password?")); } else { m_ui.m_lblTestResult->setStatus(WidgetWithStatus::StatusType::Error, @@ -133,25 +150,23 @@ void TtRssAccountDetails::onPasswordChanged() { } void TtRssAccountDetails::onHttpUsernameChanged() { - const bool is_username_ok = !m_ui.m_gbHttpAuthentication->isChecked() || !m_ui.m_txtHttpUsername->lineEdit()->text().isEmpty(); + const bool is_username_ok = + !m_ui.m_gbHttpAuthentication->isChecked() || !m_ui.m_txtHttpUsername->lineEdit()->text().isEmpty(); - m_ui.m_txtHttpUsername->setStatus(is_username_ok ? - LineEditWithStatus::StatusType::Ok : - LineEditWithStatus::StatusType::Warning, - is_username_ok ? - tr("Username is ok or it is not needed.") : - tr("Username is empty.")); + m_ui.m_txtHttpUsername->setStatus(is_username_ok ? LineEditWithStatus::StatusType::Ok + : LineEditWithStatus::StatusType::Warning, + is_username_ok ? tr("Username is ok or it is not needed.") + : tr("Username is empty.")); } void TtRssAccountDetails::onHttpPasswordChanged() { - const bool is_username_ok = !m_ui.m_gbHttpAuthentication->isChecked() || !m_ui.m_txtHttpPassword->lineEdit()->text().isEmpty(); + const bool is_username_ok = + !m_ui.m_gbHttpAuthentication->isChecked() || !m_ui.m_txtHttpPassword->lineEdit()->text().isEmpty(); - m_ui.m_txtHttpPassword->setStatus(is_username_ok ? - LineEditWithStatus::StatusType::Ok : - LineEditWithStatus::StatusType::Warning, - is_username_ok ? - tr("Password is ok or it is not needed.") : - tr("Password is empty.")); + m_ui.m_txtHttpPassword->setStatus(is_username_ok ? LineEditWithStatus::StatusType::Ok + : LineEditWithStatus::StatusType::Warning, + is_username_ok ? tr("Password is ok or it is not needed.") + : tr("Password is empty.")); } void TtRssAccountDetails::onUrlChanged() { diff --git a/src/librssguard/services/tt-rss/gui/ttrssaccountdetails.ui b/src/librssguard/services/tt-rss/gui/ttrssaccountdetails.ui index a25d0bb76..2692e93f5 100644 --- a/src/librssguard/services/tt-rss/gui/ttrssaccountdetails.ui +++ b/src/librssguard/services/tt-rss/gui/ttrssaccountdetails.ui @@ -11,7 +11,7 @@ - + Qt::Vertical @@ -41,13 +41,6 @@ - - - - Download unread articles only - - - @@ -85,17 +78,28 @@ + + + + Download unread articles only + + + + + + Intelligent synchronization algorithm + + + + Force execution of server-side feeds update - - - - + Some feeds require authentication, including GMail feeds. BASIC, NTLM-2 and DIGEST-MD5 authentication schemes are supported. @@ -139,7 +143,7 @@ - + Some feeds require authentication, including GMail feeds. BASIC, NTLM-2 and DIGEST-MD5 authentication schemes are supported. @@ -186,7 +190,7 @@ - + @@ -210,21 +214,27 @@ + + + + + + - - LineEditWithStatus - QWidget -
lineeditwithstatus.h
- 1 -
LabelWithStatus QWidget
labelwithstatus.h
1
+ + LineEditWithStatus + QWidget +
lineeditwithstatus.h
+ 1 +
MessageCountSpinBox QSpinBox diff --git a/src/librssguard/services/tt-rss/ttrssnetworkfactory.cpp b/src/librssguard/services/tt-rss/ttrssnetworkfactory.cpp index d5a1a8cc4..5206fe207 100644 --- a/src/librssguard/services/tt-rss/ttrssnetworkfactory.cpp +++ b/src/librssguard/services/tt-rss/ttrssnetworkfactory.cpp @@ -22,9 +22,10 @@ #include TtRssNetworkFactory::TtRssNetworkFactory() - : m_bareUrl(QString()), m_fullUrl(QString()), m_username(QString()), m_password(QString()), m_batchSize(TTRSS_DEFAULT_MESSAGES), - m_forceServerSideUpdate(false), m_authIsUsed(false), m_authUsername(QString()), m_authPassword(QString()), m_sessionId(QString()), - m_lastError(QNetworkReply::NoError) {} + : m_bareUrl(QString()), m_fullUrl(QString()), m_username(QString()), m_password(QString()), + m_batchSize(TTRSS_DEFAULT_MESSAGES), m_forceServerSideUpdate(false), m_intelligentSynchronization(true), + m_authIsUsed(false), m_authUsername(QString()), m_authPassword(QString()), m_sessionId(QString()), + m_lastError(QNetworkReply::NoError) {} QString TtRssNetworkFactory::url() const { return m_bareUrl; @@ -71,8 +72,7 @@ QNetworkReply::NetworkError TtRssNetworkFactory::lastError() const { TtRssLoginResponse TtRssNetworkFactory::login(const QNetworkProxy& proxy) { if (!m_sessionId.isEmpty()) { - qWarningNN << LOGSEC_TTRSS - << "Session ID is not empty before login, logging out first."; + qWarningNN << LOGSEC_TTRSS << "Session ID is not empty before login, logging out first."; logout(proxy); } @@ -88,18 +88,19 @@ TtRssLoginResponse TtRssNetworkFactory::login(const QNetworkProxy& proxy) { headers << QPair(HTTP_HEADERS_CONTENT_TYPE, TTRSS_CONTENT_TYPE_JSON); headers << NetworkFactory::generateBasicAuthHeader(m_authUsername, m_authPassword); - NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, - qApp->settings()->value(GROUP(Feeds), - SETTING( - Feeds::UpdateTimeout)).toInt(), - QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), - result_raw, - QNetworkAccessManager::Operation::PostOperation, - headers, - false, - {}, - {}, - proxy); + NetworkResult network_reply = + NetworkFactory::performNetworkOperation(m_fullUrl, + qApp->settings() + ->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)) + .toInt(), + QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), + result_raw, + QNetworkAccessManager::Operation::PostOperation, + headers, + false, + {}, + {}, + proxy); TtRssLoginResponse login_response(QString::fromUtf8(result_raw)); if (network_reply.m_networkError == QNetworkReply::NoError) { @@ -107,9 +108,7 @@ TtRssLoginResponse TtRssNetworkFactory::login(const QNetworkProxy& proxy) { m_lastLoginTime = QDateTime::currentDateTime(); } else { - qWarningNN << LOGSEC_TTRSS - << "Login failed with error:" - << QUOTE_W_SPACE_DOT(network_reply.m_networkError); + qWarningNN << LOGSEC_TTRSS << "Login failed with error:" << QUOTE_W_SPACE_DOT(network_reply.m_networkError); } m_lastError = network_reply.m_networkError; @@ -128,18 +127,19 @@ TtRssResponse TtRssNetworkFactory::logout(const QNetworkProxy& proxy) { headers << QPair(HTTP_HEADERS_CONTENT_TYPE, TTRSS_CONTENT_TYPE_JSON); headers << NetworkFactory::generateBasicAuthHeader(m_authUsername, m_authPassword); - NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, - qApp->settings()->value(GROUP(Feeds), - SETTING( - Feeds::UpdateTimeout)).toInt(), - QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), - result_raw, - QNetworkAccessManager::Operation::PostOperation, - headers, - false, - {}, - {}, - proxy); + NetworkResult network_reply = + NetworkFactory::performNetworkOperation(m_fullUrl, + qApp->settings() + ->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)) + .toInt(), + QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), + result_raw, + QNetworkAccessManager::Operation::PostOperation, + headers, + false, + {}, + {}, + proxy); m_lastError = network_reply.m_networkError; @@ -147,16 +147,13 @@ TtRssResponse TtRssNetworkFactory::logout(const QNetworkProxy& proxy) { m_sessionId.clear(); } else { - qWarningNN << LOGSEC_TTRSS - << "Logout failed with error:" - << QUOTE_W_SPACE_DOT(network_reply.m_networkError); + qWarningNN << LOGSEC_TTRSS << "Logout failed with error:" << QUOTE_W_SPACE_DOT(network_reply.m_networkError); } return TtRssResponse(QString::fromUtf8(result_raw)); } else { - qWarningNN << LOGSEC_TTRSS - << "Cannot logout because session ID is empty."; + qWarningNN << LOGSEC_TTRSS << "Cannot logout because session ID is empty."; m_lastError = QNetworkReply::NetworkError::NoError; return TtRssResponse(); } @@ -175,34 +172,35 @@ TtRssGetLabelsResponse TtRssNetworkFactory::getLabels(const QNetworkProxy& proxy headers << QPair(HTTP_HEADERS_CONTENT_TYPE, TTRSS_CONTENT_TYPE_JSON); headers << NetworkFactory::generateBasicAuthHeader(m_authUsername, m_authPassword); - NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, timeout, - QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), - result_raw, - QNetworkAccessManager::Operation::PostOperation, - headers); + NetworkResult network_reply = + NetworkFactory::performNetworkOperation(m_fullUrl, + timeout, + QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), + result_raw, + QNetworkAccessManager::Operation::PostOperation, + headers); TtRssGetLabelsResponse result(QString::fromUtf8(result_raw)); if (result.isNotLoggedIn()) { // We are not logged in. login(proxy); json[QSL("sid")] = m_sessionId; - network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, - timeout, - QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), - result_raw, - QNetworkAccessManager::Operation::PostOperation, - headers, - false, - {}, - {}, - proxy); + network_reply = + NetworkFactory::performNetworkOperation(m_fullUrl, + timeout, + QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), + result_raw, + QNetworkAccessManager::Operation::PostOperation, + headers, + false, + {}, + {}, + proxy); result = TtRssGetLabelsResponse(QString::fromUtf8(result_raw)); } if (network_reply.m_networkError != QNetworkReply::NoError) { - qWarningNN << LOGSEC_TTRSS - << "getLabels failed with error:" - << QUOTE_W_SPACE_DOT(network_reply.m_networkError); + qWarningNN << LOGSEC_TTRSS << "getLabels failed with error:" << QUOTE_W_SPACE_DOT(network_reply.m_networkError); } m_lastError = network_reply.m_networkError; @@ -225,39 +223,40 @@ TtRssResponse TtRssNetworkFactory::shareToPublished(const TtRssNoteToPublish& no headers << QPair(HTTP_HEADERS_CONTENT_TYPE, TTRSS_CONTENT_TYPE_JSON); headers << NetworkFactory::generateBasicAuthHeader(m_authUsername, m_authPassword); - NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, - timeout, - QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), - result_raw, - QNetworkAccessManager::Operation::PostOperation, - headers, - false, - {}, - {}, - proxy); + NetworkResult network_reply = + NetworkFactory::performNetworkOperation(m_fullUrl, + timeout, + QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), + result_raw, + QNetworkAccessManager::Operation::PostOperation, + headers, + false, + {}, + {}, + proxy); TtRssResponse result(QString::fromUtf8(result_raw)); if (result.isNotLoggedIn()) { // We are not logged in. login(proxy); json[QSL("sid")] = m_sessionId; - network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, - timeout, - QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), - result_raw, - QNetworkAccessManager::Operation::PostOperation, - headers, - false, - {}, - {}, - proxy); + network_reply = + NetworkFactory::performNetworkOperation(m_fullUrl, + timeout, + QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), + result_raw, + QNetworkAccessManager::Operation::PostOperation, + headers, + false, + {}, + {}, + proxy); result = TtRssResponse(QString::fromUtf8(result_raw)); } if (network_reply.m_networkError != QNetworkReply::NoError) { qWarningNN << LOGSEC_TTRSS - << "shareToPublished failed with error:" - << QUOTE_W_SPACE_DOT(network_reply.m_networkError); + << "shareToPublished failed with error:" << QUOTE_W_SPACE_DOT(network_reply.m_networkError); } m_lastError = network_reply.m_networkError; @@ -277,47 +276,166 @@ TtRssGetFeedsCategoriesResponse TtRssNetworkFactory::getFeedsCategories(const QN headers << QPair(HTTP_HEADERS_CONTENT_TYPE, TTRSS_CONTENT_TYPE_JSON); headers << NetworkFactory::generateBasicAuthHeader(m_authUsername, m_authPassword); - NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, timeout, - QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), - result_raw, - QNetworkAccessManager::Operation::PostOperation, - headers, - false, - {}, - {}, - proxy); + NetworkResult network_reply = + NetworkFactory::performNetworkOperation(m_fullUrl, + timeout, + QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), + result_raw, + QNetworkAccessManager::Operation::PostOperation, + headers, + false, + {}, + {}, + proxy); TtRssGetFeedsCategoriesResponse result(QString::fromUtf8(result_raw)); if (result.isNotLoggedIn()) { // We are not logged in. login(proxy); json[QSL("sid")] = m_sessionId; - network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, - timeout, - QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), - result_raw, - QNetworkAccessManager::Operation::PostOperation, - headers, - false, - {}, - {}, - proxy); + network_reply = + NetworkFactory::performNetworkOperation(m_fullUrl, + timeout, + QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), + result_raw, + QNetworkAccessManager::Operation::PostOperation, + headers, + false, + {}, + {}, + proxy); result = TtRssGetFeedsCategoriesResponse(QString::fromUtf8(result_raw)); } if (network_reply.m_networkError != QNetworkReply::NoError) { - qWarningNN << LOGSEC_TTRSS - << "getFeedTree failed with error:" - << QUOTE_W_SPACE_DOT(network_reply.m_networkError); + qWarningNN << LOGSEC_TTRSS << "getFeedTree failed with error:" << QUOTE_W_SPACE_DOT(network_reply.m_networkError); } m_lastError = network_reply.m_networkError; return result; } -TtRssGetHeadlinesResponse TtRssNetworkFactory::getHeadlines(int feed_id, int limit, int skip, - bool show_content, bool include_attachments, - bool sanitize, bool unread_only, +TtRssGetCompactHeadlinesResponse TtRssNetworkFactory::getCompactHeadlines(int feed_id, + int limit, + int skip, + const QString& view_mode, + const QNetworkProxy& proxy) { + QJsonObject json; + + json[QSL("op")] = QSL("getCompactHeadlines"); + json[QSL("sid")] = m_sessionId; + json[QSL("feed_id")] = feed_id; + json[QSL("limit")] = limit; + // json[QSL("skip")] = skip; + json[QSL("view_mode")] = view_mode; + + const int timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(); + QByteArray result_raw; + QList> headers; + + headers << QPair(HTTP_HEADERS_CONTENT_TYPE, TTRSS_CONTENT_TYPE_JSON); + headers << NetworkFactory::generateBasicAuthHeader(m_authUsername, m_authPassword); + + NetworkResult network_reply = + NetworkFactory::performNetworkOperation(m_fullUrl, + timeout, + QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), + result_raw, + QNetworkAccessManager::Operation::PostOperation, + headers, + false, + {}, + {}, + proxy); + TtRssGetCompactHeadlinesResponse result(QString::fromUtf8(result_raw)); + + if (result.isNotLoggedIn()) { + // We are not logged in. + login(proxy); + json[QSL("sid")] = m_sessionId; + network_reply = + NetworkFactory::performNetworkOperation(m_fullUrl, + timeout, + QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), + result_raw, + QNetworkAccessManager::Operation::PostOperation, + headers, + false, + {}, + {}, + proxy); + result = TtRssGetCompactHeadlinesResponse(QString::fromUtf8(result_raw)); + } + + if (network_reply.m_networkError != QNetworkReply::NetworkError::NoError) { + qWarningNN << LOGSEC_TTRSS + << "getCompactHeadlines failed with error:" << QUOTE_W_SPACE_DOT(network_reply.m_networkError); + } + + m_lastError = network_reply.m_networkError; + return result; +} + +TtRssGetHeadlinesResponse TtRssNetworkFactory::getArticle(const QStringList& article_ids, const QNetworkProxy& proxy) { + QJsonObject json; + + json[QSL("op")] = QSL("getArticle"); + json[QSL("sid")] = m_sessionId; + json[QSL("article_id")] = article_ids.join(QL1C(',')); + + const int timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(); + QByteArray result_raw; + QList> headers; + + headers << QPair(HTTP_HEADERS_CONTENT_TYPE, TTRSS_CONTENT_TYPE_JSON); + headers << NetworkFactory::generateBasicAuthHeader(m_authUsername, m_authPassword); + + NetworkResult network_reply = + NetworkFactory::performNetworkOperation(m_fullUrl, + timeout, + QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), + result_raw, + QNetworkAccessManager::Operation::PostOperation, + headers, + false, + {}, + {}, + proxy); + TtRssGetHeadlinesResponse result(QString::fromUtf8(result_raw)); + + if (result.isNotLoggedIn()) { + // We are not logged in. + login(proxy); + json[QSL("sid")] = m_sessionId; + network_reply = + NetworkFactory::performNetworkOperation(m_fullUrl, + timeout, + QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), + result_raw, + QNetworkAccessManager::Operation::PostOperation, + headers, + false, + {}, + {}, + proxy); + result = TtRssGetHeadlinesResponse(QString::fromUtf8(result_raw)); + } + + if (network_reply.m_networkError != QNetworkReply::NetworkError::NoError) { + qWarningNN << LOGSEC_TTRSS << "getArticle failed with error:" << QUOTE_W_SPACE_DOT(network_reply.m_networkError); + } + + m_lastError = network_reply.m_networkError; + return result; +} + +TtRssGetHeadlinesResponse TtRssNetworkFactory::getHeadlines(int feed_id, + int limit, + int skip, + bool show_content, + bool include_attachments, + bool sanitize, + bool unread_only, const QNetworkProxy& proxy) { QJsonObject json; @@ -339,47 +457,49 @@ TtRssGetHeadlinesResponse TtRssNetworkFactory::getHeadlines(int feed_id, int lim headers << QPair(HTTP_HEADERS_CONTENT_TYPE, TTRSS_CONTENT_TYPE_JSON); headers << NetworkFactory::generateBasicAuthHeader(m_authUsername, m_authPassword); - NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, - timeout, - QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), - result_raw, - QNetworkAccessManager::Operation::PostOperation, - headers, - false, - {}, - {}, - proxy); + NetworkResult network_reply = + NetworkFactory::performNetworkOperation(m_fullUrl, + timeout, + QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), + result_raw, + QNetworkAccessManager::Operation::PostOperation, + headers, + false, + {}, + {}, + proxy); TtRssGetHeadlinesResponse result(QString::fromUtf8(result_raw)); if (result.isNotLoggedIn()) { // We are not logged in. login(proxy); json[QSL("sid")] = m_sessionId; - network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, - timeout, - QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), - result_raw, - QNetworkAccessManager::Operation::PostOperation, - headers, - false, - {}, - {}, - proxy); + network_reply = + NetworkFactory::performNetworkOperation(m_fullUrl, + timeout, + QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), + result_raw, + QNetworkAccessManager::Operation::PostOperation, + headers, + false, + {}, + {}, + proxy); result = TtRssGetHeadlinesResponse(QString::fromUtf8(result_raw)); } if (network_reply.m_networkError != QNetworkReply::NoError) { - qWarningNN << LOGSEC_TTRSS - << "getHeadlines failed with error:" - << QUOTE_W_SPACE_DOT(network_reply.m_networkError); + qWarningNN << LOGSEC_TTRSS << "getHeadlines failed with error:" << QUOTE_W_SPACE_DOT(network_reply.m_networkError); } m_lastError = network_reply.m_networkError; return result; } -TtRssResponse TtRssNetworkFactory::setArticleLabel(const QStringList& article_ids, const QString& label_custom_id, - bool assign, const QNetworkProxy& proxy) { +TtRssResponse TtRssNetworkFactory::setArticleLabel(const QStringList& article_ids, + const QString& label_custom_id, + bool assign, + const QNetworkProxy& proxy) { QJsonObject json; json[QSL("op")] = QSL("setArticleLabel"); @@ -395,38 +515,39 @@ TtRssResponse TtRssNetworkFactory::setArticleLabel(const QStringList& article_id headers << QPair(HTTP_HEADERS_CONTENT_TYPE, TTRSS_CONTENT_TYPE_JSON); headers << NetworkFactory::generateBasicAuthHeader(m_authUsername, m_authPassword); - NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, - timeout, - QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), - result_raw, - QNetworkAccessManager::Operation::PostOperation, - headers, - false, - {}, - {}, - proxy); + NetworkResult network_reply = + NetworkFactory::performNetworkOperation(m_fullUrl, + timeout, + QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), + result_raw, + QNetworkAccessManager::Operation::PostOperation, + headers, + false, + {}, + {}, + proxy); TtRssResponse result(QString::fromUtf8(result_raw)); if (result.isNotLoggedIn()) { // We are not logged in. login(proxy); json[QSL("sid")] = m_sessionId; - network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, - timeout, - QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), - result_raw, - QNetworkAccessManager::Operation::PostOperation, - headers, - false, - {}, - {}, - proxy); + network_reply = + NetworkFactory::performNetworkOperation(m_fullUrl, + timeout, + QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), + result_raw, + QNetworkAccessManager::Operation::PostOperation, + headers, + false, + {}, + {}, + proxy); result = TtRssResponse(QString::fromUtf8(result_raw)); } if (network_reply.m_networkError != QNetworkReply::NoError) { - qWarningNN << LOGSEC_TTRSS - << "setArticleLabel failed with error" + qWarningNN << LOGSEC_TTRSS << "setArticleLabel failed with error" << QUOTE_W_SPACE_DOT(network_reply.m_networkError); } @@ -453,48 +574,50 @@ TtRssUpdateArticleResponse TtRssNetworkFactory::updateArticles(const QStringList headers << QPair(HTTP_HEADERS_CONTENT_TYPE, TTRSS_CONTENT_TYPE_JSON); headers << NetworkFactory::generateBasicAuthHeader(m_authUsername, m_authPassword); - NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, - timeout, - QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), - result_raw, - QNetworkAccessManager::Operation::PostOperation, - headers, - false, - {}, - {}, - proxy); + NetworkResult network_reply = + NetworkFactory::performNetworkOperation(m_fullUrl, + timeout, + QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), + result_raw, + QNetworkAccessManager::Operation::PostOperation, + headers, + false, + {}, + {}, + proxy); TtRssUpdateArticleResponse result(QString::fromUtf8(result_raw)); if (result.isNotLoggedIn()) { // We are not logged in. login(proxy); json[QSL("sid")] = m_sessionId; - network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, - timeout, - QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), - result_raw, - QNetworkAccessManager::Operation::PostOperation, - headers, - false, - {}, - {}, - proxy); + network_reply = + NetworkFactory::performNetworkOperation(m_fullUrl, + timeout, + QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), + result_raw, + QNetworkAccessManager::Operation::PostOperation, + headers, + false, + {}, + {}, + proxy); result = TtRssUpdateArticleResponse(QString::fromUtf8(result_raw)); } if (network_reply.m_networkError != QNetworkReply::NoError) { - qWarningNN << LOGSEC_TTRSS - << "updateArticle failed with error" - << QUOTE_W_SPACE_DOT(network_reply.m_networkError); + qWarningNN << LOGSEC_TTRSS << "updateArticle failed with error" << QUOTE_W_SPACE_DOT(network_reply.m_networkError); } m_lastError = network_reply.m_networkError; return result; } -TtRssSubscribeToFeedResponse TtRssNetworkFactory::subscribeToFeed(const QString& url, int category_id, +TtRssSubscribeToFeedResponse TtRssNetworkFactory::subscribeToFeed(const QString& url, + int category_id, const QNetworkProxy& proxy, - bool protectd, const QString& username, + bool protectd, + const QString& username, const QString& password) { QJsonObject json; @@ -515,39 +638,39 @@ TtRssSubscribeToFeedResponse TtRssNetworkFactory::subscribeToFeed(const QString& headers << QPair(HTTP_HEADERS_CONTENT_TYPE, TTRSS_CONTENT_TYPE_JSON); headers << NetworkFactory::generateBasicAuthHeader(m_authUsername, m_authPassword); - NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, - timeout, - QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), - result_raw, - QNetworkAccessManager::Operation::PostOperation, - headers, - false, - {}, - {}, - proxy); + NetworkResult network_reply = + NetworkFactory::performNetworkOperation(m_fullUrl, + timeout, + QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), + result_raw, + QNetworkAccessManager::Operation::PostOperation, + headers, + false, + {}, + {}, + proxy); TtRssSubscribeToFeedResponse result(QString::fromUtf8(result_raw)); if (result.isNotLoggedIn()) { // We are not logged in. login(proxy); json[QSL("sid")] = m_sessionId; - network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, - timeout, - QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), - result_raw, - QNetworkAccessManager::Operation::PostOperation, - headers, - false, - {}, - {}, - proxy); + network_reply = + NetworkFactory::performNetworkOperation(m_fullUrl, + timeout, + QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), + result_raw, + QNetworkAccessManager::Operation::PostOperation, + headers, + false, + {}, + {}, + proxy); result = TtRssSubscribeToFeedResponse(QString::fromUtf8(result_raw)); } if (network_reply.m_networkError != QNetworkReply::NoError) { - qWarningNN << LOGSEC_TTRSS - << "updateArticle failed with error" - << QUOTE_W_SPACE_DOT(network_reply.m_networkError); + qWarningNN << LOGSEC_TTRSS << "updateArticle failed with error" << QUOTE_W_SPACE_DOT(network_reply.m_networkError); } m_lastError = network_reply.m_networkError; @@ -567,39 +690,39 @@ TtRssUnsubscribeFeedResponse TtRssNetworkFactory::unsubscribeFeed(int feed_id, c headers << QPair(HTTP_HEADERS_CONTENT_TYPE, TTRSS_CONTENT_TYPE_JSON); headers << NetworkFactory::generateBasicAuthHeader(m_authUsername, m_authPassword); - NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, - timeout, - QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), - result_raw, - QNetworkAccessManager::Operation::PostOperation, - headers, - false, - {}, - {}, - proxy); + NetworkResult network_reply = + NetworkFactory::performNetworkOperation(m_fullUrl, + timeout, + QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), + result_raw, + QNetworkAccessManager::Operation::PostOperation, + headers, + false, + {}, + {}, + proxy); TtRssUnsubscribeFeedResponse result(QString::fromUtf8(result_raw)); if (result.isNotLoggedIn()) { // We are not logged in. login(proxy); json[QSL("sid")] = m_sessionId; - network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, - timeout, - QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), - result_raw, - QNetworkAccessManager::Operation::PostOperation, - headers, - false, - {}, - {}, - proxy); + network_reply = + NetworkFactory::performNetworkOperation(m_fullUrl, + timeout, + QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact), + result_raw, + QNetworkAccessManager::Operation::PostOperation, + headers, + false, + {}, + {}, + proxy); result = TtRssUnsubscribeFeedResponse(QString::fromUtf8(result_raw)); } if (network_reply.m_networkError != QNetworkReply::NoError) { - qWarningNN << LOGSEC_TTRSS - << "getFeeds failed with error" - << QUOTE_W_SPACE_DOT(network_reply.m_networkError); + qWarningNN << LOGSEC_TTRSS << "getFeeds failed with error" << QUOTE_W_SPACE_DOT(network_reply.m_networkError); } m_lastError = network_reply.m_networkError; @@ -614,6 +737,14 @@ void TtRssNetworkFactory::setBatchSize(int batch_size) { m_batchSize = batch_size; } +bool TtRssNetworkFactory::intelligentSynchronization() const { + return m_intelligentSynchronization; +} + +void TtRssNetworkFactory::setIntelligentSynchronization(bool intelligent_synchronization) { + m_intelligentSynchronization = intelligent_synchronization; +} + bool TtRssNetworkFactory::downloadOnlyUnreadMessages() const { return m_downloadOnlyUnreadMessages; } @@ -729,7 +860,8 @@ bool TtRssResponse::hasError() const { } } -TtRssGetFeedsCategoriesResponse::TtRssGetFeedsCategoriesResponse(const QString& raw_content) : TtRssResponse(raw_content) {} +TtRssGetFeedsCategoriesResponse::TtRssGetFeedsCategoriesResponse(const QString& raw_content) + : TtRssResponse(raw_content) {} TtRssGetFeedsCategoriesResponse::~TtRssGetFeedsCategoriesResponse() = default; @@ -740,14 +872,14 @@ RootItem* TtRssGetFeedsCategoriesResponse::feedsCategories(TtRssNetworkFactory* auto* parent = new RootItem(); // Chop the "api/" from the end of the address. - qDebugNN << LOGSEC_TTRSS - << "Base address to get feed icons is" - << QUOTE_W_SPACE_DOT(base_address); + qDebugNN << LOGSEC_TTRSS << "Base address to get feed icons is" << QUOTE_W_SPACE_DOT(base_address); if (status() == TTRSS_API_STATUS_OK) { // We have data, construct object tree according to data. - QJsonArray items_to_process = m_rawContent[QSL("content")].toObject()[QSL("categories")].toObject()[QSL("items")].toArray(); - QVector> pairs; pairs.reserve(items_to_process.size()); + QJsonArray items_to_process = + m_rawContent[QSL("content")].toObject()[QSL("categories")].toObject()[QSL("items")].toArray(); + QVector> pairs; + pairs.reserve(items_to_process.size()); for (const QJsonValue& item : items_to_process) { pairs.append(QPair(parent, item)); @@ -793,7 +925,8 @@ RootItem* TtRssGetFeedsCategoriesResponse::feedsCategories(TtRssNetworkFactory* auto* feed = new TtRssFeed(); if (obtain_icons) { - QString icon_path = item[QSL("icon")].type() == QJsonValue::String ? item[QSL("icon")].toString() : QString(); + QString icon_path = + item[QSL("icon")].type() == QJsonValue::String ? item[QSL("icon")].toString() : QString(); if (!icon_path.isEmpty()) { // Chop the "api/" suffix out and append @@ -802,23 +935,17 @@ RootItem* TtRssGetFeedsCategoriesResponse::feedsCategories(TtRssNetworkFactory* QList> headers; if (network->authIsUsed()) { - headers << NetworkFactory::generateBasicAuthHeader(network->authUsername(), - network->authPassword()); + headers << NetworkFactory::generateBasicAuthHeader(network->authUsername(), network->authPassword()); } - auto res = NetworkFactory::downloadIcon({ { full_icon_address, true } }, - DOWNLOAD_TIMEOUT, - icon, - headers, - proxy); + auto res = + NetworkFactory::downloadIcon({{full_icon_address, true}}, DOWNLOAD_TIMEOUT, icon, headers, proxy); if (res == QNetworkReply::NoError) { feed->setIcon(icon); } else { - qWarningNN << LOGSEC_TTRSS - << "Failed to download icon with error" - << QUOTE_W_SPACE_DOT(res); + qWarningNN << LOGSEC_TTRSS << "Failed to download icon with error" << QUOTE_W_SPACE_DOT(res); } } } @@ -851,6 +978,14 @@ TtRssGetHeadlinesResponse::TtRssGetHeadlinesResponse(const QString& raw_content) TtRssGetHeadlinesResponse::~TtRssGetHeadlinesResponse() = default; +TtRssGetArticleResponse::TtRssGetArticleResponse(const QString& raw_content) : TtRssResponse(raw_content) {} + +QList TtRssGetArticleResponse::messages(ServiceRoot* root) const { + return {}; +} + +TtRssGetArticleResponse::~TtRssGetArticleResponse() = default; + QList TtRssGetHeadlinesResponse::messages(ServiceRoot* root) const { QList messages; auto active_labels = root->labelsNode() != nullptr ? root->labelsNode()->labels() : QList(); @@ -878,9 +1013,10 @@ QList TtRssGetHeadlinesResponse::messages(ServiceRoot* root) const { for (const QJsonValue& lbl_val : qAsConst(json_labels)) { QString lbl_custom_id = QString::number(lbl_val.toArray().at(0).toInt()); - Label* label = boolinq::from(active_labels.begin(), active_labels.end()).firstOrDefault([lbl_custom_id](Label* lbl) { - return lbl->customId() == lbl_custom_id; - }); + Label* label = + boolinq::from(active_labels.begin(), active_labels.end()).firstOrDefault([lbl_custom_id](Label* lbl) { + return lbl->customId() == lbl_custom_id; + }); if (label != nullptr) { message.m_assignedLabels.append(label); @@ -1006,3 +1142,19 @@ QList TtRssGetLabelsResponse::labels() const { return labels; } + +TtRssGetCompactHeadlinesResponse::TtRssGetCompactHeadlinesResponse(const QString& raw_content) + : TtRssResponse(raw_content) {} + +TtRssGetCompactHeadlinesResponse::~TtRssGetCompactHeadlinesResponse() = default; + +QStringList TtRssGetCompactHeadlinesResponse::ids() const { + auto json_ids = m_rawContent[QSL("content")].toArray(); + QStringList msg_ids; + + for (const QJsonValue& id_val : qAsConst(json_ids)) { + msg_ids.append(QString::number(id_val.toObject()[QSL("id")].toInt())); + } + + return msg_ids; +} diff --git a/src/librssguard/services/tt-rss/ttrssnetworkfactory.h b/src/librssguard/services/tt-rss/ttrssnetworkfactory.h index 720580b9e..e6b9cc47d 100644 --- a/src/librssguard/services/tt-rss/ttrssnetworkfactory.h +++ b/src/librssguard/services/tt-rss/ttrssnetworkfactory.h @@ -60,8 +60,10 @@ class TtRssGetFeedsCategoriesResponse : public TtRssResponse { // Returns tree of feeds/categories. // Top-level root of the tree is not needed here. // Returned items do not have primary IDs assigned. - RootItem* feedsCategories(TtRssNetworkFactory* network, bool obtain_icons, - const QNetworkProxy& proxy, const QString& base_address = QString()) const; + RootItem* feedsCategories(TtRssNetworkFactory* network, + bool obtain_icons, + const QNetworkProxy& proxy, + const QString& base_address = QString()) const; }; class ServiceRoot; @@ -74,6 +76,22 @@ class TtRssGetHeadlinesResponse : public TtRssResponse { QList messages(ServiceRoot* root) const; }; +class TtRssGetArticleResponse : public TtRssResponse { + public: + explicit TtRssGetArticleResponse(const QString& raw_content = QString()); + virtual ~TtRssGetArticleResponse(); + + QList messages(ServiceRoot* root) const; +}; + +class TtRssGetCompactHeadlinesResponse : public TtRssResponse { + public: + explicit TtRssGetCompactHeadlinesResponse(const QString& raw_content = QString()); + virtual ~TtRssGetCompactHeadlinesResponse(); + + QStringList ids() const; +}; + class TtRssUpdateArticleResponse : public TtRssResponse { public: explicit TtRssUpdateArticleResponse(const QString& raw_content = QString()); @@ -100,19 +118,11 @@ class TtRssUnsubscribeFeedResponse : public TtRssResponse { }; namespace UpdateArticle { - enum class Mode { - SetToFalse = 0, - SetToTrue = 1, - Togggle = 2 - }; + enum class Mode { SetToFalse = 0, SetToTrue = 1, Togggle = 2 }; - enum class OperatingField { - Starred = 0, - Published = 1, - Unread = 2 - }; + enum class OperatingField { Starred = 0, Published = 1, Unread = 2 }; -} +} // namespace UpdateArticle class TtRssNetworkFactory { public: @@ -163,22 +173,40 @@ class TtRssNetworkFactory { // Gets feeds from the server. TtRssGetFeedsCategoriesResponse getFeedsCategories(const QNetworkProxy& proxy); + // Gets message IDs from the server. + TtRssGetCompactHeadlinesResponse getCompactHeadlines(int feed_id, + int limit, + int skip, + const QString& view_mode, + const QNetworkProxy& proxy); + + TtRssGetHeadlinesResponse getArticle(const QStringList& article_ids, const QNetworkProxy& proxy); + // Gets headlines (messages) from the server. - TtRssGetHeadlinesResponse getHeadlines(int feed_id, int limit, int skip, - bool show_content, bool include_attachments, - bool sanitize, bool unread_only, + TtRssGetHeadlinesResponse getHeadlines(int feed_id, + int limit, + int skip, + bool show_content, + bool include_attachments, + bool sanitize, + bool unread_only, const QNetworkProxy& proxy); - TtRssResponse setArticleLabel(const QStringList& article_ids, const QString& label_custom_id, - bool assign, const QNetworkProxy& proxy); + TtRssResponse setArticleLabel(const QStringList& article_ids, + const QString& label_custom_id, + bool assign, + const QNetworkProxy& proxy); TtRssUpdateArticleResponse updateArticles(const QStringList& ids, UpdateArticle::OperatingField field, UpdateArticle::Mode mode, const QNetworkProxy& proxy); - TtRssSubscribeToFeedResponse subscribeToFeed(const QString& url, int category_id, const QNetworkProxy& proxy, - bool protectd = false, const QString& username = QString(), + TtRssSubscribeToFeedResponse subscribeToFeed(const QString& url, + int category_id, + const QNetworkProxy& proxy, + bool protectd = false, + const QString& username = QString(), const QString& password = QString()); TtRssUnsubscribeFeedResponse unsubscribeFeed(int feed_id, const QNetworkProxy& proxy); @@ -186,6 +214,9 @@ class TtRssNetworkFactory { int batchSize() const; void setBatchSize(int batch_size); + bool intelligentSynchronization() const; + void setIntelligentSynchronization(bool intelligent_synchronization); + private: QString m_bareUrl; QString m_fullUrl; @@ -194,6 +225,7 @@ class TtRssNetworkFactory { int m_batchSize; bool m_forceServerSideUpdate; bool m_downloadOnlyUnreadMessages; + bool m_intelligentSynchronization; bool m_authIsUsed; QString m_authUsername; QString m_authPassword; diff --git a/src/librssguard/services/tt-rss/ttrssserviceroot.cpp b/src/librssguard/services/tt-rss/ttrssserviceroot.cpp index 3984e69c2..2753b4872 100644 --- a/src/librssguard/services/tt-rss/ttrssserviceroot.cpp +++ b/src/librssguard/services/tt-rss/ttrssserviceroot.cpp @@ -235,6 +235,7 @@ QVariantHash TtRssServiceRoot::customDatabaseData() const { data[QSL("force_update")] = m_network->forceServerSideUpdate(); data[QSL("batch_size")] = m_network->batchSize(); data[QSL("download_only_unread")] = m_network->downloadOnlyUnreadMessages(); + data[QSL("intelligent_synchronization")] = m_network->intelligentSynchronization(); return data; } @@ -249,15 +250,87 @@ void TtRssServiceRoot::setCustomDatabaseData(const QVariantHash& data) { m_network->setForceServerSideUpdate(data[QSL("force_update")].toBool()); m_network->setBatchSize(data[QSL("batch_size")].toInt()); m_network->setDownloadOnlyUnreadMessages(data[QSL("download_only_unread")].toBool()); + m_network->setIntelligentSynchronization(data[QSL("intelligent_synchronization")].toBool()); } QList TtRssServiceRoot::obtainNewMessages(Feed* feed, const QHash& stated_messages, const QHash& tagged_messages) { - Q_UNUSED(stated_messages) Q_UNUSED(tagged_messages) + if (m_network->intelligentSynchronization()) { + return obtainMessagesIntelligently(feed, stated_messages); + } + else { + return obtainMessagesViaHeadlines(feed); + } +} + +QList TtRssServiceRoot::obtainMessagesIntelligently(Feed* feed, + const QHash& stated_messages) { + // 1. Get unread IDs for a feed. + // 2. Get read IDs for a feed. + // 3. Get starred IDs for a feed. + // 4. Determine IDs needed to download. + // 5. Download needed articles. + const QStringList remote_all_ids_list = + m_network->downloadOnlyUnreadMessages() + ? QStringList() + : m_network->getCompactHeadlines(feed->customNumericId(), 1000000, 0, QSL("all_articles"), networkProxy()).ids(); + const QStringList remote_unread_ids_list = + m_network->getCompactHeadlines(feed->customNumericId(), 1000000, 0, QSL("unread"), networkProxy()).ids(); + const QStringList remote_starred_ids_list = + m_network->getCompactHeadlines(feed->customNumericId(), 1000000, 0, QSL("marked"), networkProxy()).ids(); + + const QSet remote_all_ids = FROM_LIST_TO_SET(QSet, remote_all_ids_list); + + // 1. + auto local_unread_ids_list = stated_messages.value(ServiceRoot::BagOfMessages::Unread); + const QSet remote_unread_ids = FROM_LIST_TO_SET(QSet, remote_unread_ids_list); + const QSet local_unread_ids = FROM_LIST_TO_SET(QSet, local_unread_ids_list); + + // 2. + const auto local_read_ids_list = stated_messages.value(ServiceRoot::BagOfMessages::Read); + const QSet remote_read_ids = remote_all_ids - remote_unread_ids; + const QSet local_read_ids = FROM_LIST_TO_SET(QSet, local_read_ids_list); + + // 3. + const auto local_starred_ids_list = stated_messages.value(ServiceRoot::BagOfMessages::Starred); + const QSet remote_starred_ids = FROM_LIST_TO_SET(QSet, remote_starred_ids_list); + const QSet local_starred_ids = FROM_LIST_TO_SET(QSet, local_starred_ids_list); + + // 4. + QSet to_download; + + if (!m_network->downloadOnlyUnreadMessages()) { + to_download += remote_all_ids - local_read_ids - local_unread_ids; + } + else { + to_download += remote_unread_ids - local_read_ids - local_unread_ids; + } + + auto moved_read = local_read_ids & remote_unread_ids; + + to_download += moved_read; + + if (!m_network->downloadOnlyUnreadMessages()) { + auto moved_unread = local_unread_ids & remote_read_ids; + + to_download += moved_unread; + } + + auto moved_starred = (local_starred_ids + remote_starred_ids) - (local_starred_ids & remote_starred_ids); + + to_download += moved_starred; + + // 5. + auto msgs = m_network->getArticle(to_download.values(), networkProxy()); + + return msgs.messages(this); +} + +QList TtRssServiceRoot::obtainMessagesViaHeadlines(Feed* feed) { QList messages; int newly_added_messages = 0; int limit = network()->batchSize() <= 0 ? TTRSS_MAX_MESSAGES : network()->batchSize(); @@ -336,3 +409,7 @@ RootItem* TtRssServiceRoot::obtainNewTreeForSyncIn() const { throw NetworkException(lst_error, tr("cannot get list of feeds, network error '%1'").arg(lst_error)); } } + +bool TtRssServiceRoot::wantsBaggedIdsOfExistingMessages() const { + return m_network->intelligentSynchronization(); +} diff --git a/src/librssguard/services/tt-rss/ttrssserviceroot.h b/src/librssguard/services/tt-rss/ttrssserviceroot.h index 239fbe13c..bf8171370 100644 --- a/src/librssguard/services/tt-rss/ttrssserviceroot.h +++ b/src/librssguard/services/tt-rss/ttrssserviceroot.h @@ -13,12 +13,13 @@ class TtRssFeed; class TtRssNetworkFactory; class TtRssServiceRoot : public ServiceRoot, public CacheForServiceRoot { - Q_OBJECT + Q_OBJECT public: explicit TtRssServiceRoot(RootItem* parent = nullptr); virtual ~TtRssServiceRoot(); + virtual bool wantsBaggedIdsOfExistingMessages() const; virtual LabelOperation supportedLabelOperations() const; virtual void start(bool freshly_activated); virtual void stop(); @@ -48,6 +49,9 @@ class TtRssServiceRoot : public ServiceRoot, public CacheForServiceRoot { private: void updateTitle(); + QList obtainMessagesIntelligently(Feed* feed, + const QHash& stated_messages); + QList obtainMessagesViaHeadlines(Feed* feed); private: TtRssNetworkFactory* m_network;