bare skeleton for reddit plugin
This commit is contained in:
parent
f8bb0ea6a6
commit
6b1a93b66c
BIN
resources/graphics/misc/reddit.png
Executable file
BIN
resources/graphics/misc/reddit.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 9.8 KiB |
@ -34,6 +34,7 @@
|
||||
<file>graphics/misc/image-placeholder.png</file>
|
||||
<file>graphics/misc/inoreader.png</file>
|
||||
<file>graphics/misc/nextcloud.png</file>
|
||||
<file>graphics/misc/reddit.png</file>
|
||||
<file>graphics/misc/reedah.png</file>
|
||||
<file>graphics/misc/theoldreader.png</file>
|
||||
<file>graphics/misc/tt-rss.png</file>
|
||||
|
@ -13,6 +13,7 @@
|
||||
#define SERVICE_CODE_FEEDLY "feedly"
|
||||
#define SERVICE_CODE_INOREADER "inoreader"
|
||||
#define SERVICE_CODE_GMAIL "gmail"
|
||||
#define SERVICE_CODE_REDDIT "reddit"
|
||||
|
||||
#define ADBLOCK_SERVER_PORT 48484
|
||||
#define ADBLOCK_HOWTO "https://github.com/martinrotter/rssguard/blob/master/resources/docs/Documentation.md#adblock"
|
||||
@ -132,6 +133,7 @@
|
||||
#define LOGSEC_TTRSS "tt-rss: "
|
||||
#define LOGSEC_GMAIL "gmail: "
|
||||
#define LOGSEC_OAUTH "oauth: "
|
||||
#define LOGSEC_REDDIT "reddit: "
|
||||
|
||||
#define MAX_ZOOM_FACTOR 5.0f
|
||||
#define MIN_ZOOM_FACTOR 0.25f
|
||||
|
@ -192,6 +192,12 @@ HEADERS += core/feeddownloader.h \
|
||||
services/owncloud/owncloudnetworkfactory.h \
|
||||
services/owncloud/owncloudserviceentrypoint.h \
|
||||
services/owncloud/owncloudserviceroot.h \
|
||||
services/reddit/definitions.h \
|
||||
services/reddit/gui/formeditredditaccount.h \
|
||||
services/reddit/gui/redditaccountdetails.h \
|
||||
services/reddit/redditentrypoint.h \
|
||||
services/reddit/redditnetworkfactory.h \
|
||||
services/reddit/redditserviceroot.h \
|
||||
services/standard/atomparser.h \
|
||||
services/standard/definitions.h \
|
||||
services/standard/feedparser.h \
|
||||
@ -368,6 +374,11 @@ SOURCES += core/feeddownloader.cpp \
|
||||
services/owncloud/owncloudnetworkfactory.cpp \
|
||||
services/owncloud/owncloudserviceentrypoint.cpp \
|
||||
services/owncloud/owncloudserviceroot.cpp \
|
||||
services/reddit/gui/formeditredditaccount.cpp \
|
||||
services/reddit/gui/redditaccountdetails.cpp \
|
||||
services/reddit/redditentrypoint.cpp \
|
||||
services/reddit/redditnetworkfactory.cpp \
|
||||
services/reddit/redditserviceroot.cpp \
|
||||
services/standard/atomparser.cpp \
|
||||
services/standard/feedparser.cpp \
|
||||
services/standard/gui/formeditstandardaccount.cpp \
|
||||
@ -432,6 +443,7 @@ FORMS += gui/dialogs/formabout.ui \
|
||||
services/gmail/gui/gmailaccountdetails.ui \
|
||||
services/greader/gui/greaderaccountdetails.ui \
|
||||
services/owncloud/gui/owncloudaccountdetails.ui \
|
||||
services/reddit/gui/redditaccountdetails.ui \
|
||||
services/standard/gui/formstandardimportexport.ui \
|
||||
services/standard/gui/standardfeeddetails.ui \
|
||||
services/tt-rss/gui/ttrssaccountdetails.ui \
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "services/gmail/gmailentrypoint.h"
|
||||
#include "services/greader/greaderentrypoint.h"
|
||||
#include "services/owncloud/owncloudserviceentrypoint.h"
|
||||
#include "services/reddit/redditentrypoint.h"
|
||||
#include "services/standard/standardserviceentrypoint.h"
|
||||
#include "services/tt-rss/ttrssserviceentrypoint.h"
|
||||
|
||||
@ -60,6 +61,11 @@ QList<ServiceEntryPoint*> FeedReader::feedServices() {
|
||||
m_feedServices.append(new GmailEntryPoint());
|
||||
m_feedServices.append(new GreaderEntryPoint());
|
||||
m_feedServices.append(new OwnCloudServiceEntryPoint());
|
||||
|
||||
#if defined(DEBUG)
|
||||
m_feedServices.append(new RedditEntryPoint());
|
||||
#endif
|
||||
|
||||
m_feedServices.append(new StandardServiceEntryPoint());
|
||||
m_feedServices.append(new TtRssServiceEntryPoint());
|
||||
}
|
||||
|
21
src/librssguard/services/reddit/definitions.h
Executable file
21
src/librssguard/services/reddit/definitions.h
Executable file
@ -0,0 +1,21 @@
|
||||
// For license of this file, see <project-root-folder>/LICENSE.md.
|
||||
|
||||
#ifndef REDDIT_DEFINITIONS_H
|
||||
#define REDDIT_DEFINITIONS_H
|
||||
|
||||
#define REDDIT_OAUTH_REDIRECT_URI_PORT 14499
|
||||
#define REDDIT_OAUTH_AUTH_URL "https://www.reddit.com/api/v1/authorize"
|
||||
#define REDDIT_OAUTH_TOKEN_URL "https://www.reddit.com/api/v1/access_token"
|
||||
#define REDDIT_OAUTH_SCOPE "identity"
|
||||
|
||||
#define REDDIT_REG_API_URL "https://www.reddit.com/prefs/apps"
|
||||
|
||||
#define REDDIT_API_GET_PROFILE "https://oauth.reddit.com/api/v1/me"
|
||||
|
||||
#define REDDIT_DEFAULT_BATCH_SIZE 100
|
||||
#define REDDIT_MAX_BATCH_SIZE 999
|
||||
|
||||
#define REDDIT_CONTENT_TYPE_HTTP "application/http"
|
||||
#define REDDIT_CONTENT_TYPE_JSON "application/json"
|
||||
|
||||
#endif // REDDIT_DEFINITIONS_H
|
68
src/librssguard/services/reddit/gui/formeditredditaccount.cpp
Executable file
68
src/librssguard/services/reddit/gui/formeditredditaccount.cpp
Executable file
@ -0,0 +1,68 @@
|
||||
// For license of this file, see <project-root-folder>/LICENSE.md.
|
||||
|
||||
#include "services/reddit/gui/formeditredditaccount.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/reddit/definitions.h"
|
||||
#include "services/reddit/redditserviceroot.h"
|
||||
#include "services/reddit/gui/redditaccountdetails.h"
|
||||
|
||||
FormEditRedditAccount::FormEditRedditAccount(QWidget* parent)
|
||||
: FormAccountDetails(qApp->icons()->miscIcon(QSL("reddit")), parent), m_details(new RedditAccountDetails(this)) {
|
||||
insertCustomTab(m_details, tr("Server setup"), 0);
|
||||
activateTab(0);
|
||||
|
||||
m_details->m_ui.m_txtUsername->setFocus();
|
||||
connect(m_details->m_ui.m_btnTestSetup, &QPushButton::clicked, this, [this]() {
|
||||
m_details->testSetup(m_proxyDetails->proxy());
|
||||
});
|
||||
}
|
||||
|
||||
void FormEditRedditAccount::apply() {
|
||||
FormAccountDetails::apply();
|
||||
|
||||
bool using_another_acc =
|
||||
m_details->m_ui.m_txtUsername->lineEdit()->text() !=account<RedditServiceRoot>()->network()->username();
|
||||
|
||||
// Make sure that the data copied from GUI are used for brand new login.
|
||||
account<RedditServiceRoot>()->network()->oauth()->logout(false);
|
||||
account<RedditServiceRoot>()->network()->oauth()->setClientId(m_details->m_ui.m_txtAppId->lineEdit()->text());
|
||||
account<RedditServiceRoot>()->network()->oauth()->setClientSecret(m_details->m_ui.m_txtAppKey->lineEdit()->text());
|
||||
account<RedditServiceRoot>()->network()->oauth()->setRedirectUrl(m_details->m_ui.m_txtRedirectUrl->lineEdit()->text(),
|
||||
true);
|
||||
|
||||
account<RedditServiceRoot>()->network()->setUsername(m_details->m_ui.m_txtUsername->lineEdit()->text());
|
||||
account<RedditServiceRoot>()->network()->setBatchSize(m_details->m_ui.m_spinLimitMessages->value());
|
||||
account<RedditServiceRoot>()->network()->setDownloadOnlyUnreadMessages(m_details->m_ui.m_cbDownloadOnlyUnreadMessages->isChecked());
|
||||
|
||||
account<RedditServiceRoot>()->saveAccountDataToDatabase();
|
||||
accept();
|
||||
|
||||
if (!m_creatingNew) {
|
||||
if (using_another_acc) {
|
||||
account<RedditServiceRoot>()->completelyRemoveAllData();
|
||||
}
|
||||
|
||||
account<RedditServiceRoot>()->start(true);
|
||||
}
|
||||
}
|
||||
|
||||
void FormEditRedditAccount::loadAccountData() {
|
||||
FormAccountDetails::loadAccountData();
|
||||
|
||||
m_details->m_oauth = account<RedditServiceRoot>()->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<RedditServiceRoot>()->network()->username());
|
||||
m_details->m_ui.m_spinLimitMessages->setValue(account<RedditServiceRoot>()->network()->batchSize());
|
||||
m_details->m_ui.m_cbDownloadOnlyUnreadMessages->setChecked(account<RedditServiceRoot>()->network()->downloadOnlyUnreadMessages());
|
||||
}
|
29
src/librssguard/services/reddit/gui/formeditredditaccount.h
Executable file
29
src/librssguard/services/reddit/gui/formeditredditaccount.h
Executable file
@ -0,0 +1,29 @@
|
||||
// For license of this file, see <project-root-folder>/LICENSE.md.
|
||||
|
||||
#ifndef FORMEDITINOREADERACCOUNT_H
|
||||
#define FORMEDITINOREADERACCOUNT_H
|
||||
|
||||
#include "services/abstract/gui/formaccountdetails.h"
|
||||
|
||||
#include "services/reddit/redditnetworkfactory.h"
|
||||
|
||||
class RedditServiceRoot;
|
||||
class RedditAccountDetails;
|
||||
|
||||
class FormEditRedditAccount : public FormAccountDetails {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit FormEditRedditAccount(QWidget* parent = nullptr);
|
||||
|
||||
protected slots:
|
||||
virtual void apply();
|
||||
|
||||
protected:
|
||||
virtual void loadAccountData();
|
||||
|
||||
private:
|
||||
RedditAccountDetails* m_details;
|
||||
};
|
||||
|
||||
#endif // FORMEDITINOREADERACCOUNT_H
|
124
src/librssguard/services/reddit/gui/redditaccountdetails.cpp
Executable file
124
src/librssguard/services/reddit/gui/redditaccountdetails.cpp
Executable file
@ -0,0 +1,124 @@
|
||||
// For license of this file, see <project-root-folder>/LICENSE.md.
|
||||
|
||||
#include "services/reddit/gui/redditaccountdetails.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/reddit/definitions.h"
|
||||
#include "services/reddit/redditnetworkfactory.h"
|
||||
|
||||
RedditAccountDetails::RedditAccountDetails(QWidget* parent)
|
||||
: QWidget(parent), m_oauth(nullptr), m_lastProxy({}) {
|
||||
m_ui.setupUi(this);
|
||||
|
||||
m_ui.m_lblInfo->setHelpText(tr("You have to fill in your client ID/secret and also fill in correct redirect URL."),
|
||||
true);
|
||||
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_spinLimitMessages);
|
||||
setTabOrder(m_ui.m_spinLimitMessages, m_ui.m_btnTestSetup);
|
||||
|
||||
connect(m_ui.m_txtAppId->lineEdit(), &BaseLineEdit::textChanged, this, &RedditAccountDetails::checkOAuthValue);
|
||||
connect(m_ui.m_txtAppKey->lineEdit(), &BaseLineEdit::textChanged, this, &RedditAccountDetails::checkOAuthValue);
|
||||
connect(m_ui.m_txtRedirectUrl->lineEdit(), &BaseLineEdit::textChanged, this, &RedditAccountDetails::checkOAuthValue);
|
||||
connect(m_ui.m_txtUsername->lineEdit(), &BaseLineEdit::textChanged, this, &RedditAccountDetails::checkUsername);
|
||||
connect(m_ui.m_btnRegisterApi, &QPushButton::clicked, this, &RedditAccountDetails::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 RedditAccountDetails::testSetup(const QNetworkProxy& custom_proxy) {
|
||||
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_lastProxy = custom_proxy;
|
||||
m_oauth->login();
|
||||
}
|
||||
|
||||
void RedditAccountDetails::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 RedditAccountDetails::onAuthFailed() {
|
||||
m_ui.m_lblTestResult->setStatus(WidgetWithStatus::StatusType::Error,
|
||||
tr("You did not grant access."),
|
||||
tr("There was error during testing."));
|
||||
}
|
||||
|
||||
void RedditAccountDetails::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 RedditAccountDetails::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 {
|
||||
RedditNetworkFactory fac;
|
||||
|
||||
fac.setOauth(m_oauth);
|
||||
|
||||
auto resp = fac.me(m_lastProxy);
|
||||
|
||||
m_ui.m_txtUsername->lineEdit()->setText(resp[QSL("name")].toString());
|
||||
}
|
||||
catch (const ApplicationException& ex) {
|
||||
qCriticalNN << LOGSEC_REDDIT
|
||||
<< "Failed to obtain profile with error:"
|
||||
<< QUOTE_W_SPACE_DOT(ex.message());
|
||||
}
|
||||
}
|
||||
|
||||
void RedditAccountDetails::hookNetwork() {
|
||||
connect(m_oauth, &OAuth2Service::tokensRetrieved, this, &RedditAccountDetails::onAuthGranted);
|
||||
connect(m_oauth, &OAuth2Service::tokensRetrieveError, this, &RedditAccountDetails::onAuthError);
|
||||
connect(m_oauth, &OAuth2Service::authFailed, this, &RedditAccountDetails::onAuthFailed);
|
||||
}
|
||||
|
||||
void RedditAccountDetails::registerApi() {
|
||||
qApp->web()->openUrlInExternalBrowser(QSL(REDDIT_REG_API_URL));
|
||||
}
|
||||
|
||||
void RedditAccountDetails::checkOAuthValue(const QString& value) {
|
||||
auto* line_edit = qobject_cast<LineEditWithStatus*>(sender()->parent());
|
||||
|
||||
if (line_edit != nullptr) {
|
||||
if (value.isEmpty()) {
|
||||
#if defined(REDDIT_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."));
|
||||
}
|
||||
}
|
||||
}
|
44
src/librssguard/services/reddit/gui/redditaccountdetails.h
Executable file
44
src/librssguard/services/reddit/gui/redditaccountdetails.h
Executable file
@ -0,0 +1,44 @@
|
||||
// For license of this file, see <project-root-folder>/LICENSE.md.
|
||||
|
||||
#ifndef REDDITACCOUNTDETAILS_H
|
||||
#define REDDITACCOUNTDETAILS_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include "ui_redditaccountdetails.h"
|
||||
|
||||
#include <QNetworkProxy>
|
||||
|
||||
class OAuth2Service;
|
||||
|
||||
class RedditAccountDetails : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
friend class FormEditRedditAccount;
|
||||
|
||||
public:
|
||||
explicit RedditAccountDetails(QWidget* parent = nullptr);
|
||||
|
||||
public slots:
|
||||
void testSetup(const QNetworkProxy& custom_proxy);
|
||||
|
||||
private slots:
|
||||
void registerApi();
|
||||
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::RedditAccountDetails m_ui;
|
||||
|
||||
// Pointer to live OAuth.
|
||||
OAuth2Service* m_oauth;
|
||||
QNetworkProxy m_lastProxy;
|
||||
};
|
||||
|
||||
#endif // REDDITACCOUNTDETAILS_H
|
202
src/librssguard/services/reddit/gui/redditaccountdetails.ui
Executable file
202
src/librssguard/services/reddit/gui/redditaccountdetails.ui
Executable file
@ -0,0 +1,202 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>RedditAccountDetails</class>
|
||||
<widget class="QWidget" name="RedditAccountDetails">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>431</width>
|
||||
<height>259</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>Client 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>Client secret</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 credentials</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="HelpSpoiler" name="m_lblInfo" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<layout class="QFormLayout" name="formLayout_3">
|
||||
<item row="0" column="0">
|
||||
<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 row="0" column="1">
|
||||
<widget class="MessageCountSpinBox" name="m_spinLimitMessages">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>140</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</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>&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>410</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>LineEditWithStatus</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>lineeditwithstatus.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>LabelWithStatus</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>labelwithstatus.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>MessageCountSpinBox</class>
|
||||
<extends>QSpinBox</extends>
|
||||
<header>messagecountspinbox.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>HelpSpoiler</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>helpspoiler.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>m_btnRegisterApi</tabstop>
|
||||
<tabstop>m_cbDownloadOnlyUnreadMessages</tabstop>
|
||||
<tabstop>m_spinLimitMessages</tabstop>
|
||||
<tabstop>m_btnTestSetup</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
45
src/librssguard/services/reddit/redditentrypoint.cpp
Executable file
45
src/librssguard/services/reddit/redditentrypoint.cpp
Executable file
@ -0,0 +1,45 @@
|
||||
// For license of this file, see <project-root-folder>/LICENSE.md.
|
||||
|
||||
#include "services/reddit/redditentrypoint.h"
|
||||
|
||||
#include "database/databasequeries.h"
|
||||
#include "definitions/definitions.h"
|
||||
#include "miscellaneous/application.h"
|
||||
#include "miscellaneous/iconfactory.h"
|
||||
#include "services/reddit/definitions.h"
|
||||
#include "services/reddit/gui/formeditredditaccount.h"
|
||||
#include "services/reddit/redditserviceroot.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
|
||||
ServiceRoot* RedditEntryPoint::createNewRoot() const {
|
||||
FormEditRedditAccount form_acc(qApp->mainFormWidget());
|
||||
|
||||
return form_acc.addEditAccount<RedditServiceRoot>();
|
||||
}
|
||||
|
||||
QList<ServiceRoot*> RedditEntryPoint::initializeSubtree() const {
|
||||
QSqlDatabase database = qApp->database()->driver()->connection(QSL("RedditEntryPoint"));
|
||||
|
||||
return DatabaseQueries::getAccounts<RedditServiceRoot>(database, code());
|
||||
}
|
||||
|
||||
QString RedditEntryPoint::name() const {
|
||||
return QSL("Reddit");
|
||||
}
|
||||
|
||||
QString RedditEntryPoint::code() const {
|
||||
return QSL(SERVICE_CODE_REDDIT);
|
||||
}
|
||||
|
||||
QString RedditEntryPoint::description() const {
|
||||
return QObject::tr("Simplistic Reddit client.");
|
||||
}
|
||||
|
||||
QString RedditEntryPoint::author() const {
|
||||
return QSL(APP_AUTHOR);
|
||||
}
|
||||
|
||||
QIcon RedditEntryPoint::icon() const {
|
||||
return qApp->icons()->miscIcon(QSL("reddit"));
|
||||
}
|
19
src/librssguard/services/reddit/redditentrypoint.h
Executable file
19
src/librssguard/services/reddit/redditentrypoint.h
Executable file
@ -0,0 +1,19 @@
|
||||
// For license of this file, see <project-root-folder>/LICENSE.md.
|
||||
|
||||
#ifndef REDDITENTRYPOINT_H
|
||||
#define REDDITENTRYPOINT_H
|
||||
|
||||
#include "services/abstract/serviceentrypoint.h"
|
||||
|
||||
class RedditEntryPoint : 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 // REDDITENTRYPOINT_H
|
155
src/librssguard/services/reddit/redditnetworkfactory.cpp
Executable file
155
src/librssguard/services/reddit/redditnetworkfactory.cpp
Executable file
@ -0,0 +1,155 @@
|
||||
// For license of this file, see <project-root-folder>/LICENSE.md.
|
||||
|
||||
#include "services/reddit/redditnetworkfactory.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 "miscellaneous/textfactory.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/reddit/definitions.h"
|
||||
#include "services/reddit/redditserviceroot.h"
|
||||
|
||||
#include <QHttpMultiPart>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QRegularExpression>
|
||||
#include <QThread>
|
||||
#include <QUrl>
|
||||
|
||||
RedditNetworkFactory::RedditNetworkFactory(QObject* parent) : QObject(parent),
|
||||
m_service(nullptr), m_username(QString()), m_batchSize(REDDIT_DEFAULT_BATCH_SIZE),
|
||||
m_downloadOnlyUnreadMessages(false),
|
||||
m_oauth2(new OAuth2Service(QSL(REDDIT_OAUTH_AUTH_URL), QSL(REDDIT_OAUTH_TOKEN_URL),
|
||||
{}, {}, QSL(REDDIT_OAUTH_SCOPE), this)) {
|
||||
initializeOauth();
|
||||
}
|
||||
|
||||
void RedditNetworkFactory::setService(RedditServiceRoot* service) {
|
||||
m_service = service;
|
||||
}
|
||||
|
||||
OAuth2Service* RedditNetworkFactory::oauth() const {
|
||||
return m_oauth2;
|
||||
}
|
||||
|
||||
QString RedditNetworkFactory::username() const {
|
||||
return m_username;
|
||||
}
|
||||
|
||||
int RedditNetworkFactory::batchSize() const {
|
||||
return m_batchSize;
|
||||
}
|
||||
|
||||
void RedditNetworkFactory::setBatchSize(int batch_size) {
|
||||
m_batchSize = batch_size;
|
||||
}
|
||||
|
||||
void RedditNetworkFactory::initializeOauth() {
|
||||
m_oauth2->setUseHttpBasicAuthWithClientData(true);
|
||||
m_oauth2->setRedirectUrl(QSL(OAUTH_REDIRECT_URI) +
|
||||
QL1C(':') +
|
||||
QString::number(REDDIT_OAUTH_REDIRECT_URI_PORT),
|
||||
true);
|
||||
|
||||
connect(m_oauth2, &OAuth2Service::tokensRetrieveError, this, &RedditNetworkFactory::onTokensError);
|
||||
connect(m_oauth2, &OAuth2Service::authFailed, this, &RedditNetworkFactory::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 && !refresh_token.isEmpty()) {
|
||||
QSqlDatabase database = qApp->database()->driver()->connection(metaObject()->className());
|
||||
|
||||
DatabaseQueries::storeNewOauthTokens(database, refresh_token, m_service->accountId());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool RedditNetworkFactory::downloadOnlyUnreadMessages() const {
|
||||
return m_downloadOnlyUnreadMessages;
|
||||
}
|
||||
|
||||
void RedditNetworkFactory::setDownloadOnlyUnreadMessages(bool download_only_unread_messages) {
|
||||
m_downloadOnlyUnreadMessages = download_only_unread_messages;
|
||||
}
|
||||
|
||||
void RedditNetworkFactory::setOauth(OAuth2Service* oauth) {
|
||||
m_oauth2 = oauth;
|
||||
}
|
||||
|
||||
void RedditNetworkFactory::setUsername(const QString& username) {
|
||||
m_username = username;
|
||||
}
|
||||
|
||||
QVariantHash RedditNetworkFactory::me(const QNetworkProxy& custom_proxy) {
|
||||
QString bearer = m_oauth2->bearer().toLocal8Bit();
|
||||
|
||||
if (bearer.isEmpty()) {
|
||||
throw ApplicationException(tr("you are not logged in"));
|
||||
}
|
||||
|
||||
QList<QPair<QByteArray, QByteArray>> headers;
|
||||
|
||||
headers.append(QPair<QByteArray, QByteArray>(QSL(HTTP_HEADERS_AUTHORIZATION).toLocal8Bit(),
|
||||
m_oauth2->bearer().toLocal8Bit()));
|
||||
|
||||
int timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt();
|
||||
QByteArray output;
|
||||
auto result = NetworkFactory::performNetworkOperation(QSL(REDDIT_API_GET_PROFILE),
|
||||
timeout,
|
||||
{},
|
||||
output,
|
||||
QNetworkAccessManager::Operation::GetOperation,
|
||||
headers,
|
||||
false,
|
||||
{},
|
||||
{},
|
||||
custom_proxy).first;
|
||||
|
||||
if (result != QNetworkReply::NetworkError::NoError) {
|
||||
throw NetworkException(result, output);
|
||||
}
|
||||
else {
|
||||
QJsonDocument doc = QJsonDocument::fromJson(output);
|
||||
|
||||
return doc.object().toVariantHash();
|
||||
}
|
||||
}
|
||||
|
||||
void RedditNetworkFactory::onTokensError(const QString& error, const QString& error_description) {
|
||||
Q_UNUSED(error)
|
||||
|
||||
qApp->showGuiMessage(Notification::Event::LoginFailure, {
|
||||
tr("Reddit: authentication error"),
|
||||
tr("Click this to login again. Error is: '%1'").arg(error_description),
|
||||
QSystemTrayIcon::MessageIcon::Critical },
|
||||
{}, {
|
||||
tr("Login"),
|
||||
[this]() {
|
||||
m_oauth2->setAccessToken(QString());
|
||||
m_oauth2->setRefreshToken(QString());
|
||||
m_oauth2->login();
|
||||
} });
|
||||
}
|
||||
|
||||
void RedditNetworkFactory::onAuthFailed() {
|
||||
qApp->showGuiMessage(Notification::Event::LoginFailure, {
|
||||
tr("Reddit: authorization denied"),
|
||||
tr("Click this to login again."),
|
||||
QSystemTrayIcon::MessageIcon::Critical },
|
||||
{}, {
|
||||
tr("Login"),
|
||||
[this]() {
|
||||
m_oauth2->login();
|
||||
} });
|
||||
}
|
59
src/librssguard/services/reddit/redditnetworkfactory.h
Executable file
59
src/librssguard/services/reddit/redditnetworkfactory.h
Executable file
@ -0,0 +1,59 @@
|
||||
// For license of this file, see <project-root-folder>/LICENSE.md.
|
||||
|
||||
#ifndef REDDITNETWORKFACTORY_H
|
||||
#define REDDITNETWORKFACTORY_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "core/message.h"
|
||||
|
||||
#include "3rd-party/mimesis/mimesis.hpp"
|
||||
#include "services/abstract/feed.h"
|
||||
#include "services/abstract/rootitem.h"
|
||||
|
||||
#include <QNetworkReply>
|
||||
|
||||
class RootItem;
|
||||
class RedditServiceRoot;
|
||||
class OAuth2Service;
|
||||
class Downloader;
|
||||
|
||||
class RedditNetworkFactory : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit RedditNetworkFactory(QObject* parent = nullptr);
|
||||
|
||||
void setService(RedditServiceRoot* service);
|
||||
|
||||
OAuth2Service* oauth() const;
|
||||
void setOauth(OAuth2Service* oauth);
|
||||
|
||||
QString username() const;
|
||||
void setUsername(const QString& username);
|
||||
|
||||
int batchSize() const;
|
||||
void setBatchSize(int batch_size);
|
||||
|
||||
bool downloadOnlyUnreadMessages() const;
|
||||
void setDownloadOnlyUnreadMessages(bool download_only_unread_messages);
|
||||
|
||||
// API methods.
|
||||
QVariantHash me(const QNetworkProxy& custom_proxy);
|
||||
|
||||
private slots:
|
||||
void onTokensError(const QString& error, const QString& error_description);
|
||||
void onAuthFailed();
|
||||
|
||||
private:
|
||||
void initializeOauth();
|
||||
|
||||
private:
|
||||
RedditServiceRoot* m_service;
|
||||
QString m_username;
|
||||
int m_batchSize;
|
||||
bool m_downloadOnlyUnreadMessages;
|
||||
OAuth2Service* m_oauth2;
|
||||
};
|
||||
|
||||
#endif // REDDITNETWORKFACTORY_H
|
127
src/librssguard/services/reddit/redditserviceroot.cpp
Executable file
127
src/librssguard/services/reddit/redditserviceroot.cpp
Executable file
@ -0,0 +1,127 @@
|
||||
// For license of this file, see <project-root-folder>/LICENSE.md.
|
||||
|
||||
#include "services/reddit/redditserviceroot.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/recyclebin.h"
|
||||
#include "services/reddit/definitions.h"
|
||||
#include "services/reddit/gui/formeditredditaccount.h"
|
||||
#include "services/reddit/redditentrypoint.h"
|
||||
#include "services/reddit/redditnetworkfactory.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
|
||||
RedditServiceRoot::RedditServiceRoot(RootItem* parent)
|
||||
: ServiceRoot(parent), m_network(new RedditNetworkFactory(this)) {
|
||||
m_network->setService(this);
|
||||
setIcon(RedditEntryPoint().icon());
|
||||
}
|
||||
|
||||
void RedditServiceRoot::updateTitle() {
|
||||
setTitle(TextFactory::extractUsernameFromEmail(m_network->username()) + QSL(" (Reddit)"));
|
||||
}
|
||||
|
||||
RootItem* RedditServiceRoot::obtainNewTreeForSyncIn() const {
|
||||
auto* root = new RootItem();
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
QVariantHash RedditServiceRoot::customDatabaseData() const {
|
||||
QVariantHash data;
|
||||
|
||||
data[QSL("username")] = m_network->username();
|
||||
data[QSL("batch_size")] = m_network->batchSize();
|
||||
data[QSL("download_only_unread")] = m_network->downloadOnlyUnreadMessages();
|
||||
data[QSL("client_id")] = m_network->oauth()->clientId();
|
||||
data[QSL("client_secret")] = m_network->oauth()->clientSecret();
|
||||
data[QSL("refresh_token")] = m_network->oauth()->refreshToken();
|
||||
data[QSL("redirect_uri")] = m_network->oauth()->redirectUrl();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void RedditServiceRoot::setCustomDatabaseData(const QVariantHash& data) {
|
||||
m_network->setUsername(data[QSL("username")].toString());
|
||||
m_network->setBatchSize(data[QSL("batch_size")].toInt());
|
||||
m_network->setDownloadOnlyUnreadMessages(data[QSL("download_only_unread")].toBool());
|
||||
m_network->oauth()->setClientId(data[QSL("client_id")].toString());
|
||||
m_network->oauth()->setClientSecret(data[QSL("client_secret")].toString());
|
||||
m_network->oauth()->setRefreshToken(data[QSL("refresh_token")].toString());
|
||||
m_network->oauth()->setRedirectUrl(data[QSL("redirect_uri")].toString(), true);
|
||||
}
|
||||
|
||||
QList<Message> RedditServiceRoot::obtainNewMessages(Feed* feed,
|
||||
const QHash<ServiceRoot::BagOfMessages, QStringList>& stated_messages,
|
||||
const QHash<QString, QStringList>& tagged_messages) {
|
||||
Q_UNUSED(stated_messages)
|
||||
Q_UNUSED(tagged_messages)
|
||||
Q_UNUSED(feed)
|
||||
|
||||
QList<Message> messages;
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
bool RedditServiceRoot::isSyncable() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RedditServiceRoot::canBeEdited() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RedditServiceRoot::editViaGui() {
|
||||
FormEditRedditAccount form_pointer(qApp->mainFormWidget());
|
||||
|
||||
form_pointer.addEditAccount(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RedditServiceRoot::supportsFeedAdding() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RedditServiceRoot::supportsCategoryAdding() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void RedditServiceRoot::start(bool freshly_activated) {
|
||||
if (!freshly_activated) {
|
||||
DatabaseQueries::loadFromDatabase<Category, Feed>(this);
|
||||
loadCacheFromFile();
|
||||
}
|
||||
|
||||
updateTitle();
|
||||
|
||||
/*
|
||||
if (getSubTreeFeeds().isEmpty()) {
|
||||
syncIn();
|
||||
}
|
||||
*/
|
||||
|
||||
m_network->oauth()->login();
|
||||
}
|
||||
|
||||
QString RedditServiceRoot::code() const {
|
||||
return RedditEntryPoint().code();
|
||||
}
|
||||
|
||||
QString RedditServiceRoot::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("-"));
|
||||
}
|
||||
|
||||
void RedditServiceRoot::saveAllCachedData(bool ignore_errors) {
|
||||
Q_UNUSED(ignore_errors)
|
||||
auto msg_cache = takeMessageCache();
|
||||
}
|
53
src/librssguard/services/reddit/redditserviceroot.h
Executable file
53
src/librssguard/services/reddit/redditserviceroot.h
Executable file
@ -0,0 +1,53 @@
|
||||
// For license of this file, see <project-root-folder>/LICENSE.md.
|
||||
|
||||
#ifndef REDDITSERVICEROOT_H
|
||||
#define REDDITSERVICEROOT_H
|
||||
|
||||
#include "services/abstract/cacheforserviceroot.h"
|
||||
#include "services/abstract/serviceroot.h"
|
||||
|
||||
class RedditNetworkFactory;
|
||||
|
||||
class RedditServiceRoot : public ServiceRoot, public CacheForServiceRoot {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit RedditServiceRoot(RootItem* parent = nullptr);
|
||||
|
||||
void setNetwork(RedditNetworkFactory* network);
|
||||
RedditNetworkFactory* network() 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:
|
||||
RedditNetworkFactory* m_network;
|
||||
};
|
||||
|
||||
inline void RedditServiceRoot::setNetwork(RedditNetworkFactory* network) {
|
||||
m_network = network;
|
||||
}
|
||||
|
||||
inline RedditNetworkFactory* RedditServiceRoot::network() const {
|
||||
return m_network;
|
||||
}
|
||||
|
||||
#endif // REDDITSERVICEROOT_H
|
Loading…
x
Reference in New Issue
Block a user