630 changes: 391 additions & 239 deletions src/librssguard/services/tt-rss/ttrssnetworkfactory.cpp

Large diffs are not rendered by default.

72 changes: 52 additions & 20 deletions src/librssguard/services/tt-rss/ttrssnetworkfactory.h
Expand Up @@ -60,8 +60,10 @@ class TtRssGetFeedsCategoriesResponse : public TtRssResponse {
// 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(TtRssNetworkFactory* network, bool obtain_icons,
const QNetworkProxy& proxy, const QString& base_address = QString()) const;
RootItem* feedsCategories(TtRssNetworkFactory* network,
bool obtain_icons,
const QNetworkProxy& proxy,
const QString& base_address = QString()) const;
};

class ServiceRoot;
Expand All @@ -74,6 +76,22 @@ class TtRssGetHeadlinesResponse : public TtRssResponse {
QList<Message> messages(ServiceRoot* root) const;
};

class TtRssGetArticleResponse : public TtRssResponse {
public:
explicit TtRssGetArticleResponse(const QString& raw_content = QString());
virtual ~TtRssGetArticleResponse();

QList<Message> messages(ServiceRoot* root) const;
};

class TtRssGetCompactHeadlinesResponse : public TtRssResponse {
public:
explicit TtRssGetCompactHeadlinesResponse(const QString& raw_content = QString());
virtual ~TtRssGetCompactHeadlinesResponse();

QStringList ids() const;
};

class TtRssUpdateArticleResponse : public TtRssResponse {
public:
explicit TtRssUpdateArticleResponse(const QString& raw_content = QString());
Expand All @@ -100,19 +118,11 @@ class TtRssUnsubscribeFeedResponse : public TtRssResponse {
};

namespace UpdateArticle {
enum class Mode {
SetToFalse = 0,
SetToTrue = 1,
Togggle = 2
};
enum class Mode { SetToFalse = 0, SetToTrue = 1, Togggle = 2 };

enum class OperatingField {
Starred = 0,
Published = 1,
Unread = 2
};
enum class OperatingField { Starred = 0, Published = 1, Unread = 2 };

}
} // namespace UpdateArticle

