From 683517948d84c229ba2201dfaf82aae03daabfeb Mon Sep 17 00:00:00 2001 From: Martin Rotter Date: Tue, 8 Dec 2015 09:11:55 +0100 Subject: [PATCH] Many changes, working Sync in, fixed some problems with item expanding, items now can inform model that it wants some items expanded or not. --- resources/misc/db_init_mysql.sql | 10 +- resources/misc/db_init_sqlite.sql | 10 +- resources/misc/db_update_mysql_3_4.sql | 15 +++ resources/misc/db_update_sqlite_3_4.sql | 50 ++++++++ src/core/feedsmodel.cpp | 1 + src/core/feedsmodel.h | 3 + src/gui/feedsview.cpp | 19 +++- src/gui/feedsview.h | 1 + src/services/abstract/serviceroot.cpp | 6 +- src/services/abstract/serviceroot.h | 5 +- .../standard/standardserviceentrypoint.cpp | 2 - src/services/standard/standardserviceroot.cpp | 2 + src/services/tt-rss/gui/formeditaccount.cpp | 5 +- .../tt-rss/network/ttrssnetworkfactory.cpp | 102 ++++++++++------- .../tt-rss/network/ttrssnetworkfactory.h | 5 +- .../tt-rss/ttrssserviceentrypoint.cpp | 1 - src/services/tt-rss/ttrssserviceroot.cpp | 107 +++++++++++++++++- src/services/tt-rss/ttrssserviceroot.h | 8 +- 18 files changed, 282 insertions(+), 70 deletions(-) diff --git a/resources/misc/db_init_mysql.sql b/resources/misc/db_init_mysql.sql index b3eb5e2cd..d1bbd370d 100644 --- a/resources/misc/db_init_mysql.sql +++ b/resources/misc/db_init_mysql.sql @@ -36,7 +36,7 @@ CREATE TABLE IF NOT EXISTS Categories ( parent_id INTEGER NOT NULL, title VARCHAR(100) NOT NULL CHECK (title != ''), description TEXT, - date_created BIGINT NOT NULL CHECK (date_created != 0), + date_created BIGINT, icon BLOB, account_id INTEGER NOT NULL, custom_id TEXT, @@ -50,17 +50,17 @@ CREATE TABLE IF NOT EXISTS Feeds ( id INTEGER AUTO_INCREMENT PRIMARY KEY, title TEXT NOT NULL CHECK (title != ''), description TEXT, - date_created BIGINT NOT NULL CHECK (date_created != 0), + date_created BIGINT, icon BLOB, category INTEGER NOT NULL CHECK (category >= -1), - encoding TEXT NOT NULL CHECK (encoding != ''), - url VARCHAR(100) NOT NULL CHECK (url != ''), + encoding TEXT, + url VARCHAR(100), protected INTEGER(1) NOT NULL CHECK (protected >= 0 AND protected <= 1), username TEXT, password TEXT, update_type INTEGER(1) NOT NULL CHECK (update_type >= 0), update_interval INTEGER NOT NULL DEFAULT 15 CHECK (update_interval >= 5), - type INTEGER NOT NULL CHECK (type >= 0), + type INTEGER, account_id INTEGER NOT NULL, custom_id TEXT, diff --git a/resources/misc/db_init_sqlite.sql b/resources/misc/db_init_sqlite.sql index 80a091205..a0e357296 100644 --- a/resources/misc/db_init_sqlite.sql +++ b/resources/misc/db_init_sqlite.sql @@ -31,7 +31,7 @@ CREATE TABLE IF NOT EXISTS Categories ( parent_id INTEGER NOT NULL, title TEXT NOT NULL CHECK (title != ''), description TEXT, - date_created INTEGER NOT NULL CHECK (date_created != 0), + date_created INTEGER, icon BLOB, account_id INTEGER NOT NULL, custom_id TEXT, @@ -45,17 +45,17 @@ CREATE TABLE IF NOT EXISTS Feeds ( id INTEGER PRIMARY KEY, title TEXT NOT NULL CHECK (title != ''), description TEXT, - date_created INTEGER NOT NULL CHECK (date_created != 0), + date_created INTEGER, icon BLOB, category INTEGER NOT NULL CHECK (category >= -1), - encoding TEXT NOT NULL CHECK (encoding != ''), - url TEXT NOT NULL CHECK (url != ''), + encoding TEXT, + url TEXT, protected INTEGER(1) NOT NULL CHECK (protected >= 0 AND protected <= 1), username TEXT, password TEXT, update_type INTEGER(1) NOT NULL CHECK (update_type >= 0), update_interval INTEGER NOT NULL CHECK (update_interval >= 5) DEFAULT 15, - type INTEGER NOT NULL CHECK (type >= 0), + type INTEGER, account_id INTEGER NOT NULL, custom_id TEXT, diff --git a/resources/misc/db_update_mysql_3_4.sql b/resources/misc/db_update_mysql_3_4.sql index 892d54579..25038fd1e 100644 --- a/resources/misc/db_update_mysql_3_4.sql +++ b/resources/misc/db_update_mysql_3_4.sql @@ -40,4 +40,19 @@ DROP FOREIGN KEY feed; ALTER TABLE Messages MODIFY Feeds TEXT; -- ! +ALTER TABLE Feeds +MODIFY date_created BIGINT; +-- ! +ALTER TABLE Feeds +MODIFY encoding TEXT; +-- ! +ALTER TABLE Feeds +MODIFY url VARCHAR(100); +-- ! +ALTER TABLE Feeds +MODIFY type INTEGER; +-- ! +ALTER TABLE Categories +MODIFY date_created BIGINT; +-- ! UPDATE Information SET inf_value = '4' WHERE inf_key = 'schema_version'; \ No newline at end of file diff --git a/resources/misc/db_update_sqlite_3_4.sql b/resources/misc/db_update_sqlite_3_4.sql index 6a6433312..6765c6491 100644 --- a/resources/misc/db_update_sqlite_3_4.sql +++ b/resources/misc/db_update_sqlite_3_4.sql @@ -61,4 +61,54 @@ INSERT INTO Messages SELECT * FROM backup_Messages; -- ! DROP TABLE backup_Messages; -- ! +CREATE TABLE backup_Feeds AS SELECT * FROM Feeds; +-- ! +DROP TABLE Feeds; +-- ! +CREATE TABLE Feeds ( + id INTEGER PRIMARY KEY, + title TEXT NOT NULL CHECK (title != ''), + description TEXT, + date_created INTEGER, + icon BLOB, + category INTEGER NOT NULL CHECK (category >= -1), + encoding TEXT, + url TEXT, + protected INTEGER(1) NOT NULL CHECK (protected >= 0 AND protected <= 1), + username TEXT, + password TEXT, + update_type INTEGER(1) NOT NULL CHECK (update_type >= 0), + update_interval INTEGER NOT NULL CHECK (update_interval >= 5) DEFAULT 15, + type INTEGER, + account_id INTEGER NOT NULL, + custom_id TEXT, + + FOREIGN KEY (account_id) REFERENCES Accounts (id) +); +-- ! +INSERT INTO Feeds SELECT * FROM backup_Feeds; +-- ! +DROP TABLE backup_Feeds; +-- ! +CREATE TABLE backup_Categories AS SELECT * FROM Categories; +-- ! +DROP TABLE Categories; +-- ! +CREATE TABLE Categories ( + id INTEGER PRIMARY KEY, + parent_id INTEGER NOT NULL, + title TEXT NOT NULL CHECK (title != ''), + description TEXT, + date_created INTEGER, + icon BLOB, + account_id INTEGER NOT NULL, + custom_id TEXT, + + FOREIGN KEY (account_id) REFERENCES Accounts (id) +); +-- ! +INSERT INTO Categories SELECT * FROM backup_Categories; +-- ! +DROP TABLE backup_Categories; +-- ! UPDATE Information SET inf_value = '4' WHERE inf_key = 'schema_version'; \ No newline at end of file diff --git a/src/core/feedsmodel.cpp b/src/core/feedsmodel.cpp index ce322eb5e..b6040476e 100755 --- a/src/core/feedsmodel.cpp +++ b/src/core/feedsmodel.cpp @@ -697,6 +697,7 @@ bool FeedsModel::addServiceAccount(ServiceRoot *root) { connect(root, SIGNAL(readFeedsFilterInvalidationRequested()), this, SIGNAL(readFeedsFilterInvalidationRequested())); connect(root, SIGNAL(dataChanged(QList)), this, SLOT(onItemDataChanged(QList))); connect(root, SIGNAL(reloadMessageListRequested(bool)), this, SIGNAL(reloadMessageListRequested(bool))); + connect(root, SIGNAL(itemExpandRequested(QList,bool)), this, SIGNAL(itemExpandRequested(QList,bool))); root->start(); return true; diff --git a/src/core/feedsmodel.h b/src/core/feedsmodel.h index 83df548e5..1e94efc93 100755 --- a/src/core/feedsmodel.h +++ b/src/core/feedsmodel.h @@ -206,6 +206,9 @@ class FeedsModel : public QAbstractItemModel { // Emitted if counts of messages are changed. void messageCountsChanged(int unread_messages, int total_messages, bool any_feed_has_unread_messages); + // Emitted if any item requested that any view should expand it. + void itemExpandRequested(QList items, bool expand); + // Emitted when there is a need of reloading of displayed messages. void reloadMessageListRequested(bool mark_selected_messages_read); diff --git a/src/gui/feedsview.cpp b/src/gui/feedsview.cpp index d976b6e84..39a48d211 100755 --- a/src/gui/feedsview.cpp +++ b/src/gui/feedsview.cpp @@ -55,6 +55,7 @@ FeedsView::FeedsView(QWidget *parent) // Connections. connect(m_sourceModel, SIGNAL(requireItemValidationAfterDragDrop(QModelIndex)), this, SLOT(validateItemAfterDragDrop(QModelIndex))); + connect(m_sourceModel, SIGNAL(itemExpandRequested(QList,bool)), this, SLOT(onItemExpandRequested(QList,bool))); connect(header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), this, SLOT(saveSortState(int,Qt::SortOrder))); setModel(m_proxyModel); @@ -102,11 +103,11 @@ void FeedsView::saveExpandedStates() { Settings *settings = qApp->settings(); QList expandable_items; - expandable_items.append(sourceModel()->rootItem()->getSubTree(RootItemKind::Category)); + expandable_items.append(sourceModel()->rootItem()->getSubTree(RootItemKind::Category | RootItemKind::ServiceRoot)); // Iterate all categories and save their expand statuses. foreach (RootItem *item, expandable_items) { - QString setting_name = QString::number(qHash(item->title())) + QL1S("-") + QString::number(item->id()); + QString setting_name = QString::number(item->kind()) + QL1S("-") + QString::number(qHash(item->title())) + QL1S("-") + QString::number(item->id()); settings->setValue(GROUP(Categories), setting_name, @@ -122,10 +123,10 @@ void FeedsView::loadExpandedStates() { // Iterate all categories and save their expand statuses. foreach (RootItem *item, expandable_items) { - QString setting_name = QString::number(qHash(item->title())) + QL1S("-") + QString::number(item->id()); + QString setting_name = QString::number(item->kind()) + QL1S("-") + QString::number(qHash(item->title())) + QL1S("-") + QString::number(item->id()); setExpanded(model()->mapFromSource(sourceModel()->indexForItem(item)), - settings->value(GROUP(Categories), setting_name, true).toBool()); + settings->value(GROUP(Categories), setting_name, item->childCount() > 0).toBool()); } } @@ -466,3 +467,13 @@ void FeedsView::validateItemAfterDragDrop(const QModelIndex &source_index) { setCurrentIndex(mapped); } } + +void FeedsView::onItemExpandRequested(const QList &items, bool exp) { + foreach (RootItem *item, items) { + QModelIndex source_index = m_sourceModel->indexForItem(item); + QModelIndex proxy_index = m_proxyModel->mapFromSource(source_index); + + setExpanded(proxy_index, !exp); + setExpanded(proxy_index, exp); + } +} diff --git a/src/gui/feedsview.h b/src/gui/feedsview.h index 05f9f8a31..f80a9a250 100755 --- a/src/gui/feedsview.h +++ b/src/gui/feedsview.h @@ -104,6 +104,7 @@ class FeedsView : public QTreeView { void saveSortState(int column, Qt::SortOrder order); void validateItemAfterDragDrop(const QModelIndex &source_index); + void onItemExpandRequested(const QList &items, bool exp); private: // Initializes context menus. diff --git a/src/services/abstract/serviceroot.cpp b/src/services/abstract/serviceroot.cpp index 2a804ce18..cd25632b1 100755 --- a/src/services/abstract/serviceroot.cpp +++ b/src/services/abstract/serviceroot.cpp @@ -58,7 +58,7 @@ bool ServiceRoot::deleteViaGui() { return data_removed; } -void ServiceRoot::itemChanged(QList items) { +void ServiceRoot::itemChanged(const QList &items) { emit dataChanged(items); } @@ -70,6 +70,10 @@ void ServiceRoot::requestFeedReadFilterReload() { emit readFeedsFilterInvalidationRequested(); } +void ServiceRoot::requestItemExpand(const QList &items, bool expand) { + emit itemExpandRequested(items, expand); +} + void ServiceRoot::requestItemReassignment(RootItem *item, RootItem *new_parent) { emit itemReassignmentRequested(item, new_parent); } diff --git a/src/services/abstract/serviceroot.h b/src/services/abstract/serviceroot.h index 3dbd13475..30498fc73 100755 --- a/src/services/abstract/serviceroot.h +++ b/src/services/abstract/serviceroot.h @@ -138,10 +138,10 @@ class ServiceRoot : public RootItem { ///////////////////////////////////////// // Obvious methods to wrap signals. - void itemChanged(QList items); + void itemChanged(const QList &items); void requestReloadMessageList(bool mark_selected_messages_read); void requestFeedReadFilterReload(); - + void requestItemExpand(const QList &items, bool expand); void requestItemReassignment(RootItem *item, RootItem *new_parent); void requestItemRemoval(RootItem *item); @@ -154,6 +154,7 @@ class ServiceRoot : public RootItem { void dataChanged(QList items); void readFeedsFilterInvalidationRequested(); void reloadMessageListRequested(bool mark_selected_messages_read); + void itemExpandRequested(QList items, bool expand); void itemReassignmentRequested(RootItem *item, RootItem *new_parent); void itemRemovalRequested(RootItem *item); diff --git a/src/services/standard/standardserviceentrypoint.cpp b/src/services/standard/standardserviceentrypoint.cpp index ca3615619..f852f6bb5 100755 --- a/src/services/standard/standardserviceentrypoint.cpp +++ b/src/services/standard/standardserviceentrypoint.cpp @@ -75,7 +75,6 @@ ServiceRoot *StandardServiceEntryPoint::createNewRoot() { SERVICE_CODE_STD_RSS))) { StandardServiceRoot *root = new StandardServiceRoot(); root->setAccountId(id_to_assing); - root->loadFromDatabase(); return root; } else { @@ -93,7 +92,6 @@ QList StandardServiceEntryPoint::initializeSubtree() { while (query.next()) { StandardServiceRoot *root = new StandardServiceRoot(); root->setAccountId(query.value(0).toInt()); - root->loadFromDatabase(); roots.append(root); } } diff --git a/src/services/standard/standardserviceroot.cpp b/src/services/standard/standardserviceroot.cpp index ecebe24bf..c261155f0 100755 --- a/src/services/standard/standardserviceroot.cpp +++ b/src/services/standard/standardserviceroot.cpp @@ -61,6 +61,8 @@ StandardServiceRoot::~StandardServiceRoot() { } void StandardServiceRoot::start() { + loadFromDatabase(); + if (qApp->isFirstRun()) { if (MessageBox::show(qApp->mainForm(), QMessageBox::Question, QObject::tr("Load initial set of feeds"), tr("You started %1 for the first time, now you can load initial set of feeds.").arg(APP_NAME), diff --git a/src/services/tt-rss/gui/formeditaccount.cpp b/src/services/tt-rss/gui/formeditaccount.cpp index 43068321c..865e2df0e 100755 --- a/src/services/tt-rss/gui/formeditaccount.cpp +++ b/src/services/tt-rss/gui/formeditaccount.cpp @@ -148,7 +148,7 @@ void FormEditAccount::onClickedOk() { m_editableRoot->network()->setUrl(m_ui->m_txtUrl->lineEdit()->text()); m_editableRoot->network()->setUsername(m_ui->m_txtUsername->lineEdit()->text()); m_editableRoot->network()->setPassword(m_ui->m_txtPassword->lineEdit()->text()); - m_editableRoot->saveToDatabase(); + m_editableRoot->saveAccountDataToDatabase(); accept(); } @@ -185,6 +185,9 @@ void FormEditAccount::onUrlChanged() { if (url.isEmpty()) { m_ui->m_txtUrl->setStatus(WidgetWithStatus::Error, tr("URL cannot be empty.")); } + else if (!url.endsWith(QL1S("api/"))) { + m_ui->m_txtUrl->setStatus(WidgetWithStatus::Error, tr("URL must end with \"api/\".")); + } else { m_ui->m_txtUrl->setStatus(WidgetWithStatus::Ok, tr("URL is okay.")); } diff --git a/src/services/tt-rss/network/ttrssnetworkfactory.cpp b/src/services/tt-rss/network/ttrssnetworkfactory.cpp index 6576b9c08..5915f545e 100755 --- a/src/services/tt-rss/network/ttrssnetworkfactory.cpp +++ b/src/services/tt-rss/network/ttrssnetworkfactory.cpp @@ -92,15 +92,22 @@ TtRssLoginResponse TtRssNetworkFactory::login(QNetworkReply::NetworkError &error } TtRssResponse TtRssNetworkFactory::logout(QNetworkReply::NetworkError &error) { - QtJson::JsonObject json; - json["op"] = "logout"; - json["sid"] = m_sessionId; + if (!m_sessionId.isEmpty()) { - QByteArray result_raw; - NetworkResult network_reply = NetworkFactory::uploadData(m_url, DOWNLOAD_TIMEOUT, QtJson::serialize(json), CONTENT_TYPE, result_raw); + QtJson::JsonObject json; + json["op"] = "logout"; + json["sid"] = m_sessionId; - error = network_reply.first; - return TtRssResponse(QString::fromUtf8(result_raw)); + QByteArray result_raw; + NetworkResult network_reply = NetworkFactory::uploadData(m_url, DOWNLOAD_TIMEOUT, QtJson::serialize(json), CONTENT_TYPE, result_raw); + + error = network_reply.first; + return TtRssResponse(QString::fromUtf8(result_raw)); + } + else { + error = QNetworkReply::NoError; + return TtRssResponse(); + } } TtRssGetFeedsCategoriesResponse TtRssNetworkFactory::getFeedsCategories(QNetworkReply::NetworkError &error) { @@ -210,9 +217,12 @@ TtRssGetFeedsCategoriesResponse::TtRssGetFeedsCategoriesResponse(const QString & TtRssGetFeedsCategoriesResponse::~TtRssGetFeedsCategoriesResponse() { } -RootItem *TtRssGetFeedsCategoriesResponse::feedsCategories() { +RootItem *TtRssGetFeedsCategoriesResponse::feedsCategories(bool obtain_icons, QString base_address) { RootItem *parent = new RootItem(); + // Chop the "api/" from the end of the address. + base_address.chop(4); + if (status() == API_STATUS_OK) { // We have data, construct object tree according to data. QList items_to_process = m_rawContent["content"].toMap()["categories"].toMap()["items"].toList(); @@ -227,49 +237,61 @@ RootItem *TtRssGetFeedsCategoriesResponse::feedsCategories() { RootItem *act_parent = pair.first; QMap item = pair.second.toMap(); - if (item.contains("type") && item["type"].toString() == GFT_TYPE_CATEGORY) { - // Add category to the parent, go through children. - int item_bare_id = item["bare_id"].toInt(); + int item_id = item["bare_id"].toInt(); + bool is_category = item.contains("type") && item["type"].toString() == GFT_TYPE_CATEGORY; - if (item_bare_id < 0) { - // Ignore virtual categories or feeds. - continue; - } + if (item_id >= 0) { + if (is_category) { + if (item_id == 0) { + // This is "Uncategorized" category, all its feeds belong to top-level root. + if (item.contains("items")) { + foreach (QVariant child_feed, item["items"].toList()) { + pairs.append(QPair(parent, child_feed)); + } + } + } + else { + TtRssCategory *category = new TtRssCategory(); - if (item_bare_id == 0) { - // This is "Uncategorized" category, all its feeds belong to total parent. - if (item.contains("items")) { - foreach (QVariant child_feed, item["items"].toList()) { - pairs.append(QPair(parent, child_feed)); + category->setIcon(qApp->icons()->fromTheme(QSL("folder-category"))); + category->setTitle(item["name"].toString()); + category->setCustomId(item_id); + act_parent->appendChild(category); + + if (item.contains("items")) { + foreach (QVariant child, item["items"].toList()) { + pairs.append(QPair(category, child)); + } } } } - else if (item_bare_id > 0) { - TtRssCategory *category = new TtRssCategory(); + else { + // We have feed. + TtRssFeed *feed = new TtRssFeed(); - category->setIcon(qApp->icons()->fromTheme(QSL("folder-category"))); - category->setTitle(item["name"].toString()); - category->setCustomId(item_bare_id); - act_parent->appendChild(category); + if (obtain_icons) { + QString icon_path = item["icon"].type() == QVariant::String ? item["icon"].toString() : QString(); - if (item.contains("items")) { - foreach (QVariant child, item["items"].toList()) { - pairs.append(QPair(category, child)); + if (!icon_path.isEmpty()) { + // Chop the "api/" suffix out and append + QString full_icon_address = base_address + QL1C('/') + icon_path; + QByteArray icon_data; + + if (NetworkFactory::downloadFile(full_icon_address, DOWNLOAD_TIMEOUT, icon_data).first == QNetworkReply::NoError) { + // Icon downloaded, set it up. + QPixmap icon_pixmap; + icon_pixmap.loadFromData(icon_data); + feed->setIcon(QIcon(icon_pixmap)); + } } } + + // TODO: stahnout a nastavit ikonu + feed->setTitle(item["name"].toString()); + feed->setCustomId(item_id); + act_parent->appendChild(feed); } } - else { - // We have feed. - int item_bare_id = item["bare_id"].toInt(); - TtRssFeed *feed = new TtRssFeed(); - - // TODO: stahnout a nastavit ikonu - feed->setTitle(item["name"].toString()); - feed->setCustomId(item_bare_id); - act_parent->appendChild(feed); - } - } } diff --git a/src/services/tt-rss/network/ttrssnetworkfactory.h b/src/services/tt-rss/network/ttrssnetworkfactory.h index 5bc259209..8e76f3b09 100755 --- a/src/services/tt-rss/network/ttrssnetworkfactory.h +++ b/src/services/tt-rss/network/ttrssnetworkfactory.h @@ -58,7 +58,10 @@ class TtRssGetFeedsCategoriesResponse : public TtRssResponse { explicit TtRssGetFeedsCategoriesResponse(const QString &raw_content = QString()); virtual ~TtRssGetFeedsCategoriesResponse(); - RootItem *feedsCategories(); + // 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, QString base_address = QString()); }; class TtRssNetworkFactory { diff --git a/src/services/tt-rss/ttrssserviceentrypoint.cpp b/src/services/tt-rss/ttrssserviceentrypoint.cpp index 02c03c88a..6af16e2a8 100755 --- a/src/services/tt-rss/ttrssserviceentrypoint.cpp +++ b/src/services/tt-rss/ttrssserviceentrypoint.cpp @@ -87,7 +87,6 @@ QList TtRssServiceEntryPoint::initializeSubtree() { root->network()->setPassword(query.value(2).toString()); root->network()->setUrl(query.value(3).toString()); root->updateTitle(); - root->loadFromDatabase(); roots.append(root); } } diff --git a/src/services/tt-rss/ttrssserviceroot.cpp b/src/services/tt-rss/ttrssserviceroot.cpp index 48575f313..bffdd96e0 100755 --- a/src/services/tt-rss/ttrssserviceroot.cpp +++ b/src/services/tt-rss/ttrssserviceroot.cpp @@ -21,6 +21,8 @@ #include "miscellaneous/settings.h" #include "gui/dialogs/formmain.h" #include "services/tt-rss/ttrssserviceentrypoint.h" +#include "services/tt-rss/ttrssfeed.h" +#include "services/tt-rss/ttrsscategory.h" #include "services/tt-rss/network/ttrssnetworkfactory.h" #include "services/tt-rss/gui/formeditaccount.h" @@ -42,13 +44,16 @@ TtRssServiceRoot::~TtRssServiceRoot() { } void TtRssServiceRoot::start() { - if (childItems().isEmpty()) { - syncIn(); - } + // TODO: posunout starty rootů až je okno uděláno + loadFromDatabase(); + + // TODO: pokud tady není nic načteno, tak + // syncIn } void TtRssServiceRoot::stop() { - + QNetworkReply::NetworkError error; + m_network->logout(error); } QString TtRssServiceRoot::code() { @@ -164,7 +169,7 @@ TtRssNetworkFactory *TtRssServiceRoot::network() const { return m_network; } -void TtRssServiceRoot::saveToDatabase() { +void TtRssServiceRoot::saveAccountDataToDatabase() { if (accountId() != NO_PARENT_CATEGORY) { // We are overwritting previously saved data. QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); @@ -228,7 +233,97 @@ void TtRssServiceRoot::syncIn() { // 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 QNetworkReply::NetworkError err; + TtRssGetFeedsCategoriesResponse feed_cats_response = m_network->getFeedsCategories(err); + if (err == QNetworkReply::NoError) { + RootItem *new_tree = feed_cats_response.feedsCategories(true, m_network->url()); - RootItem *aa = m_network->getFeedsCategories(err).feedsCategories(); + // Purge old data from SQL and clean all model items. + removeOldFeedTree(); + cleanAllItems(); + + // Model is clean, now store new tree into DB and + // set primary IDs of the items. + storeNewFeedTree(new_tree); + + foreach (RootItem *top_level_item, new_tree->childItems()) { + appendChild(top_level_item); + } + + new_tree->clearChildren(); + new_tree->deleteLater(); + itemChanged(QList() << this); + requestFeedReadFilterReload(); + requestReloadMessageList(true); + requestItemExpand(getSubTree(), true); + } +} + +void TtRssServiceRoot::removeOldFeedTree() { + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + QSqlQuery query(database); + query.setForwardOnly(true); + + query.prepare(QSL("DELETE FROM Feeds WHERE account_id = :account_id;")); + query.bindValue(QSL(":account_id"), accountId()); + query.exec(); + + query.prepare(QSL("DELETE FROM Categories WHERE account_id = :account_id;")); + query.bindValue(QSL(":account_id"), accountId()); + query.exec(); +} + +void TtRssServiceRoot::cleanAllItems() { + foreach (RootItem *top_level_item, childItems()) { + requestItemRemoval(top_level_item); + } +} + +void TtRssServiceRoot::storeNewFeedTree(RootItem *root) { + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + QSqlQuery query_category(database); + QSqlQuery query_feed(database); + + query_category.prepare("INSERT INTO Categories (parent_id, title, account_id, custom_id) " + "VALUES (:parent_id, :title, :account_id, :custom_id);"); + query_feed.prepare("INSERT INTO Feeds (title, icon, category, protected, update_type, update_interval, account_id, custom_id) " + "VALUES (:title, :icon, :category, :protected, :update_type, :update_interval, :account_id, :custom_id);"); + + // Iterate all children. + foreach (RootItem *child, root->getSubTree()) { + if (child->kind() == RootItemKind::Category) { + query_category.bindValue(QSL(":parent_id"), child->parent()->id()); + query_category.bindValue(QSL(":title"), child->title()); + query_category.bindValue(QSL(":account_id"), accountId()); + query_category.bindValue(QSL(":custom_id"), QString::number(static_cast(child)->customId())); + + if (query_category.exec()) { + child->setId(query_category.lastInsertId().toInt()); + } + else { + // TODO: logovat + } + } + else if (child->kind() == RootItemKind::Feed) { + TtRssFeed *feed = static_cast(child); + + query_feed.bindValue(QSL(":title"), feed->title()); + query_feed.bindValue(QSL(":icon"), qApp->icons()->toByteArray(feed->icon())); + query_feed.bindValue(QSL(":category"), feed->parent()->id()); + query_feed.bindValue(QSL(":protected"), 0); + query_feed.bindValue(QSL(":update_type"), (int) feed->autoUpdateType()); + query_feed.bindValue(QSL(":update_interval"), feed->autoUpdateInitialInterval()); + query_feed.bindValue(QSL(":account_id"), accountId()); + query_feed.bindValue(QSL(":custom_id"), feed->customId()); + + if (query_feed.exec()) { + feed->setId(query_feed.lastInsertId().toInt()); + + // TODO: updatecounts; + } + else { + // TODO: logovat. + } + } + } } diff --git a/src/services/tt-rss/ttrssserviceroot.h b/src/services/tt-rss/ttrssserviceroot.h index 00ba6446e..a549d1374 100755 --- a/src/services/tt-rss/ttrssserviceroot.h +++ b/src/services/tt-rss/ttrssserviceroot.h @@ -66,14 +66,18 @@ class TtRssServiceRoot : public ServiceRoot { TtRssNetworkFactory *network() const; - void saveToDatabase(); - void loadFromDatabase(); + void saveAccountDataToDatabase(); void updateTitle(); private slots: void syncIn(); private: + void removeOldFeedTree(); + void cleanAllItems(); + void storeNewFeedTree(RootItem *root); + void loadFromDatabase(); + QAction *m_actionSyncIn; QList m_serviceMenu;