diff --git a/resources/desktop/com.github.rssguard.appdata.xml b/resources/desktop/com.github.rssguard.appdata.xml index 98487b569..5d9ee2735 100644 --- a/resources/desktop/com.github.rssguard.appdata.xml +++ b/resources/desktop/com.github.rssguard.appdata.xml @@ -30,7 +30,7 @@ https://martinrotter.github.io/donate/ - + none diff --git a/src/librssguard/services/feedly/definitions.h b/src/librssguard/services/feedly/definitions.h index 2fdfeb214..5e1cd9eac 100755 --- a/src/librssguard/services/feedly/definitions.h +++ b/src/librssguard/services/feedly/definitions.h @@ -4,6 +4,7 @@ #define FEEDLY_UNLIMITED_BATCH_SIZE -1 #define FEEDLY_DEFAULT_BATCH_SIZE 20 #define FEEDLY_MAX_BATCH_SIZE 500 +#define FEEDLX_MAX_TOTAL_SIZE 5000 #define FEEDLY_GENERATE_DAT "https://feedly.com/v3/auth/dev" @@ -16,6 +17,9 @@ #define FEEDLY_API_URL_BASE "https://cloud.feedly.com/v3/" #endif +#define FEEDLY_API_SYSTEM_TAG_READ "global.read" +#define FEEDLY_API_SYSTEM_TAG_SAVED "global.saved" + #define FEEDLY_API_URL_AUTH "auth/auth" #define FEEDLY_API_URL_TOKEN "auth/token" #define FEEDLY_API_URL_PROFILE "profile" diff --git a/src/librssguard/services/feedly/feedlyfeed.cpp b/src/librssguard/services/feedly/feedlyfeed.cpp index 72efda71f..2b0e9cbab 100755 --- a/src/librssguard/services/feedly/feedlyfeed.cpp +++ b/src/librssguard/services/feedly/feedlyfeed.cpp @@ -2,6 +2,7 @@ #include "services/feedly/feedlyfeed.h" +#include "exceptions/applicationexception.h" #include "miscellaneous/application.h" #include "miscellaneous/iconfactory.h" #include "services/feedly/feedlynetwork.h" @@ -16,14 +17,23 @@ FeedlyServiceRoot* FeedlyFeed::serviceRoot() const { } QList FeedlyFeed::obtainNewMessages(bool* error_during_obtaining) { - Feed::Status error = Feed::Status::Normal; - QList messages = serviceRoot()->network()->streamContents(customId()); + try { + QList messages = serviceRoot()->network()->streamContents(customId()); - setStatus(error); - - if (error == Feed::Status::NetworkError || error == Feed::Status::AuthError) { - *error_during_obtaining = true; + setStatus(Feed::Status::Normal); + *error_during_obtaining = false; + return messages;; } + catch (const ApplicationException& ex) { + setStatus(Feed::Status::NetworkError); + *error_during_obtaining = true; - return messages; + qCriticalNN << LOGSEC_FEEDLY + << "Problem" + << QUOTE_W_SPACE(ex.message()) + << "when obtaining messages for feed" + << QUOTE_W_SPACE_DOT(customId()); + + return {}; + } } diff --git a/src/librssguard/services/feedly/feedlynetwork.cpp b/src/librssguard/services/feedly/feedlynetwork.cpp index 65aec3a1e..325d7aec6 100755 --- a/src/librssguard/services/feedly/feedlynetwork.cpp +++ b/src/librssguard/services/feedly/feedlynetwork.cpp @@ -2,6 +2,7 @@ #include "services/feedly/feedlynetwork.h" +#include "3rd-party/boolinq/boolinq.h" #include "3rd-party/boolinq/boolinq.h" #include "exceptions/networkexception.h" #include "miscellaneous/application.h" @@ -68,8 +69,14 @@ QList FeedlyNetwork::streamContents(const QString& stream_id) { if (!continuation.isEmpty()) { target_url += QSL("&continuation=%1").arg(continuation); } - else if (m_batchSize > 0) { - target_url += QSL("&count=%2").arg(QString::number(m_batchSize)); + + if (m_batchSize > 0) { + target_url += QSL("&count=%1").arg(QString::number(m_batchSize)); + } + else { + // User wants to download all messages. Make sure we use large batches + // to limit network requests. + target_url += QSL("&count=%1").arg(QString::number(FEEDLY_MAX_BATCH_SIZE)); } auto result = NetworkFactory::performNetworkOperation(target_url, @@ -83,10 +90,15 @@ QList FeedlyNetwork::streamContents(const QString& stream_id) { {}, m_service->networkProxy()); - messages += decodeStreamContents(output, continuation); + if (result.first != QNetworkReply::NetworkError::NoError) { + throw NetworkException(result.first); + } + messages += decodeStreamContents(output, continuation); } - while (!continuation.isEmpty()); + while (!continuation.isEmpty() && + (m_batchSize <= 0 || messages.size() < m_batchSize) && + messages.size() <= FEEDLX_MAX_TOTAL_SIZE); return messages; } @@ -94,6 +106,7 @@ QList FeedlyNetwork::streamContents(const QString& stream_id) { QList FeedlyNetwork::decodeStreamContents(const QByteArray& stream_contents, QString& continuation) const { QList messages; QJsonDocument json = QJsonDocument::fromJson(stream_contents); + auto active_labels = m_service->labelsNode() != nullptr ? m_service->labelsNode()->labels() : QList(); continuation = json.object()["continuation"].toString(); @@ -104,10 +117,47 @@ QList FeedlyNetwork::decodeStreamContents(const QByteArray& stream_cont message.m_title = entry_obj["title"].toString(); message.m_author = entry_obj["author"].toString(); message.m_contents = entry_obj["content"].toObject()["content"].toString(); + + if (message.m_contents.isEmpty()) { + message.m_contents = entry_obj["summary"].toObject()["content"].toString(); + } + message.m_createdFromFeed = true; message.m_created = QDateTime::fromMSecsSinceEpoch(entry_obj["published"].toVariant().toLongLong(), Qt::TimeSpec::UTC); message.m_customId = entry_obj["id"].toString(); + message.m_isRead = !entry_obj["unread"].toBool(); + message.m_url = entry_obj["canonicalUrl"].toString(); + + if (message.m_url.isEmpty()) { + message.m_url = entry_obj["canonical"].toObject()["href"].toString(); + } + + for (const QJsonValue& tag : entry_obj["tags"].toArray()) { + const QJsonObject& tag_obj = tag.toObject(); + const QString& tag_id = tag_obj["id"].toString(); + + if (tag_id.endsWith(FEEDLY_API_SYSTEM_TAG_SAVED)) { + message.m_isImportant = true; + } + else if (tag_id.endsWith(FEEDLY_API_SYSTEM_TAG_READ)) { + // NOTE: We don't do anything with "global read" tag. + } + else { + Label* label = boolinq::from(active_labels.begin(), active_labels.end()).firstOrDefault([tag_id](Label* lbl) { + return lbl->customId() == tag_id; + }); + + if (label != nullptr) { + message.m_assignedLabels.append(label); + } + else { + qCriticalNN << LOGSEC_FEEDLY + << "Failed to find live Label object for tag" + << QUOTE_W_SPACE_DOT(tag_id); + } + } + } messages.append(message); } @@ -267,6 +317,12 @@ QList FeedlyNetwork::tags() { for (const QJsonValue& tag : json.array()) { const QJsonObject& tag_obj = tag.toObject(); QString name_id = tag_obj["id"].toString(); + + if (name_id.endsWith(FEEDLY_API_SYSTEM_TAG_READ) || + name_id.endsWith(FEEDLY_API_SYSTEM_TAG_SAVED)) { + continue; + } + QString plain_name = tag_obj["label"].toString(); auto* new_lbl = new Label(plain_name, TextFactory::generateColorFromText(name_id)); diff --git a/src/librssguard/services/feedly/feedlynetwork.h b/src/librssguard/services/feedly/feedlynetwork.h index 93d73e28a..6dc544be0 100755 --- a/src/librssguard/services/feedly/feedlynetwork.h +++ b/src/librssguard/services/feedly/feedlynetwork.h @@ -74,7 +74,11 @@ class FeedlyNetwork : public QObject { QString m_username; QString m_developerAccessToken; + + // Only download N newest messages per feed. int m_batchSize; + + // Only download unread messages. bool m_downloadOnlyUnreadMessages; }; diff --git a/src/librssguard/services/feedly/gui/feedlyaccountdetails.cpp b/src/librssguard/services/feedly/gui/feedlyaccountdetails.cpp index a5d6c51f9..b63159afd 100755 --- a/src/librssguard/services/feedly/gui/feedlyaccountdetails.cpp +++ b/src/librssguard/services/feedly/gui/feedlyaccountdetails.cpp @@ -47,6 +47,10 @@ FeedlyAccountDetails::FeedlyAccountDetails(QWidget* parent) : QWidget(parent) { "each day.").arg(APP_NAME)); #endif + m_ui.m_lblLimitMessagesInfo->setText(tr("Be very careful about downloading too many messages, because " + "Feedly automagically caches ALL messages of a feed forever so you might " + "end with thousands of messages you will never read anyway.")); + connect(m_ui.m_spinLimitMessages, static_cast(&QSpinBox::valueChanged), this, [=](int value) { if (value <= 0) { m_ui.m_spinLimitMessages->setSuffix(QSL(" ") + tr("= unlimited")); @@ -57,6 +61,7 @@ FeedlyAccountDetails::FeedlyAccountDetails(QWidget* parent) : QWidget(parent) { }); GuiUtilities::setLabelAsNotice(*m_ui.m_lblInfo, true); + GuiUtilities::setLabelAsNotice(*m_ui.m_lblLimitMessagesInfo, true); connect(m_ui.m_btnGetToken, &QPushButton::clicked, this, &FeedlyAccountDetails::getDeveloperAccessToken); connect(m_ui.m_txtUsername->lineEdit(), &BaseLineEdit::textChanged, this, &FeedlyAccountDetails::onUsernameChanged); diff --git a/src/librssguard/services/feedly/gui/feedlyaccountdetails.ui b/src/librssguard/services/feedly/gui/feedlyaccountdetails.ui index 9429ec630..36d7022a2 100755 --- a/src/librssguard/services/feedly/gui/feedlyaccountdetails.ui +++ b/src/librssguard/services/feedly/gui/feedlyaccountdetails.ui @@ -79,7 +79,7 @@ - + Qt::Vertical @@ -92,7 +92,7 @@ - + @@ -117,6 +117,19 @@ + + + + TextLabel + + + Qt::AlignCenter + + + true + + +