remove inoreader plugin as it is now fully replaced by greader plugin

This commit is contained in:
Martin Rotter 2021-08-06 13:03:40 +02:00
parent b068fafe42
commit eaffa942f8
15 changed files with 0 additions and 1447 deletions

View File

@ -183,12 +183,6 @@ HEADERS += core/feeddownloader.h \
services/greader/greaderserviceroot.h \
services/greader/gui/formeditgreaderaccount.h \
services/greader/gui/greaderaccountdetails.h \
services/inoreader/definitions.h \
services/inoreader/gui/formeditinoreaderaccount.h \
services/inoreader/gui/inoreaderaccountdetails.h \
services/inoreader/inoreaderentrypoint.h \
services/inoreader/inoreadernetworkfactory.h \
services/inoreader/inoreaderserviceroot.h \
services/owncloud/definitions.h \
services/owncloud/gui/formeditowncloudaccount.h \
services/owncloud/gui/owncloudaccountdetails.h \
@ -364,11 +358,6 @@ SOURCES += core/feeddownloader.cpp \
services/greader/greaderserviceroot.cpp \
services/greader/gui/formeditgreaderaccount.cpp \
services/greader/gui/greaderaccountdetails.cpp \
services/inoreader/gui/formeditinoreaderaccount.cpp \
services/inoreader/gui/inoreaderaccountdetails.cpp \
services/inoreader/inoreaderentrypoint.cpp \
services/inoreader/inoreadernetworkfactory.cpp \
services/inoreader/inoreaderserviceroot.cpp \
services/owncloud/gui/formeditowncloudaccount.cpp \
services/owncloud/gui/owncloudaccountdetails.cpp \
services/owncloud/owncloudfeed.cpp \
@ -438,7 +427,6 @@ FORMS += gui/dialogs/formabout.ui \
services/gmail/gui/formdownloadattachment.ui \
services/gmail/gui/gmailaccountdetails.ui \
services/greader/gui/greaderaccountdetails.ui \
services/inoreader/gui/inoreaderaccountdetails.ui \
services/owncloud/gui/owncloudaccountdetails.ui \
services/standard/gui/formstandardimportexport.ui \
services/standard/gui/standardfeeddetails.ui \

View File

