Work on TT-RSS network.
This commit is contained in:
parent
8fbb434a56
commit
32a8fc81cb
BIN
resources/graphics/icons/mini-kfaenza/item-sync.png
Executable file
BIN
resources/graphics/icons/mini-kfaenza/item-sync.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 2.9 KiB |
@ -22,7 +22,7 @@ CREATE TABLE IF NOT EXISTS Accounts (
|
||||
INSERT INTO Accounts (type) VALUES ('std-rss');
|
||||
-- !
|
||||
CREATE TABLE IF NOT EXISTS TtRssAccounts (
|
||||
id INTEGER PRIMARY KEY,
|
||||
id INTEGER,
|
||||
username TEXT NOT NULL,
|
||||
password TEXT,
|
||||
url TEXT NOT NULL,
|
||||
|
@ -16,7 +16,7 @@ CREATE TABLE IF NOT EXISTS Accounts (
|
||||
INSERT INTO Accounts (type) VALUES ('std-rss');
|
||||
-- !
|
||||
CREATE TABLE IF NOT EXISTS TtRssAccounts (
|
||||
id INTEGER PRIMARY KEY,
|
||||
id INTEGER,
|
||||
username TEXT NOT NULL,
|
||||
password TEXT,
|
||||
url TEXT NOT NULL,
|
||||
|
@ -8,7 +8,7 @@ INSERT INTO Accounts (type) VALUES ('std-rss');
|
||||
DROP TABLE IF EXISTS FeedsData;
|
||||
-- !
|
||||
CREATE TABLE IF NOT EXISTS TtRssAccounts (
|
||||
id INTEGER PRIMARY KEY,
|
||||
id INTEGER,
|
||||
username TEXT NOT NULL,
|
||||
password TEXT,
|
||||
url TEXT NOT NULL,
|
||||
|
@ -8,7 +8,7 @@ INSERT INTO Accounts (type) VALUES ('std-rss');
|
||||
DROP TABLE IF EXISTS FeedsData;
|
||||
-- !
|
||||
CREATE TABLE IF NOT EXISTS TtRssAccounts (
|
||||
id INTEGER PRIMARY KEY,
|
||||
id INTEGER,
|
||||
username TEXT NOT NULL,
|
||||
password TEXT,
|
||||
url TEXT NOT NULL,
|
||||
|
@ -42,7 +42,7 @@ RootItem::~RootItem() {
|
||||
qDeleteAll(m_childItems);
|
||||
}
|
||||
|
||||
QList<QAction*> RootItem::contextMenuActions() {
|
||||
QList<QAction*> RootItem::contextMenu() {
|
||||
return QList<QAction*>();
|
||||
}
|
||||
|
||||
|
@ -76,7 +76,7 @@ class RootItem : public QObject {
|
||||
// Returns list of specific actions which can be done with the item.
|
||||
// Do not include general actions here like actions: Mark as read, Update, ...
|
||||
// NOTE: Ownership of returned actions is not switched to caller, free them when needed.
|
||||
virtual QList<QAction*> contextMenuActions();
|
||||
virtual QList<QAction*> contextMenu();
|
||||
|
||||
// Can properties of this item be edited?
|
||||
virtual bool canBeEdited();
|
||||
|
@ -303,7 +303,7 @@ QMenu *FeedsView::initializeContextMenuCategories(RootItem *clicked_item) {
|
||||
m_contextMenuCategories->clear();
|
||||
}
|
||||
|
||||
QList<QAction*> specific_actions = clicked_item->contextMenuActions();
|
||||
QList<QAction*> specific_actions = clicked_item->contextMenu();
|
||||
|
||||
m_contextMenuCategories->addActions(QList<QAction*>() <<
|
||||
qApp->mainForm()->m_ui->m_actionUpdateSelectedItems <<
|
||||
@ -329,7 +329,7 @@ QMenu *FeedsView::initializeContextMenuFeeds(RootItem *clicked_item) {
|
||||
m_contextMenuFeeds->clear();
|
||||
}
|
||||
|
||||
QList<QAction*> specific_actions = clicked_item->contextMenuActions();
|
||||
QList<QAction*> specific_actions = clicked_item->contextMenu();
|
||||
|
||||
m_contextMenuFeeds->addActions(QList<QAction*>() <<
|
||||
qApp->mainForm()->m_ui->m_actionUpdateSelectedItems <<
|
||||
@ -365,7 +365,7 @@ QMenu *FeedsView::initializeContextMenuOtherItem(RootItem *clicked_item) {
|
||||
m_contextMenuOtherItems->clear();
|
||||
}
|
||||
|
||||
QList<QAction*> specific_actions = clicked_item->contextMenuActions();
|
||||
QList<QAction*> specific_actions = clicked_item->contextMenu();
|
||||
|
||||
if (!specific_actions.isEmpty()) {
|
||||
m_contextMenuOtherItems->addSeparator();
|
||||
|
@ -18,6 +18,9 @@
|
||||
#include "services/abstract/serviceroot.h"
|
||||
|
||||
#include "core/feedsmodel.h"
|
||||
#include "miscellaneous/application.h"
|
||||
|
||||
#include <QSqlQuery>
|
||||
|
||||
|
||||
ServiceRoot::ServiceRoot(RootItem *parent) : RootItem(parent), m_accountId(NO_PARENT_CATEGORY) {
|
||||
@ -27,6 +30,34 @@ ServiceRoot::ServiceRoot(RootItem *parent) : RootItem(parent), m_accountId(NO_PA
|
||||
ServiceRoot::~ServiceRoot() {
|
||||
}
|
||||
|
||||
bool ServiceRoot::deleteViaGui() {
|
||||
QSqlDatabase connection = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings);
|
||||
|
||||
// Remove all messages.
|
||||
if (!QSqlQuery(connection).exec(QString("DELETE FROM Messages WHERE account_id = %1;").arg(accountId()))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove all feeds.
|
||||
if (!QSqlQuery(connection).exec(QString("DELETE FROM Feeds WHERE account_id = %1;").arg(accountId()))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove all categories.
|
||||
if (!QSqlQuery(connection).exec(QString("DELETE FROM Categories WHERE account_id = %1;").arg(accountId()))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Switch "existence" flag.
|
||||
bool data_removed = QSqlQuery(connection).exec(QString("DELETE FROM Accounts WHERE id = %1;").arg(accountId()));
|
||||
|
||||
if (data_removed) {
|
||||
requestItemRemoval(this);
|
||||
}
|
||||
|
||||
return data_removed;
|
||||
}
|
||||
|
||||
void ServiceRoot::itemChanged(QList<RootItem*> items) {
|
||||
emit dataChanged(items);
|
||||
}
|
||||
|
@ -44,6 +44,8 @@ class ServiceRoot : public RootItem {
|
||||
// /* Members to override.
|
||||
/////////////////////////////////////////
|
||||
|
||||
bool deleteViaGui();
|
||||
|
||||
// Returns list of specific actions for "Add new item" main window menu.
|
||||
// So typical list of returned actions could look like:
|
||||
// a) Add new feed
|
||||
|
@ -98,7 +98,7 @@ int StandardFeed::countOfUnreadMessages() const {
|
||||
return m_unreadCount;
|
||||
}
|
||||
|
||||
QList<QAction*> StandardFeed::contextMenuActions() {
|
||||
QList<QAction*> StandardFeed::contextMenu() {
|
||||
return serviceRoot()->getContextMenuForFeed(this);
|
||||
}
|
||||
|
||||
|
@ -61,7 +61,7 @@ class StandardFeed : public Feed {
|
||||
int countOfAllMessages() const;
|
||||
int countOfUnreadMessages() const;
|
||||
|
||||
QList<QAction*> contextMenuActions();
|
||||
QList<QAction*> contextMenu();
|
||||
|
||||
bool canBeEdited() {
|
||||
return true;
|
||||
|
@ -109,31 +109,7 @@ bool StandardServiceRoot::canBeDeleted() {
|
||||
}
|
||||
|
||||
bool StandardServiceRoot::deleteViaGui() {
|
||||
QSqlDatabase connection = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings);
|
||||
|
||||
// Remove all messages.
|
||||
if (!QSqlQuery(connection).exec(QString("DELETE FROM Messages WHERE account_id = %1;").arg(accountId()))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove all feeds.
|
||||
if (!QSqlQuery(connection).exec(QString("DELETE FROM Feeds WHERE account_id = %1;").arg(accountId()))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove all categories.
|
||||
if (!QSqlQuery(connection).exec(QString("DELETE FROM Categories WHERE account_id = %1;").arg(accountId()))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Switch "existence" flag.
|
||||
bool data_removed = QSqlQuery(connection).exec(QString("DELETE FROM Accounts WHERE id = %1;").arg(accountId()));
|
||||
|
||||
if (data_removed) {
|
||||
requestItemRemoval(this);
|
||||
}
|
||||
|
||||
return data_removed;
|
||||
return ServiceRoot::deleteViaGui();
|
||||
}
|
||||
|
||||
QVariant StandardServiceRoot::data(int column, int role) const {
|
||||
@ -622,7 +598,7 @@ QList<QAction*> StandardServiceRoot::serviceMenu() {
|
||||
return m_serviceMenu;
|
||||
}
|
||||
|
||||
QList<QAction*> StandardServiceRoot::contextMenuActions() {
|
||||
QList<QAction*> StandardServiceRoot::contextMenu() {
|
||||
return serviceMenu();
|
||||
}
|
||||
|
||||
|
@ -64,7 +64,7 @@ class StandardServiceRoot : public ServiceRoot {
|
||||
// Return menu to be shown in "Services -> service" menu.
|
||||
QList<QAction*> serviceMenu();
|
||||
|
||||
QList<QAction*> contextMenuActions();
|
||||
QList<QAction*> contextMenu();
|
||||
|
||||
// Message stuff.
|
||||
bool loadMessagesForItem(RootItem *item, QSqlTableModel *model);
|
||||
|
@ -23,4 +23,7 @@
|
||||
// Logout.
|
||||
#define LOGOUT_OK "OK"
|
||||
|
||||
// Get feed tree.
|
||||
#define GFT_TYPE_CATEGORY "category"
|
||||
|
||||
#endif // DEFINITIONS_H
|
||||
|
@ -18,12 +18,13 @@
|
||||
#include "services/tt-rss/network/ttrssnetworkfactory.h"
|
||||
|
||||
#include "definitions/definitions.h"
|
||||
#include "core/rootitem.h"
|
||||
#include "services/tt-rss/definitions.h"
|
||||
#include "network-web/networkfactory.h"
|
||||
|
||||
|
||||
TtRssNetworkFactory::TtRssNetworkFactory()
|
||||
: m_url(QString()), m_username(QString()), m_password(QString()), m_session_Id(QString()) {
|
||||
: m_url(QString()), m_username(QString()), m_password(QString()), m_sessionId(QString()) {
|
||||
}
|
||||
|
||||
TtRssNetworkFactory::~TtRssNetworkFactory() {
|
||||
@ -63,22 +64,56 @@ void TtRssNetworkFactory::setPassword(const QString &password) {
|
||||
|
||||
|
||||
LoginResult TtRssNetworkFactory::login() {
|
||||
if (!m_sessionId.isEmpty()) {
|
||||
logout();
|
||||
}
|
||||
|
||||
QtJson::JsonObject json;
|
||||
json["op"] = "login";
|
||||
json["user"] = m_username;
|
||||
json["password"] = m_password;
|
||||
|
||||
QByteArray result_raw;
|
||||
NetworkResult res = NetworkFactory::uploadData(m_url, DOWNLOAD_TIMEOUT, QtJson::serialize(json), CONTENT_TYPE, result_raw);
|
||||
NetworkResult network_reply = NetworkFactory::uploadData(m_url, DOWNLOAD_TIMEOUT, QtJson::serialize(json), CONTENT_TYPE, result_raw);
|
||||
LoginResult result(network_reply.first, TtRssLoginResponse(QString::fromUtf8(result_raw)));
|
||||
|
||||
if (res.first != QNetworkReply::NoError) {
|
||||
return LoginResult(res.first, TtRssLoginResponse());
|
||||
if (network_reply.first == QNetworkReply::NoError) {
|
||||
m_sessionId = result.second.sessionId();
|
||||
}
|
||||
else {
|
||||
LoginResult result(res.first, TtRssLoginResponse(QString::fromUtf8(result_raw)));
|
||||
m_session_Id = result.second.sessionId();
|
||||
return result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
LogoutResult TtRssNetworkFactory::logout() {
|
||||
QtJson::JsonObject json;
|
||||
json["op"] = "logout";
|
||||
json["sid"] = m_sessionId;
|
||||
|
||||
QByteArray result_raw;
|
||||
NetworkResult network_reply = NetworkFactory::uploadData(m_url, DOWNLOAD_TIMEOUT, QtJson::serialize(json), CONTENT_TYPE, result_raw);
|
||||
|
||||
return LogoutResult(network_reply.first, TtRssResponse(QString::fromUtf8(result_raw)));
|
||||
}
|
||||
|
||||
GetFeedTreeResult TtRssNetworkFactory::getFeedTree() {
|
||||
QtJson::JsonObject json;
|
||||
json["op"] = "getFeedTree";
|
||||
json["sid"] = m_sessionId;
|
||||
json["include_empty"] = true;
|
||||
|
||||
QByteArray result_raw;
|
||||
NetworkResult network_reply = NetworkFactory::uploadData(m_url, DOWNLOAD_TIMEOUT, QtJson::serialize(json), CONTENT_TYPE, result_raw);
|
||||
GetFeedTreeResult result(network_reply.first, TtRssGetFeedTreeResponse(QString::fromUtf8(result_raw)));
|
||||
|
||||
if (result.second.isNotLoggedIn()) {
|
||||
// We are not logged in.
|
||||
login();
|
||||
|
||||
network_reply = NetworkFactory::uploadData(m_url, DOWNLOAD_TIMEOUT, QtJson::serialize(json), CONTENT_TYPE, result_raw);
|
||||
result = GetFeedTreeResult(network_reply.first, TtRssGetFeedTreeResponse(QString::fromUtf8(result_raw)));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
TtRssResponse::TtRssResponse(const QString &raw_content) {
|
||||
@ -110,6 +145,10 @@ int TtRssResponse::status() const {
|
||||
}
|
||||
}
|
||||
|
||||
bool TtRssResponse::isNotLoggedIn() const {
|
||||
return status() == API_STATUS_ERR && hasError() && error() == NOT_LOGGED_IN;
|
||||
}
|
||||
|
||||
|
||||
TtRssLoginResponse::TtRssLoginResponse(const QString &raw_content) : TtRssResponse(raw_content) {
|
||||
}
|
||||
@ -135,7 +174,7 @@ QString TtRssLoginResponse::sessionId() const {
|
||||
}
|
||||
}
|
||||
|
||||
QString TtRssLoginResponse::error() const {
|
||||
QString TtRssResponse::error() const {
|
||||
if (!isLoaded()) {
|
||||
return QString();
|
||||
}
|
||||
@ -144,7 +183,7 @@ QString TtRssLoginResponse::error() const {
|
||||
}
|
||||
}
|
||||
|
||||
bool TtRssLoginResponse::hasError() const {
|
||||
bool TtRssResponse::hasError() const {
|
||||
if (!isLoaded()) {
|
||||
return false;
|
||||
}
|
||||
@ -152,3 +191,58 @@ bool TtRssLoginResponse::hasError() const {
|
||||
return m_rawContent["content"].toMap().contains("error");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TtRssGetFeedTreeResponse::TtRssGetFeedTreeResponse(const QString &raw_content) : TtRssResponse(raw_content) {
|
||||
|
||||
}
|
||||
|
||||
TtRssGetFeedTreeResponse::~TtRssGetFeedTreeResponse() {
|
||||
}
|
||||
|
||||
QList<RootItem*> TtRssGetFeedTreeResponse::getTree() {
|
||||
QList<RootItem*> items;
|
||||
|
||||
if (status() == API_STATUS_OK) {
|
||||
// We have data, construct object tree according to data.
|
||||
QList<QVariant> items_to_process = m_rawContent["content"].toMap()["categories"].toMap()["items"].toList();
|
||||
|
||||
processSubtree(true, items, NULL, items_to_process);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
void TtRssGetFeedTreeResponse::processSubtree(bool is_top_level, QList<RootItem *> &top_level_items,
|
||||
RootItem *parent, const QList<QVariant> &items) {
|
||||
foreach (QVariant item, items) {
|
||||
QMap<QString,QVariant> map_item = item.toMap();
|
||||
|
||||
if (map_item.contains("type") && map_item["type"].toString() == GFT_TYPE_CATEGORY) {
|
||||
// TODO: pokračovat tady
|
||||
|
||||
// We have category, create it, add it to "parent".
|
||||
// Then process all its children.
|
||||
//
|
||||
// TtRssCategory *new_category = new TtRssCategory();
|
||||
// naplnit informace.....
|
||||
// parent->appendChild(new_category);
|
||||
// if (is_top_level) {
|
||||
// top_level_items.append(new_category);
|
||||
// }
|
||||
// else {
|
||||
// parent->appendChild(new_category);
|
||||
// }
|
||||
// processSubtree(false, top_level_items, new_category, map_item["items"].toList());
|
||||
}
|
||||
else {
|
||||
// We have feed, add it.
|
||||
// TtRssFeed *new_feed = new TtRssFeed();
|
||||
// naplnit informace.....
|
||||
// parent->appendChild(new_feed);
|
||||
// if (is_top_level) {
|
||||
// top_level_items.append(new_feed);
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,8 @@
|
||||
#include <QNetworkReply>
|
||||
|
||||
|
||||
class RootItem;
|
||||
|
||||
class TtRssResponse {
|
||||
public:
|
||||
explicit TtRssResponse(const QString &raw_content = QString());
|
||||
@ -34,6 +36,9 @@ class TtRssResponse {
|
||||
|
||||
int seq() const;
|
||||
int status() const;
|
||||
QString error() const;
|
||||
bool hasError() const;
|
||||
bool isNotLoggedIn() const;
|
||||
|
||||
protected:
|
||||
QtJson::JsonObject m_rawContent;
|
||||
@ -46,11 +51,24 @@ class TtRssLoginResponse : public TtRssResponse {
|
||||
|
||||
int apiLevel() const;
|
||||
QString sessionId() const;
|
||||
QString error() const;
|
||||
bool hasError() const;
|
||||
};
|
||||
|
||||
typedef QPair<QNetworkReply::NetworkError,TtRssLoginResponse> LoginResult;
|
||||
typedef QPair<QNetworkReply::NetworkError,TtRssResponse> LogoutResult;
|
||||
|
||||
class TtRssGetFeedTreeResponse : public TtRssResponse {
|
||||
public:
|
||||
explicit TtRssGetFeedTreeResponse(const QString &raw_content = QString());
|
||||
virtual ~TtRssGetFeedTreeResponse();
|
||||
|
||||
QList<RootItem*> getTree();
|
||||
|
||||
private:
|
||||
void processSubtree(bool is_top_level, QList<RootItem*> &top_level_items,
|
||||
RootItem *parent, const QList<QVariant> &items);
|
||||
};
|
||||
|
||||
typedef QPair<QNetworkReply::NetworkError,TtRssGetFeedTreeResponse> GetFeedTreeResult;
|
||||
|
||||
class TtRssNetworkFactory {
|
||||
public:
|
||||
@ -67,13 +85,21 @@ class TtRssNetworkFactory {
|
||||
void setPassword(const QString &password);
|
||||
|
||||
// Operations.
|
||||
|
||||
// Logs user in.
|
||||
LoginResult login();
|
||||
|
||||
private:
|
||||
// Logs user out.
|
||||
LogoutResult logout();
|
||||
|
||||
// Gets tree from feeds/categories obtained from the server.
|
||||
GetFeedTreeResult getFeedTree();
|
||||
|
||||
private:
|
||||
QString m_url;
|
||||
QString m_username;
|
||||
QString m_password;
|
||||
QString m_session_Id;
|
||||
QString m_sessionId;
|
||||
};
|
||||
|
||||
#endif // TTRSSNETWORKFACTORY_H
|
||||
|
@ -30,7 +30,7 @@
|
||||
|
||||
|
||||
TtRssServiceRoot::TtRssServiceRoot(RootItem *parent)
|
||||
: ServiceRoot(parent), m_network(new TtRssNetworkFactory) {
|
||||
: ServiceRoot(parent), m_network(new TtRssNetworkFactory), m_actionSyncIn(NULL), m_serviceMenu(QList<QAction*>()) {
|
||||
setIcon(TtRssServiceEntryPoint().icon());
|
||||
setCreationDate(QDateTime::currentDateTime());
|
||||
}
|
||||
@ -63,7 +63,16 @@ bool TtRssServiceRoot::editViaGui() {
|
||||
}
|
||||
|
||||
bool TtRssServiceRoot::deleteViaGui() {
|
||||
return false;
|
||||
QSqlDatabase connection = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings);
|
||||
|
||||
// Remove extra entry in "Tiny Tiny RSS accounts list" and then delete
|
||||
// all the categories/feeds and messages.
|
||||
if (!QSqlQuery(connection).exec(QString("DELETE FROM TtRssAccounts WHERE id = %1;").arg(accountId()))) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return ServiceRoot::deleteViaGui();
|
||||
}
|
||||
}
|
||||
|
||||
bool TtRssServiceRoot::canBeEdited() {
|
||||
@ -105,7 +114,19 @@ bool TtRssServiceRoot::loadMessagesForItem(RootItem *item, QSqlTableModel *model
|
||||
}
|
||||
|
||||
QList<QAction*> TtRssServiceRoot::serviceMenu() {
|
||||
return QList<QAction*>();
|
||||
if (m_serviceMenu.isEmpty()) {
|
||||
m_actionSyncIn = new QAction(qApp->icons()->fromTheme(QSL("item-sync")), tr("Sync in"), this);
|
||||
|
||||
connect(m_actionSyncIn, SIGNAL(triggered()), this, SLOT(syncIn()));
|
||||
|
||||
m_serviceMenu.append(m_actionSyncIn);
|
||||
}
|
||||
|
||||
return m_serviceMenu;
|
||||
}
|
||||
|
||||
QList<QAction*> TtRssServiceRoot::contextMenu() {
|
||||
return serviceMenu();
|
||||
}
|
||||
|
||||
bool TtRssServiceRoot::onBeforeSetMessagesRead(RootItem *selected_item, QList<int> message_db_ids, RootItem::ReadStatus read) {
|
||||
@ -207,4 +228,5 @@ void TtRssServiceRoot::syncIn() {
|
||||
// TODO: provede stažení kanálů/kategorií
|
||||
// ze serveru, a sloučení s aktuálními
|
||||
// neprovádí aktualizace kanálů ani stažení počtu nepřečtených zpráv
|
||||
m_network->getFeedTree();
|
||||
}
|
||||
|
@ -44,8 +44,9 @@ class TtRssServiceRoot : public ServiceRoot {
|
||||
|
||||
QVariant data(int column, int role) const;
|
||||
|
||||
QList<QAction *> addItemMenu();
|
||||
QList<QAction *> serviceMenu();
|
||||
QList<QAction*> addItemMenu();
|
||||
QList<QAction*> serviceMenu();
|
||||
QList<QAction*> contextMenu();
|
||||
|
||||
RecycleBin *recycleBin();
|
||||
|
||||
@ -69,9 +70,13 @@ class TtRssServiceRoot : public ServiceRoot {
|
||||
void loadFromDatabase();
|
||||
void updateTitle();
|
||||
|
||||
private:
|
||||
private slots:
|
||||
void syncIn();
|
||||
|
||||
private:
|
||||
QAction *m_actionSyncIn;
|
||||
QList<QAction*> m_serviceMenu;
|
||||
|
||||
TtRssNetworkFactory *m_network;
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user