class TtRssNetworkFactory {
public:
Expand Down Expand Up @@ -163,29 +173,50 @@ class TtRssNetworkFactory {
// Gets feeds from the server.
TtRssGetFeedsCategoriesResponse getFeedsCategories(const QNetworkProxy& proxy);

// Gets message IDs from the server.
TtRssGetCompactHeadlinesResponse getCompactHeadlines(int feed_id,
int limit,
int skip,
const QString& view_mode,
const QNetworkProxy& proxy);

TtRssGetHeadlinesResponse getArticle(const QStringList& article_ids, const QNetworkProxy& proxy);

// Gets headlines (messages) from the server.
TtRssGetHeadlinesResponse getHeadlines(int feed_id, int limit, int skip,
bool show_content, bool include_attachments,
bool sanitize, bool unread_only,
TtRssGetHeadlinesResponse getHeadlines(int feed_id,
int limit,
int skip,
bool show_content,
bool include_attachments,
bool sanitize,
bool unread_only,
const QNetworkProxy& proxy);

TtRssResponse setArticleLabel(const QStringList& article_ids, const QString& label_custom_id,
bool assign, const QNetworkProxy& proxy);
TtRssResponse setArticleLabel(const QStringList& article_ids,
const QString& label_custom_id,
bool assign,
const QNetworkProxy& proxy);

TtRssUpdateArticleResponse updateArticles(const QStringList& ids,
UpdateArticle::OperatingField field,
UpdateArticle::Mode mode,
const QNetworkProxy& proxy);

TtRssSubscribeToFeedResponse subscribeToFeed(const QString& url, int category_id, const QNetworkProxy& proxy,
bool protectd = false, const QString& username = QString(),
TtRssSubscribeToFeedResponse subscribeToFeed(const QString& url,
int category_id,
const QNetworkProxy& proxy,
bool protectd = false,
const QString& username = QString(),
const QString& password = QString());

TtRssUnsubscribeFeedResponse unsubscribeFeed(int feed_id, const QNetworkProxy& proxy);

int batchSize() const;
void setBatchSize(int batch_size);

bool intelligentSynchronization() const;
void setIntelligentSynchronization(bool intelligent_synchronization);

private:
QString m_bareUrl;
QString m_fullUrl;
Expand All @@ -194,6 +225,7 @@ class TtRssNetworkFactory {
int m_batchSize;
bool m_forceServerSideUpdate;
bool m_downloadOnlyUnreadMessages;
bool m_intelligentSynchronization;
bool m_authIsUsed;
QString m_authUsername;
QString m_authPassword;
Expand Down
79 changes: 78 additions & 1 deletion src/librssguard/services/tt-rss/ttrssserviceroot.cpp
Expand Up @@ -235,6 +235,7 @@ QVariantHash TtRssServiceRoot::customDatabaseData() const {
data[QSL("force_update")] = m_network->forceServerSideUpdate();
data[QSL("batch_size")] = m_network->batchSize();
data[QSL("download_only_unread")] = m_network->downloadOnlyUnreadMessages();
data[QSL("intelligent_synchronization")] = m_network->intelligentSynchronization();

return data;
}
Expand All @@ -249,15 +250,87 @@ void TtRssServiceRoot::setCustomDatabaseData(const QVariantHash& data) {
m_network->setForceServerSideUpdate(data[QSL("force_update")].toBool());
m_network->setBatchSize(data[QSL("batch_size")].toInt());
m_network->setDownloadOnlyUnreadMessages(data[QSL("download_only_unread")].toBool());
m_network->setIntelligentSynchronization(data[QSL("intelligent_synchronization")].toBool());
}

QList<Message> TtRssServiceRoot::obtainNewMessages(Feed* feed,
const QHash<ServiceRoot::BagOfMessages, QStringList>&
stated_messages,
const QHash<QString, QStringList>& tagged_messages) {
Q_UNUSED(stated_messages)
Q_UNUSED(tagged_messages)

if (m_network->intelligentSynchronization()) {
return obtainMessagesIntelligently(feed, stated_messages);
}
else {
return obtainMessagesViaHeadlines(feed);
}
}

QList<Message> TtRssServiceRoot::obtainMessagesIntelligently(Feed* feed,
const QHash<BagOfMessages, QStringList>& stated_messages) {
// 1. Get unread IDs for a feed.
// 2. Get read IDs for a feed.
// 3. Get starred IDs for a feed.
// 4. Determine IDs needed to download.
// 5. Download needed articles.
const QStringList remote_all_ids_list =
m_network->downloadOnlyUnreadMessages()
? QStringList()
: m_network->getCompactHeadlines(feed->customNumericId(), 1000000, 0, QSL("all_articles"), networkProxy()).ids();
const QStringList remote_unread_ids_list =
m_network->getCompactHeadlines(feed->customNumericId(), 1000000, 0, QSL("unread"), networkProxy()).ids();
const QStringList remote_starred_ids_list =
m_network->getCompactHeadlines(feed->customNumericId(), 1000000, 0, QSL("marked"), networkProxy()).ids();

const QSet<QString> remote_all_ids = FROM_LIST_TO_SET(QSet<QString>, remote_all_ids_list);

// 1.
auto local_unread_ids_list = stated_messages.value(ServiceRoot::BagOfMessages::Unread);
const QSet<QString> remote_unread_ids = FROM_LIST_TO_SET(QSet<QString>, remote_unread_ids_list);
const QSet<QString> local_unread_ids = FROM_LIST_TO_SET(QSet<QString>, local_unread_ids_list);

// 2.
const auto local_read_ids_list = stated_messages.value(ServiceRoot::BagOfMessages::Read);
const QSet<QString> remote_read_ids = remote_all_ids - remote_unread_ids;
const QSet<QString> local_read_ids = FROM_LIST_TO_SET(QSet<QString>, local_read_ids_list);

// 3.
const auto local_starred_ids_list = stated_messages.value(ServiceRoot::BagOfMessages::Starred);
const QSet<QString> remote_starred_ids = FROM_LIST_TO_SET(QSet<QString>, remote_starred_ids_list);
const QSet<QString> local_starred_ids = FROM_LIST_TO_SET(QSet<QString>, local_starred_ids_list);

// 4.
QSet<QString> to_download;

if (!m_network->downloadOnlyUnreadMessages()) {
to_download += remote_all_ids - local_read_ids - local_unread_ids;
}
else {
to_download += remote_unread_ids - local_read_ids - local_unread_ids;
}

auto moved_read = local_read_ids & remote_unread_ids;

to_download += moved_read;

if (!m_network->downloadOnlyUnreadMessages()) {
auto moved_unread = local_unread_ids & remote_read_ids;

to_download += moved_unread;
}

auto moved_starred = (local_starred_ids + remote_starred_ids) - (local_starred_ids & remote_starred_ids);

to_download += moved_starred;

// 5.
auto msgs = m_network->getArticle(to_download.values(), networkProxy());

return msgs.messages(this);
}

QList<Message> TtRssServiceRoot::obtainMessagesViaHeadlines(Feed* feed) {
QList<Message> messages;
int newly_added_messages = 0;
int limit = network()->batchSize() <= 0 ? TTRSS_MAX_MESSAGES : network()->batchSize();
Expand Down Expand Up @@ -336,3 +409,7 @@ RootItem* TtRssServiceRoot::obtainNewTreeForSyncIn() const {
throw NetworkException(lst_error, tr("cannot get list of feeds, network error '%1'").arg(lst_error));
}
}

bool TtRssServiceRoot::wantsBaggedIdsOfExistingMessages() const {
return m_network->intelligentSynchronization();
}
6 changes: 5 additions & 1 deletion src/librssguard/services/tt-rss/ttrssserviceroot.h
Expand Up @@ -13,12 +13,13 @@ class TtRssFeed;
class TtRssNetworkFactory;

class TtRssServiceRoot : public ServiceRoot, public CacheForServiceRoot {
Q_OBJECT
Q_OBJECT

public:
explicit TtRssServiceRoot(RootItem* parent = nullptr);
virtual ~TtRssServiceRoot();

virtual bool wantsBaggedIdsOfExistingMessages() const;
virtual LabelOperation supportedLabelOperations() const;
virtual void start(bool freshly_activated);
virtual void stop();
Expand Down Expand Up @@ -48,6 +49,9 @@ class TtRssServiceRoot : public ServiceRoot, public CacheForServiceRoot {

private:
void updateTitle();
QList<Message> obtainMessagesIntelligently(Feed* feed,
const QHash<ServiceRoot::BagOfMessages, QStringList>& stated_messages);
QList<Message> obtainMessagesViaHeadlines(Feed* feed);

private:
TtRssNetworkFactory* m_network;
Expand Down