This commit is contained in:
Martin Rotter 2021-02-17 15:13:46 +01:00
parent b19450932a
commit 6faeba1185
7 changed files with 106 additions and 14 deletions

View File

@ -30,7 +30,7 @@
<url type="donation">https://martinrotter.github.io/donate/</url>
<content_rating type="oars-1.1" />
<releases>
<release version="3.8.4" date="2021-02-16"/>
<release version="3.8.4" date="2021-02-17"/>
</releases>
<content_rating type="oars-1.0">
<content_attribute id="violence-cartoon">none</content_attribute>

View File

@ -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"

View File

@ -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<Message> FeedlyFeed::obtainNewMessages(bool* error_during_obtaining) {
Feed::Status error = Feed::Status::Normal;
QList<Message> messages = serviceRoot()->network()->streamContents(customId());
try {
QList<Message> 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 {};
}
}

View File

@ -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<Message> 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<Message> 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<Message> FeedlyNetwork::streamContents(const QString& stream_id) {
QList<Message> FeedlyNetwork::decodeStreamContents(const QByteArray& stream_contents, QString& continuation) const {
QList<Message> messages;
QJsonDocument json = QJsonDocument::fromJson(stream_contents);
auto active_labels = m_service->labelsNode() != nullptr ? m_service->labelsNode()->labels() : QList<Label*>();
continuation = json.object()["continuation"].toString();
@ -104,10 +117,47 @@ QList<Message> 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<RootItem*> 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));

View File

@ -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;
};

View File

@ -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<void (QSpinBox::*)(int)>(&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);

View File

@ -79,7 +79,7 @@
</item>
</layout>
</item>
<item row="6" column="0" colspan="2">
<item row="7" column="0" colspan="2">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -92,7 +92,7 @@
</property>
</spacer>
</item>
<item row="5" column="0" colspan="2">
<item row="6" column="0" colspan="2">
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QPushButton" name="m_btnTestSetup">
@ -117,6 +117,19 @@
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<widget class="QLabel" name="m_lblLimitMessagesInfo">
<property name="text">
<string>TextLabel</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>