@ -17,7 +17,6 @@
#include "services/feedly/feedlyentrypoint.h"
#include "services/gmail/gmailentrypoint.h"
#include "services/greader/greaderentrypoint.h"
#include "services/inoreader/inoreaderentrypoint.h"
#include "services/owncloud/owncloudserviceentrypoint.h"
#include "services/standard/standardserviceentrypoint.h"
#include "services/tt-rss/ttrssserviceentrypoint.h"
@ -61,7 +60,6 @@ QList<ServiceEntryPoint*> FeedReader::feedServices() {
m_feedServices.append(new FeedlyEntryPoint());
m_feedServices.append(new GmailEntryPoint());
m_feedServices.append(new GreaderEntryPoint());
m_feedServices.append(new InoreaderEntryPoint());
m_feedServices.append(new OwnCloudServiceEntryPoint());
m_feedServices.append(new StandardServiceEntryPoint());
m_feedServices.append(new TtRssServiceEntryPoint());

View File

@ -30,7 +30,6 @@
#include "network-web/networkfactory.h"
#include "network-web/oauthhttphandler.h"
#include "network-web/webfactory.h"
#include "services/inoreader/definitions.h"
#include <QDebug>
#include <QInputDialog>

View File

@ -1,34 +0,0 @@
// For license of this file, see <project-root-folder>/LICENSE.md.
#ifndef INOREADER_DEFINITIONS_H
#define INOREADER_DEFINITIONS_H
#define INOREADER_OAUTH_REDIRECT_URI_PORT 14488
#define INOREADER_OAUTH_SCOPE "read write"
#define INOREADER_OAUTH_TOKEN_URL "https://www.inoreader.com/oauth2/token"
#define INOREADER_OAUTH_AUTH_URL "https://www.inoreader.com/oauth2/auth"
#define INOREADER_REG_API_URL "https://www.inoreader.com/developers/register-app"
#define INOREADER_REFRESH_TOKEN_KEY "refresh_token"
#define INOREADER_ACCESS_TOKEN_KEY "access_token"
#define INOREADER_DEFAULT_BATCH_SIZE 100
#define INOREADER_MAX_BATCH_SIZE 999
#define INOREADER_MIN_BATCH_SIZE 20
#define INOREADER_API_EDIT_TAG_BATCH 50
#define INOREADER_STATE_READING_LIST "state/com.google/reading-list"
#define INOREADER_STATE_READ "state/com.google/read"
#define INOREADER_STATE_IMPORTANT "state/com.google/starred"
#define INOREADER_FULL_STATE_READING_LIST "user/-/state/com.google/reading-list"
#define INOREADER_FULL_STATE_READ "user/-/state/com.google/read"
#define INOREADER_FULL_STATE_IMPORTANT "user/-/state/com.google/starred"
#define INOREADER_API_FEED_CONTENTS "https://www.inoreader.com/reader/api/0/stream/contents"
#define INOREADER_API_LIST_LABELS "https://www.inoreader.com/reader/api/0/tag/list?types=1"
#define INOREADER_API_LIST_FEEDS "https://www.inoreader.com/reader/api/0/subscription/list"
#define INOREADER_API_EDIT_TAG "https://www.inoreader.com/reader/api/0/edit-tag"
#define INOREADER_API_USER_INFO "https://www.inoreader.com/reader/api/0/user-info"
#endif // INOREADER_DEFINITIONS_H

View File

@ -1,67 +0,0 @@
// For license of this file, see <project-root-folder>/LICENSE.md.
#include "services/inoreader/gui/formeditinoreaderaccount.h"
#include "gui/guiutilities.h"
#include "miscellaneous/application.h"
#include "miscellaneous/iconfactory.h"
#include "network-web/oauth2service.h"
#include "network-web/webfactory.h"
#include "services/inoreader/definitions.h"
#include "services/inoreader/gui/inoreaderaccountdetails.h"
#include "services/inoreader/inoreadernetworkfactory.h"
#include "services/inoreader/inoreaderserviceroot.h"
#include <QThread>
FormEditInoreaderAccount::FormEditInoreaderAccount(QWidget* parent)
: FormAccountDetails(qApp->icons()->miscIcon(QSL("inoreader")), parent), m_details(new InoreaderAccountDetails(this)) {
insertCustomTab(m_details, tr("Server setup"), 0);
activateTab(0);
connect(m_details->m_ui.m_btnTestSetup, &QPushButton::clicked, this, &FormEditInoreaderAccount::testSetup);
m_details->m_ui.m_txtUsername->setFocus();
}
void FormEditInoreaderAccount::apply() {
FormAccountDetails::apply();
account<InoreaderServiceRoot>()->network()->oauth()->logout(false);
account<InoreaderServiceRoot>()->network()->oauth()->setClientId(m_details->m_ui.m_txtAppId->lineEdit()->text());
account<InoreaderServiceRoot>()->network()->oauth()->setClientSecret(m_details->m_ui.m_txtAppKey->lineEdit()->text());
account<InoreaderServiceRoot>()->network()->oauth()->setRedirectUrl(m_details->m_ui.m_txtRedirectUrl->lineEdit()->text(),
true);
account<InoreaderServiceRoot>()->network()->setUsername(m_details->m_ui.m_txtUsername->lineEdit()->text());
account<InoreaderServiceRoot>()->network()->setBatchSize(m_details->m_ui.m_spinLimitMessages->value());
account<InoreaderServiceRoot>()->network()->setDownloadOnlyUnreadMessages(m_details->m_ui.m_cbDownloadOnlyUnreadMessages->isChecked());
account<InoreaderServiceRoot>()->saveAccountDataToDatabase();
accept();
if (!m_creatingNew) {
account<InoreaderServiceRoot>()->completelyRemoveAllData();
account<InoreaderServiceRoot>()->start(true);
}
}
void FormEditInoreaderAccount::loadAccountData() {
FormAccountDetails::loadAccountData();
m_details->m_oauth = account<InoreaderServiceRoot>()->network()->oauth();
m_details->hookNetwork();
// Setup the GUI.
m_details->m_ui.m_txtAppId->lineEdit()->setText(m_details->m_oauth->clientId());
m_details->m_ui.m_txtAppKey->lineEdit()->setText(m_details->m_oauth->clientSecret());
m_details->m_ui.m_txtRedirectUrl->lineEdit()->setText(m_details->m_oauth->redirectUrl());
m_details->m_ui.m_txtUsername->lineEdit()->setText(account<InoreaderServiceRoot>()->network()->username());
m_details->m_ui.m_spinLimitMessages->setValue(account<InoreaderServiceRoot>()->network()->batchSize());
m_details->m_ui.m_cbDownloadOnlyUnreadMessages->setChecked(account<InoreaderServiceRoot>()->network()->downloadOnlyUnreadMessages());
}
void FormEditInoreaderAccount::testSetup() {
m_details->testSetup(m_proxyDetails->proxy());
}

View File

@ -1,30 +0,0 @@
// For license of this file, see <project-root-folder>/LICENSE.md.
#ifndef FORMEDITINOREADERACCOUNT_H
#define FORMEDITINOREADERACCOUNT_H
#include "services/abstract/gui/formaccountdetails.h"
class InoreaderServiceRoot;
class InoreaderAccountDetails;
class FormEditInoreaderAccount : public FormAccountDetails {
Q_OBJECT
public:
explicit FormEditInoreaderAccount(QWidget* parent = nullptr);
protected slots:
virtual void apply();
protected:
virtual void loadAccountData();
private slots:
void testSetup();
private:
InoreaderAccountDetails* m_details;
};
#endif // FORMEDITINOREADERACCOUNT_H

View File

@ -1,139 +0,0 @@
// For license of this file, see <project-root-folder>/LICENSE.md.
#include "services/inoreader/gui/inoreaderaccountdetails.h"
#include "exceptions/applicationexception.h"
#include "gui/guiutilities.h"
#include "miscellaneous/application.h"
#include "network-web/oauth2service.h"
#include "network-web/webfactory.h"
#include "services/inoreader/definitions.h"
#include "services/inoreader/inoreadernetworkfactory.h"
InoreaderAccountDetails::InoreaderAccountDetails(QWidget* parent)
: QWidget(parent), m_oauth(nullptr), m_lastProxy({}) {
m_ui.setupUi(this);
GuiUtilities::setLabelAsNotice(*m_ui.m_lblInfo, true);
#if defined(INOREADER_OFFICIAL_SUPPORT)
m_ui.m_lblInfo->setText(tr("There are some preconfigured OAuth tokens so you do not have to fill in your "
"client ID/secret, but it is strongly recommended to obtain your "
"own as it preconfigured tokens have limited global usage quota. If you wish "
"to use preconfigured tokens, simply leave those fields empty and make sure "
"to leave default value of redirect URL."));
#else
m_ui.m_lblInfo->setText(tr("You have to fill in your client ID/secret and also fill in correct redirect URL."));
#endif
m_ui.m_lblTestResult->setStatus(WidgetWithStatus::StatusType::Information,
tr("Not tested yet."),
tr("Not tested yet."));
m_ui.m_lblTestResult->label()->setWordWrap(true);
m_ui.m_txtUsername->lineEdit()->setPlaceholderText(tr("User-visible username"));
setTabOrder(m_ui.m_txtUsername->lineEdit(), m_ui.m_txtAppId);
setTabOrder(m_ui.m_txtAppId, m_ui.m_txtAppKey);
setTabOrder(m_ui.m_txtAppKey, m_ui.m_txtRedirectUrl);
setTabOrder(m_ui.m_txtRedirectUrl, m_ui.m_btnRegisterApi);
setTabOrder(m_ui.m_btnRegisterApi, m_ui.m_cbDownloadOnlyUnreadMessages);
setTabOrder(m_ui.m_cbDownloadOnlyUnreadMessages, m_ui.m_spinLimitMessages);
setTabOrder(m_ui.m_spinLimitMessages, m_ui.m_btnTestSetup);
connect(m_ui.m_txtAppId->lineEdit(), &BaseLineEdit::textChanged, this, &InoreaderAccountDetails::checkOAuthValue);
connect(m_ui.m_txtAppKey->lineEdit(), &BaseLineEdit::textChanged, this, &InoreaderAccountDetails::checkOAuthValue);
connect(m_ui.m_txtRedirectUrl->lineEdit(), &BaseLineEdit::textChanged, this, &InoreaderAccountDetails::checkOAuthValue);
connect(m_ui.m_txtUsername->lineEdit(), &BaseLineEdit::textChanged, this, &InoreaderAccountDetails::checkUsername);
connect(m_ui.m_btnRegisterApi, &QPushButton::clicked, this, &InoreaderAccountDetails::registerApi);
emit m_ui.m_txtUsername->lineEdit()->textChanged(m_ui.m_txtUsername->lineEdit()->text());
emit m_ui.m_txtAppId->lineEdit()->textChanged(m_ui.m_txtAppId->lineEdit()->text());
emit m_ui.m_txtAppKey->lineEdit()->textChanged(m_ui.m_txtAppKey->lineEdit()->text());
emit m_ui.m_txtRedirectUrl->lineEdit()->textChanged(m_ui.m_txtAppKey->lineEdit()->text());
hookNetwork();
}
void InoreaderAccountDetails::testSetup(const QNetworkProxy& custom_proxy) {
m_lastProxy = custom_proxy;
if (m_oauth != nullptr) {
m_oauth->logout(true);
m_oauth->setClientId(m_ui.m_txtAppId->lineEdit()->text());
m_oauth->setClientSecret(m_ui.m_txtAppKey->lineEdit()->text());
m_oauth->setRedirectUrl(m_ui.m_txtRedirectUrl->lineEdit()->text(), true);
m_oauth->login();
}
}
void InoreaderAccountDetails::checkUsername(const QString& username) {
if (username.isEmpty()) {
m_ui.m_txtUsername->setStatus(WidgetWithStatus::StatusType::Error, tr("No username entered."));
}
else {
m_ui.m_txtUsername->setStatus(WidgetWithStatus::StatusType::Ok, tr("Some username entered."));
}
}
void InoreaderAccountDetails::onAuthFailed() {
m_ui.m_lblTestResult->setStatus(WidgetWithStatus::StatusType::Error,
tr("You did not grant access."),
tr("There was error during testing."));
}
void InoreaderAccountDetails::onAuthError(const QString& error, const QString& detailed_description) {
Q_UNUSED(error)
m_ui.m_lblTestResult->setStatus(WidgetWithStatus::StatusType::Error,
tr("There is error. %1").arg(detailed_description),
tr("There was error during testing."));
}
void InoreaderAccountDetails::onAuthGranted() {
m_ui.m_lblTestResult->setStatus(WidgetWithStatus::StatusType::Ok,
tr("Tested successfully. You may be prompted to login once more."),
tr("Your access was approved."));
try {
InoreaderNetworkFactory fac;
fac.setOauth(m_oauth);
auto resp = fac.userInfo(m_lastProxy);
m_ui.m_txtUsername->lineEdit()->setText(resp["userEmail"].toString());
}
catch (const ApplicationException& ex) {
qCriticalNN << LOGSEC_INOREADER
<< "Failed to obtain profile with error:"
<< QUOTE_W_SPACE_DOT(ex.message());
}
}
void InoreaderAccountDetails::hookNetwork() {
if (m_oauth != nullptr) {
connect(m_oauth, &OAuth2Service::tokensRetrieved, this, &InoreaderAccountDetails::onAuthGranted);
connect(m_oauth, &OAuth2Service::tokensRetrieveError, this, &InoreaderAccountDetails::onAuthError);
connect(m_oauth, &OAuth2Service::authFailed, this, &InoreaderAccountDetails::onAuthFailed);
}
}
void InoreaderAccountDetails::registerApi() {
qApp->web()->openUrlInExternalBrowser(INOREADER_REG_API_URL);
}
void InoreaderAccountDetails::checkOAuthValue(const QString& value) {
auto* line_edit = qobject_cast<LineEditWithStatus*>(sender()->parent());
if (line_edit != nullptr) {
if (value.isEmpty()) {
#if defined(INOREADER_OFFICIAL_SUPPORT)
line_edit->setStatus(WidgetWithStatus::StatusType::Ok, tr("Preconfigured client ID/secret will be used."));
#else
line_edit->setStatus(WidgetWithStatus::StatusType::Error, tr("Empty value is entered."));
#endif
}
else {
line_edit->setStatus(WidgetWithStatus::StatusType::Ok, tr("Some value is entered."));
}
}
}

View File

@ -1,46 +0,0 @@
// For license of this file, see <project-root-folder>/LICENSE.md.
#ifndef INOREADERACCOUNTDETAILS_H
#define INOREADERACCOUNTDETAILS_H
#include <QWidget>
#include "ui_inoreaderaccountdetails.h"
#include <QNetworkProxy>
class OAuth2Service;
class InoreaderAccountDetails : public QWidget {
Q_OBJECT
friend class FormEditInoreaderAccount;
public:
explicit InoreaderAccountDetails(QWidget* parent = nullptr);
private slots:
void registerApi();
void testSetup(const QNetworkProxy& custom_proxy);;
void checkOAuthValue(const QString& value);
void checkUsername(const QString& username);
void onAuthFailed();
void onAuthError(const QString& error, const QString& detailed_description);
void onAuthGranted();
private:
void hookNetwork();
private:
Ui::InoreaderAccountDetails m_ui;
// Testing OAuth service. This object is not ever copied
// to new living account instance, instead only its properties
// like tokens are copied.
// If editing existing account, then the pointer points
// directly to existing OAuth from the account.
OAuth2Service* m_oauth;
QNetworkProxy m_lastProxy;
};
#endif // INOREADERACCOUNTDETAILS_H

View File

@ -1,216 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>InoreaderAccountDetails</class>
<widget class="QWidget" name="InoreaderAccountDetails">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>427</width>
<height>265</height>
</rect>
</property>
<layout class="QFormLayout" name="formLayout_4">
<item row="0" column="0">
<widget class="QLabel" name="m_lblUsername">
<property name="text">
<string>Username</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="LineEditWithStatus" name="m_txtUsername" native="true"/>
</item>
<item row="1" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>OAuth 2.0 settings</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="m_lblUsername_2">
<property name="text">
<string>App ID</string>
</property>
<property name="buddy">
<cstring>m_txtAppId</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="LineEditWithStatus" name="m_txtAppId" native="true"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="m_lblUsername_3">
<property name="text">
<string>App key</string>
</property>
<property name="buddy">
<cstring>m_txtAppKey</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="LineEditWithStatus" name="m_txtAppKey" native="true"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="m_lblUsername_4">
<property name="text">
<string>Redirect URL</string>
</property>
<property name="buddy">
<cstring>m_txtRedirectUrl</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="LineEditWithStatus" name="m_txtRedirectUrl" native="true"/>
</item>
<item row="4" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="m_btnRegisterApi">
<property name="text">
<string>Get my own App ID</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="3" column="0" colspan="2">
<widget class="QLabel" name="m_lblInfo">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="3" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Only download newest X articles per feed</string>
</property>
<property name="buddy">
<cstring>m_spinLimitMessages</cstring>
</property>
</widget>
</item>
<item>
<widget class="MessageCountSpinBox" name="m_spinLimitMessages">
<property name="maximumSize">
<size>
<width>140</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="4" column="0" colspan="2">
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QPushButton" name="m_btnTestSetup">
<property name="text">
<string>&amp;Login</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="LabelWithStatus" name="m_lblTestResult" native="true">
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
</widget>
</item>
</layout>
</item>
<item row="5" column="0" colspan="2">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="0" colspan="2">
<widget class="QCheckBox" name="m_cbDownloadOnlyUnreadMessages">
<property name="text">
<string>Download unread articles only</string>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LabelWithStatus</class>
<extends>QWidget</extends>
<header>labelwithstatus.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>LineEditWithStatus</class>
<extends>QWidget</extends>
<header>lineeditwithstatus.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>MessageCountSpinBox</class>
<extends>QSpinBox</extends>
<header>messagecountspinbox.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -1,46 +0,0 @@
// For license of this file, see <project-root-folder>/LICENSE.md.
#include "services/inoreader/inoreaderentrypoint.h"
#include "definitions/definitions.h"
#include "miscellaneous/application.h"
#include "database/databasequeries.h"
#include "miscellaneous/iconfactory.h"
#include "services/inoreader/definitions.h"
#include "services/inoreader/gui/formeditinoreaderaccount.h"
#include "services/inoreader/inoreadernetworkfactory.h"
#include "services/inoreader/inoreaderserviceroot.h"
#include <QMessageBox>
ServiceRoot* InoreaderEntryPoint::createNewRoot() const {
FormEditInoreaderAccount form_acc(qApp->mainFormWidget());
return form_acc.addEditAccount<InoreaderServiceRoot>();
}
QList<ServiceRoot*> InoreaderEntryPoint::initializeSubtree() const {
QSqlDatabase database = qApp->database()->driver()->connection(QSL("InoreaderEntryPoint"));
return DatabaseQueries::getAccounts<InoreaderServiceRoot>(database, code());
}
QString InoreaderEntryPoint::name() const {
return QSL("Inoreader");
}
QString InoreaderEntryPoint::code() const {
return SERVICE_CODE_INOREADER;
}
QString InoreaderEntryPoint::description() const {
return QObject::tr("This is integration of Inoreader.");
}
QString InoreaderEntryPoint::author() const {
return APP_AUTHOR;
}
QIcon InoreaderEntryPoint::icon() const {
return qApp->icons()->miscIcon(QSL("inoreader"));
}

View File

@ -1,19 +0,0 @@
// For license of this file, see <project-root-folder>/LICENSE.md.
#ifndef INOREADERENTRYPOINT_H
#define INOREADERENTRYPOINT_H
#include "services/abstract/serviceentrypoint.h"
class InoreaderEntryPoint : public ServiceEntryPoint {
public:
virtual ServiceRoot* createNewRoot() const;
virtual QList<ServiceRoot*> initializeSubtree() const;
virtual QString name() const;
virtual QString code() const;
virtual QString description() const;
virtual QString author() const;
virtual QIcon icon() const;
};
#endif // INOREADERENTRYPOINT_H

View File

@ -1,504 +0,0 @@
// For license of this file, see <project-root-folder>/LICENSE.md.
#include "services/inoreader/inoreadernetworkfactory.h"
#include "3rd-party/boolinq/boolinq.h"
#include "database/databasequeries.h"
#include "definitions/definitions.h"
#include "exceptions/applicationexception.h"
#include "exceptions/networkexception.h"
#include "gui/dialogs/formmain.h"
#include "gui/tabwidget.h"
#include "miscellaneous/application.h"
#include "network-web/networkfactory.h"
#include "network-web/oauth2service.h"
#include "network-web/silentnetworkaccessmanager.h"
#include "network-web/webfactory.h"
#include "services/abstract/category.h"
#include "services/abstract/labelsnode.h"
#include "services/inoreader/definitions.h"
#include "services/inoreader/inoreaderserviceroot.h"
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QRandomGenerator>
#include <QRegularExpression>
#include <QUrl>
InoreaderNetworkFactory::InoreaderNetworkFactory(QObject* parent) : QObject(parent),
m_service(nullptr), m_username(QString()), m_downloadOnlyUnreadMessages(false), m_batchSize(INOREADER_DEFAULT_BATCH_SIZE),
m_oauth2(new OAuth2Service(INOREADER_OAUTH_AUTH_URL, INOREADER_OAUTH_TOKEN_URL,
{}, {}, INOREADER_OAUTH_SCOPE, this)) {
initializeOauth();
}
void InoreaderNetworkFactory::setService(InoreaderServiceRoot* service) {
m_service = service;
}
OAuth2Service* InoreaderNetworkFactory::oauth() const {
return m_oauth2;
}
QString InoreaderNetworkFactory::username() const {
return m_username;
}
int InoreaderNetworkFactory::batchSize() const {
return m_batchSize;
}
void InoreaderNetworkFactory::setBatchSize(int batch_size) {
m_batchSize = batch_size;
}
void InoreaderNetworkFactory::initializeOauth() {
#if defined(INOREADER_OFFICIAL_SUPPORT)
m_oauth2->setClientSecretId(TextFactory::decrypt(INOREADER_CLIENT_ID, OAUTH_DECRYPTION_KEY));
m_oauth2->setClientSecretSecret(TextFactory::decrypt(INOREADER_CLIENT_SECRET, OAUTH_DECRYPTION_KEY));
#endif
m_oauth2->setRedirectUrl(QString(OAUTH_REDIRECT_URI) +
QL1C(':') +
QString::number(INOREADER_OAUTH_REDIRECT_URI_PORT),
true);
connect(m_oauth2, &OAuth2Service::tokensRetrieveError, this, &InoreaderNetworkFactory::onTokensError);
connect(m_oauth2, &OAuth2Service::authFailed, this, &InoreaderNetworkFactory::onAuthFailed);
connect(m_oauth2, &OAuth2Service::tokensRetrieved, this, [this](QString access_token, QString refresh_token, int expires_in) {
Q_UNUSED(expires_in)
Q_UNUSED(access_token)
if (m_service != nullptr && m_service->accountId() > 0 && !refresh_token.isEmpty()) {
QSqlDatabase database = qApp->database()->driver()->connection(metaObject()->className());
DatabaseQueries::storeNewOauthTokens(database, refresh_token, m_service->accountId());
}
});
}
bool InoreaderNetworkFactory::downloadOnlyUnreadMessages() const {
return m_downloadOnlyUnreadMessages;
}
void InoreaderNetworkFactory::setDownloadOnlyUnreadMessages(bool download_only_unread) {
m_downloadOnlyUnreadMessages = download_only_unread;
}
void InoreaderNetworkFactory::setUsername(const QString& username) {
m_username = username;
}
RootItem* InoreaderNetworkFactory::feedsCategories(bool obtain_icons) {
QString bearer = m_oauth2->bearer().toLocal8Bit();
if (bearer.isEmpty()) {
return nullptr;
}
QByteArray output_labels;
auto result_labels = NetworkFactory::performNetworkOperation(INOREADER_API_LIST_LABELS,
qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(),
{},
output_labels,
QNetworkAccessManager::Operation::GetOperation,
{ { QString(HTTP_HEADERS_AUTHORIZATION).toLocal8Bit(),
bearer.toLocal8Bit() } },
false,
{},
{},
m_service->networkProxy());
if (result_labels.first != QNetworkReply::NetworkError::NoError) {
return nullptr;
}
QByteArray output_feeds;
auto result_feeds = NetworkFactory::performNetworkOperation(INOREADER_API_LIST_FEEDS,
qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(),
{},
output_feeds,
QNetworkAccessManager::Operation::GetOperation,
{ { QString(HTTP_HEADERS_AUTHORIZATION).toLocal8Bit(),
bearer.toLocal8Bit() } },
false,
{},
{},
m_service->networkProxy());
if (result_feeds.first != QNetworkReply::NetworkError::NoError) {
return nullptr;
}
return decodeFeedCategoriesData(output_labels, output_feeds, obtain_icons);
}
QVariantHash InoreaderNetworkFactory::userInfo(const QNetworkProxy& custom_proxy) {
QString bearer = m_oauth2->bearer().toLocal8Bit();
if (bearer.isEmpty()) {
throw ApplicationException(tr("not logged in"));
}
int timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt();
QByteArray output;
auto res = NetworkFactory::performNetworkOperation(INOREADER_API_USER_INFO,
timeout,
{},
output,
QNetworkAccessManager::Operation::GetOperation,
{ { QString(HTTP_HEADERS_AUTHORIZATION).toLocal8Bit(),
bearer.toLocal8Bit() } },
false,
{},
{},
custom_proxy);
if (res.first != QNetworkReply::NetworkError::NoError) {
throw NetworkException(res.first);
}
return QJsonDocument::fromJson(output).object().toVariantHash();
}
QList<RootItem*> InoreaderNetworkFactory::getLabels() {
QList<RootItem*> lbls;
QString bearer = m_oauth2->bearer().toLocal8Bit();
if (bearer.isEmpty()) {
return lbls;
}
QByteArray output;
auto result = NetworkFactory::performNetworkOperation(INOREADER_API_LIST_LABELS,
qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(),
{},
output,
QNetworkAccessManager::Operation::GetOperation,
{ { QString(HTTP_HEADERS_AUTHORIZATION).toLocal8Bit(),
bearer.toLocal8Bit() } },
false,
{},
{},
m_service->networkProxy());
QJsonDocument json_lbls = QJsonDocument::fromJson(output);
auto json_tags = json_lbls.object()["tags"].toArray();
for (const QJsonValue& lbl_val : qAsConst(json_tags)) {
QJsonObject lbl_obj = lbl_val.toObject();
if (lbl_obj["type"] == QL1S("tag")) {
QString name_id = lbl_obj["id"].toString();
QString plain_name = QRegularExpression(".+\\/([^\\/]+)").match(name_id).captured(1);
auto* new_lbl = new Label(plain_name, TextFactory::generateColorFromText(name_id));
new_lbl->setCustomId(name_id);
lbls.append(new_lbl);
}
}
return lbls;
}
QList<Message> InoreaderNetworkFactory::messages(ServiceRoot* root, const QString& stream_id, Feed::Status& error) {
QString target_url = INOREADER_API_FEED_CONTENTS;
QString bearer = m_oauth2->bearer().toLocal8Bit();
if (bearer.isEmpty()) {
qCriticalNN << LOGSEC_INOREADER
<< "Cannot download messages for"
<< QUOTE_NO_SPACE(stream_id)
<< ", bearer is empty.";
error = Feed::Status::AuthError;
return QList<Message>();
}
target_url += QSL("/") + QUrl::toPercentEncoding(stream_id) + QString("?n=%1").arg(batchSize() <= 0
? INOREADER_MAX_BATCH_SIZE
: batchSize());
if (downloadOnlyUnreadMessages()) {
target_url += QSL("&xt=%1").arg(INOREADER_FULL_STATE_READ);
}
QByteArray output_msgs;
auto result = NetworkFactory::performNetworkOperation(target_url,
qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(),
{},
output_msgs,
QNetworkAccessManager::Operation::GetOperation,
{ { QString(HTTP_HEADERS_AUTHORIZATION).toLocal8Bit(),
bearer.toLocal8Bit() } },
false,
{},
{},
m_service->networkProxy());
if (result.first != QNetworkReply::NetworkError::NoError) {
qCriticalNN << LOGSEC_INOREADER
<< "Cannot download messages for "
<< QUOTE_NO_SPACE(stream_id)
<< ", network error:"
<< QUOTE_W_SPACE_DOT(result.first);
error = Feed::Status::NetworkError;
return QList<Message>();
}
else {
error = Feed::Status::Normal;
return decodeMessages(root, output_msgs, stream_id);
}
}
QNetworkReply::NetworkError InoreaderNetworkFactory::editLabels(const QString& state, bool assign,
const QStringList& msg_custom_ids) {
QString target_url = INOREADER_API_EDIT_TAG;
if (assign) {
target_url += QString("?a=") + state + "&";
}
else {
target_url += QString("?r=") + state + "&";
}
QString bearer = m_oauth2->bearer().toLocal8Bit();
if (bearer.isEmpty()) {
return QNetworkReply::NetworkError::AuthenticationRequiredError;
}
QList<QPair<QByteArray, QByteArray>> headers;
headers.append(QPair<QByteArray, QByteArray>(QString(HTTP_HEADERS_AUTHORIZATION).toLocal8Bit(),
m_oauth2->bearer().toLocal8Bit()));
QStringList trimmed_ids;
for (const QString& id : msg_custom_ids) {
trimmed_ids.append(QString("i=") + id);
}
QStringList working_subset; working_subset.reserve(std::min(INOREADER_API_EDIT_TAG_BATCH, trimmed_ids.size()));
int timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt();
// Now, we perform messages update in batches (max XX messages per batch).
while (!trimmed_ids.isEmpty()) {
// We take XX IDs.
for (int i = 0; i < INOREADER_API_EDIT_TAG_BATCH && !trimmed_ids.isEmpty(); i++) {
working_subset.append(trimmed_ids.takeFirst());
}
QString batch_final_url = target_url + working_subset.join(QL1C('&'));
// We send this batch.
QByteArray output;
auto result = NetworkFactory::performNetworkOperation(batch_final_url,
timeout,
{},
output,
QNetworkAccessManager::Operation::GetOperation,
headers,
false,
{},
{},
m_service->networkProxy());
if (result.first != QNetworkReply::NetworkError::NoError) {
return result.first;
}
// Cleanup for next batch.
working_subset.clear();
}
return QNetworkReply::NetworkError::NoError;
}
QNetworkReply::NetworkError InoreaderNetworkFactory::markMessagesRead(RootItem::ReadStatus status, const QStringList& msg_custom_ids) {
return editLabels(INOREADER_FULL_STATE_READ, status == RootItem::ReadStatus::Read, msg_custom_ids);
}
QNetworkReply::NetworkError InoreaderNetworkFactory::markMessagesStarred(RootItem::Importance importance, const QStringList& msg_custom_ids) {
return editLabels(INOREADER_FULL_STATE_IMPORTANT, importance == RootItem::Importance::Important, msg_custom_ids);
}
void InoreaderNetworkFactory::onTokensError(const QString& error, const QString& error_description) {
Q_UNUSED(error)
qApp->showGuiMessage(Notification::Event::GeneralEvent,
tr("Inoreader: authentication error"),
tr("Click this to login again. Error is: '%1'").arg(error_description),
QSystemTrayIcon::MessageIcon::Critical,
{}, {},
[this]() {
m_oauth2->setAccessToken(QString());
m_oauth2->setRefreshToken(QString());
m_oauth2->login();
});
}
void InoreaderNetworkFactory::onAuthFailed() {
qApp->showGuiMessage(Notification::Event::GeneralEvent,
tr("Inoreader: authorization denied"),
tr("Click this to login again."),
QSystemTrayIcon::MessageIcon::Critical,
{}, {},
[this]() {
m_oauth2->login();
});
}
QList<Message> InoreaderNetworkFactory::decodeMessages(ServiceRoot* root, const QString& messages_json_data, const QString& stream_id) {
QList<Message> messages;
QJsonArray json = QJsonDocument::fromJson(messages_json_data.toUtf8()).object()["items"].toArray();
auto active_labels = root->labelsNode() != nullptr ? root->labelsNode()->labels() : QList<Label*>();
messages.reserve(json.count());
for (const QJsonValue& obj : json) {
auto message_obj = obj.toObject();
Message message;
message.m_title = qApp->web()->unescapeHtml(message_obj["title"].toString());
message.m_author = qApp->web()->unescapeHtml(message_obj["author"].toString());
message.m_created = QDateTime::fromSecsSinceEpoch(message_obj["published"].toInt(), Qt::UTC);
message.m_createdFromFeed = true;
message.m_customId = message_obj["id"].toString();
auto alternates = message_obj["alternate"].toArray();
auto enclosures = message_obj["enclosure"].toArray();
auto categories = message_obj["categories"].toArray();
for (const QJsonValue& alt : alternates) {
auto alt_obj = alt.toObject();
QString mime = alt_obj["type"].toString();
QString href = alt_obj["href"].toString();
if (mime == QL1S("text/html")) {
message.m_url = href;
}
else {
message.m_enclosures.append(Enclosure(href, mime));
}
}
for (const QJsonValue& enc : enclosures) {
auto enc_obj = enc.toObject();
QString mime = enc_obj["type"].toString();
QString href = enc_obj["href"].toString();
message.m_enclosures.append(Enclosure(href, mime));
}
for (const QJsonValue& cat : categories) {
QString category = cat.toString();
if (category.contains(INOREADER_STATE_READ)) {
message.m_isRead = !category.contains(INOREADER_STATE_READING_LIST);
}
else if (category.contains(INOREADER_STATE_IMPORTANT)) {
message.m_isImportant = category.contains(INOREADER_STATE_IMPORTANT);
}
else if (category.contains(QSL("label"))) {
Label* label = boolinq::from(active_labels.begin(), active_labels.end()).firstOrDefault([category](Label* lbl) {
return lbl->customId() == category;
});
if (label != nullptr) {
// We found live Label object for our assigned label.
message.m_assignedLabels.append(label);
}
}
}
message.m_contents = message_obj["summary"].toObject()["content"].toString();
message.m_rawContents = QJsonDocument(message_obj).toJson(QJsonDocument::JsonFormat::Compact);
message.m_feedId = stream_id;
messages.append(message);
}
return messages;
}
RootItem* InoreaderNetworkFactory::decodeFeedCategoriesData(const QString& categories, const QString& feeds, bool obtain_icons) {
auto* parent = new RootItem();
QJsonArray json = QJsonDocument::fromJson(categories.toUtf8()).object()["tags"].toArray();
QMap<QString, RootItem*> cats;
cats.insert(QString(), parent);
for (const QJsonValue& obj : json) {
auto label = obj.toObject();
if (label["type"].toString() == QL1S("folder")) {
QString label_id = label["id"].toString();
// We have label (not "state").
auto* category = new Category();
category->setDescription(label["htmlUrl"].toString());
category->setTitle(label_id.mid(label_id.lastIndexOf(QL1C('/')) + 1));
category->setCustomId(label_id);
cats.insert(category->customId(), category);
parent->appendChild(category);
}
}
json = QJsonDocument::fromJson(feeds.toUtf8()).object()["subscriptions"].toArray();
for (const QJsonValue& obj : qAsConst(json)) {
auto subscription = obj.toObject();
QString id = subscription["id"].toString();
QString title = subscription["title"].toString();
QString url = subscription["htmlUrl"].toString();
QString parent_label;
QJsonArray assigned_categories = subscription["categories"].toArray();
for (const QJsonValue& cat : assigned_categories) {
QString potential_id = cat.toObject()["id"].toString();
if (potential_id.contains(QSL("/label/"))) {
parent_label = potential_id;
break;
}
}
// We have label (not "state").
auto* feed = new Feed();
feed->setDescription(url);
feed->setSource(url);
feed->setTitle(title);
feed->setCustomId(id);
if (obtain_icons) {
QString icon_url = subscription["iconUrl"].toString();
if (!icon_url.isEmpty()) {
QByteArray icon_data;
if (NetworkFactory::performNetworkOperation(icon_url, DOWNLOAD_TIMEOUT,
QByteArray(), icon_data,
QNetworkAccessManager::GetOperation).first == QNetworkReply::NoError) {
// Icon downloaded, set it up.
QPixmap icon_pixmap;
icon_pixmap.loadFromData(icon_data);
feed->setIcon(QIcon(icon_pixmap));
}
}
}
if (cats.contains(parent_label)) {
cats[parent_label]->appendChild(feed);
}
}
return parent;
}
void InoreaderNetworkFactory::setOauth(OAuth2Service* oauth) {
m_oauth2 = oauth;
}

View File

@ -1,70 +0,0 @@
// For license of this file, see <project-root-folder>/LICENSE.md.
#ifndef INOREADERNETWORKFACTORY_H
#define INOREADERNETWORKFACTORY_H
#include <QObject>
#include "core/message.h"
#include "services/abstract/feed.h"
#include "services/abstract/rootitem.h"
#include <QNetworkReply>
class RootItem;
class InoreaderServiceRoot;
class OAuth2Service;
class InoreaderNetworkFactory : public QObject {
Q_OBJECT
public:
explicit InoreaderNetworkFactory(QObject* parent = nullptr);
void setService(InoreaderServiceRoot* service);
OAuth2Service* oauth() const;
void setOauth(OAuth2Service* oauth);
QString username() const;
void setUsername(const QString& username);
// Gets/sets the amount of messages to obtain during single feed update.
int batchSize() const;
void setBatchSize(int batch_size);
// 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(bool obtain_icons);
QVariantHash userInfo(const QNetworkProxy& custom_proxy);
QList<RootItem*> getLabels();
QList<Message> messages(ServiceRoot* root, const QString& stream_id, Feed::Status& error);
QNetworkReply::NetworkError editLabels(const QString& state, bool assign, const QStringList& msg_custom_ids);
QNetworkReply::NetworkError markMessagesRead(RootItem::ReadStatus status, const QStringList& msg_custom_ids);
QNetworkReply::NetworkError markMessagesStarred(RootItem::Importance importance, const QStringList& msg_custom_ids);
bool downloadOnlyUnreadMessages() const;
void setDownloadOnlyUnreadMessages(bool download_only_unread);
private slots:
void onTokensError(const QString& error, const QString& error_description);
void onAuthFailed();
private:
QList<Message> decodeMessages(ServiceRoot* root, const QString& messages_json_data, const QString& stream_id);
RootItem* decodeFeedCategoriesData(const QString& categories, const QString& feeds, bool obtain_icons);
void initializeOauth();
private:
InoreaderServiceRoot* m_service;
QString m_username;
bool m_downloadOnlyUnreadMessages;
int m_batchSize;
OAuth2Service* m_oauth2;
};
#endif // INOREADERNETWORKFACTORY_H

View File

@ -1,211 +0,0 @@
// For license of this file, see <project-root-folder>/LICENSE.md.
#include "services/inoreader/inoreaderserviceroot.h"
#include "database/databasequeries.h"
#include "exceptions/feedfetchexception.h"
#include "miscellaneous/application.h"
#include "miscellaneous/iconfactory.h"
#include "network-web/oauth2service.h"
#include "services/abstract/importantnode.h"
#include "services/abstract/labelsnode.h"
#include "services/abstract/recyclebin.h"
#include "services/inoreader/gui/formeditinoreaderaccount.h"
#include "services/inoreader/inoreaderentrypoint.h"
#include "services/inoreader/inoreadernetworkfactory.h"
#include <QThread>
InoreaderServiceRoot::InoreaderServiceRoot(RootItem* parent)
: ServiceRoot(parent), m_network(new InoreaderNetworkFactory(this)) {
m_network->setService(this);
setIcon(InoreaderEntryPoint().icon());
}
InoreaderServiceRoot::~InoreaderServiceRoot() = default;
void InoreaderServiceRoot::updateTitle() {
setTitle(TextFactory::extractUsernameFromEmail(m_network->username()) + QSL(" (Inoreader)"));
}
ServiceRoot::LabelOperation InoreaderServiceRoot::supportedLabelOperations() const {
return ServiceRoot::LabelOperation(0);
}
QVariantHash InoreaderServiceRoot::customDatabaseData() const {
QVariantHash data;
data["username"] = m_network->username();
data["download_only_unread"] = m_network->downloadOnlyUnreadMessages();
data["batch_size"] = m_network->batchSize();
data["client_id"] = m_network->oauth()->clientId();
data["client_secret"] = m_network->oauth()->clientSecret();
data["refresh_token"] = m_network->oauth()->refreshToken();
data["redirect_uri"] = m_network->oauth()->redirectUrl();
return data;
}
void InoreaderServiceRoot::setCustomDatabaseData(const QVariantHash& data) {
m_network->setUsername(data["username"].toString());
m_network->setBatchSize(data["batch_size"].toInt());
m_network->setDownloadOnlyUnreadMessages(data["download_only_unread"].toBool());
m_network->oauth()->setClientId(data["client_id"].toString());
m_network->oauth()->setClientSecret(data["client_secret"].toString());
m_network->oauth()->setRefreshToken(data["refresh_token"].toString());
m_network->oauth()->setRedirectUrl(data["redirect_uri"].toString(), true);
}
QList<Message> InoreaderServiceRoot::obtainNewMessages(Feed* feed,
const QHash<ServiceRoot::BagOfMessages, QStringList>& stated_messages,
const QHash<QString, QStringList>& tagged_messages) {
Q_UNUSED(stated_messages)
Q_UNUSED(tagged_messages)
Feed::Status error = Feed::Status::Normal;
QList<Message> messages = network()->messages(this, feed->customId(), error);
if (error != Feed::Status::NewMessages && error != Feed::Status::Normal) {
throw FeedFetchException(error);
}
return messages;
}
bool InoreaderServiceRoot::isSyncable() const {
return true;
}
bool InoreaderServiceRoot::canBeEdited() const {
return true;
}
bool InoreaderServiceRoot::editViaGui() {
FormEditInoreaderAccount form_pointer(qApp->mainFormWidget());
form_pointer.addEditAccount(this);
return true;
}
bool InoreaderServiceRoot::supportsFeedAdding() const {
return false;
}
bool InoreaderServiceRoot::supportsCategoryAdding() const {
return false;
}
void InoreaderServiceRoot::start(bool freshly_activated) {
if (!freshly_activated) {
DatabaseQueries::loadFromDatabase<Category, Feed>(this);
loadCacheFromFile();
}
updateTitle();
if (getSubTreeFeeds().isEmpty()) {
m_network->oauth()->login([this]() {
syncIn();
});
}
else {
m_network->oauth()->login();
}
}
QString InoreaderServiceRoot::code() const {
return InoreaderEntryPoint().code();
}
QString InoreaderServiceRoot::additionalTooltip() const {
return tr("Authentication status: %1\n"
"Login tokens expiration: %2").arg(network()->oauth()->isFullyLoggedIn() ? tr("logged-in") : tr("NOT logged-in"),
network()->oauth()->tokensExpireIn().isValid() ?
network()->oauth()->tokensExpireIn().toString() : QSL("-"));
}
RootItem* InoreaderServiceRoot::obtainNewTreeForSyncIn() const {
auto tree = m_network->feedsCategories(true);
if (tree != nullptr) {
auto* lblroot = new LabelsNode(tree);
auto labels = m_network->getLabels();
lblroot->setChildItems(labels);
tree->appendChild(lblroot);
return tree;
}
else {
return nullptr;
}
}
void InoreaderServiceRoot::saveAllCachedData(bool ignore_errors) {
auto msg_cache = takeMessageCache();
QMapIterator<RootItem::ReadStatus, QStringList> i(msg_cache.m_cachedStatesRead);
// Save the actual data read/unread.
while (i.hasNext()) {
i.next();
auto key = i.key();
QStringList ids = i.value();
if (!ids.isEmpty()) {
if (network()->markMessagesRead(key, ids) != QNetworkReply::NetworkError::NoError && !ignore_errors) {
addMessageStatesToCache(ids, key);
}
}
}
QMapIterator<RootItem::Importance, QList<Message>> j(msg_cache.m_cachedStatesImportant);
// Save the actual data important/not important.
while (j.hasNext()) {
j.next();
auto key = j.key();
QList<Message> messages = j.value();
if (!messages.isEmpty()) {
QStringList custom_ids;
for (const Message& msg : messages) {
custom_ids.append(msg.m_customId);
}
if (network()->markMessagesStarred(key, custom_ids) != QNetworkReply::NetworkError::NoError && !ignore_errors) {
addMessageStatesToCache(messages, key);
}
}
}
QMapIterator<QString, QStringList> k(msg_cache.m_cachedLabelAssignments);
// Assign label for these messages.
while (k.hasNext()) {
k.next();
auto label_custom_id = k.key();
QStringList messages = k.value();
if (!messages.isEmpty()) {
if (network()->editLabels(label_custom_id, true, messages) != QNetworkReply::NetworkError::NoError && !ignore_errors) {
addLabelsAssignmentsToCache(messages, label_custom_id, true);
}
}
}
QMapIterator<QString, QStringList> l(msg_cache.m_cachedLabelDeassignments);
// Remove label from these messages.
while (l.hasNext()) {
l.next();
auto label_custom_id = l.key();
QStringList messages = l.value();
if (!messages.isEmpty()) {
if (network()->editLabels(label_custom_id, false, messages) != QNetworkReply::NetworkError::NoError && !ignore_errors) {
addLabelsAssignmentsToCache(messages, label_custom_id, false);
}
}
}
}

View File

@ -1,50 +0,0 @@
// For license of this file, see <project-root-folder>/LICENSE.md.
#ifndef INOREADERSERVICEROOT_H
#define INOREADERSERVICEROOT_H
#include "services/abstract/cacheforserviceroot.h"
#include "services/abstract/serviceroot.h"
class InoreaderNetworkFactory;
class InoreaderServiceRoot : public ServiceRoot, public CacheForServiceRoot {
Q_OBJECT
public:
explicit InoreaderServiceRoot(RootItem* parent = nullptr);
virtual ~InoreaderServiceRoot();
InoreaderNetworkFactory* network() const;
virtual LabelOperation supportedLabelOperations() const;
virtual bool isSyncable() const;
virtual bool canBeEdited() const;
virtual bool editViaGui();
virtual bool supportsFeedAdding() const;
virtual bool supportsCategoryAdding() const;
virtual void start(bool freshly_activated);
virtual QString code() const;
virtual QString additionalTooltip() const;
virtual void saveAllCachedData(bool ignore_errors);
virtual QVariantHash customDatabaseData() const;
virtual void setCustomDatabaseData(const QVariantHash& data);
virtual QList<Message> obtainNewMessages(Feed* feed,
const QHash<ServiceRoot::BagOfMessages, QStringList>& stated_messages,
const QHash<QString, QStringList>& tagged_messages);
protected:
virtual RootItem* obtainNewTreeForSyncIn() const;
private:
void updateTitle();
private:
InoreaderNetworkFactory* m_network;
};
inline InoreaderNetworkFactory* InoreaderServiceRoot::network() const {
return m_network;
}
#endif // INOREADERSERVICEROOT_H