Message marking starred now works. New schema DB version.

This commit is contained in:
Martin Rotter 2016-02-26 09:15:57 +01:00
parent fb1233d379
commit 3efd22491c
18 changed files with 146 additions and 43 deletions

View file

@ -96,6 +96,7 @@ CREATE TABLE IF NOT EXISTS Messages (
enclosures TEXT, enclosures TEXT,
account_id INTEGER NOT NULL, account_id INTEGER NOT NULL,
custom_id TEXT, custom_id TEXT,
custom_hash TEXT,
FOREIGN KEY (account_id) REFERENCES Accounts (id) FOREIGN KEY (account_id) REFERENCES Accounts (id)
); );

View file

@ -91,6 +91,7 @@ CREATE TABLE IF NOT EXISTS Messages (
enclosures TEXT, enclosures TEXT,
account_id INTEGER NOT NULL, account_id INTEGER NOT NULL,
custom_id TEXT, custom_id TEXT,
custom_hash TEXT,
FOREIGN KEY (account_id) REFERENCES Accounts (id) FOREIGN KEY (account_id) REFERENCES Accounts (id)
); );

View file

@ -0,0 +1,5 @@
-- !
ALTER TABLE Messages
ADD COLUMN custom_hash TEXT;
-- !
UPDATE Information SET inf_value = '6' WHERE inf_key = 'schema_version';

View file

@ -0,0 +1,30 @@
CREATE TABLE backup_Messages AS SELECT * FROM Messages;
-- !
DROP TABLE Messages;
-- !
CREATE TABLE Messages (
id INTEGER PRIMARY KEY,
is_read INTEGER(1) NOT NULL CHECK (is_read >= 0 AND is_read <= 1) DEFAULT 0,
is_deleted INTEGER(1) NOT NULL CHECK (is_deleted >= 0 AND is_deleted <= 1) DEFAULT 0,
is_important INTEGER(1) NOT NULL CHECK (is_important >= 0 AND is_important <= 1) DEFAULT 0,
feed TEXT NOT NULL,
title TEXT NOT NULL CHECK (title != ''),
url TEXT,
author TEXT,
date_created INTEGER NOT NULL CHECK (date_created != 0),
contents TEXT,
is_pdeleted INTEGER(1) NOT NULL CHECK (is_pdeleted >= 0 AND is_pdeleted <= 1) DEFAULT 0,
enclosures TEXT,
account_id INTEGER NOT NULL,
custom_id TEXT,
custom_hash TEXT,
FOREIGN KEY (account_id) REFERENCES Accounts (id)
);
-- !
INSERT INTO Messages (id, is_read, is_deleted, is_important, feed, title, url, author, date_created, contents, is_pdeleted, enclosures, account_id, custom_id)
SELECT id, is_read, is_deleted, is_important, feed, title, url, author, date_created, contents, is_pdeleted, enclosures, account_id, custom_id FROM backup_Messages;
-- !
DROP TABLE backup_Messages;
-- !
UPDATE Information SET inf_value = '6' WHERE inf_key = 'schema_version';

View file

@ -65,14 +65,14 @@ QString Enclosures::encodeEnclosuresToString(const QList<Enclosure> &enclosures)
} }
Message::Message() { Message::Message() {
m_title = m_url = m_author = m_contents = m_feedId = m_customId = ""; m_title = m_url = m_author = m_contents = m_feedId = m_customId = m_customHash = "";
m_enclosures = QList<Enclosure>(); m_enclosures = QList<Enclosure>();
m_accountId = m_id = 0; m_accountId = m_id = 0;
m_isRead = m_isImportant = false; m_isRead = m_isImportant = false;
} }
Message Message::fromSqlRecord(const QSqlRecord &record, bool *result) { Message Message::fromSqlRecord(const QSqlRecord &record, bool *result) {
if (record.count() != MSG_DB_CUSTOM_ID_INDEX + 1) { if (record.count() != MSG_DB_CUSTOM_HASH_INDEX + 1) {
if (result != NULL) { if (result != NULL) {
*result = false; *result = false;
return Message(); return Message();
@ -93,6 +93,7 @@ Message Message::fromSqlRecord(const QSqlRecord &record, bool *result) {
message.m_enclosures = Enclosures::decodeEnclosuresFromString(record.value(MSG_DB_ENCLOSURES_INDEX).toString()); message.m_enclosures = Enclosures::decodeEnclosuresFromString(record.value(MSG_DB_ENCLOSURES_INDEX).toString());
message.m_accountId = record.value(MSG_DB_ACCOUNT_ID_INDEX).toInt(); message.m_accountId = record.value(MSG_DB_ACCOUNT_ID_INDEX).toInt();
message.m_customId = record.value(MSG_DB_CUSTOM_ID_INDEX).toString(); message.m_customId = record.value(MSG_DB_CUSTOM_ID_INDEX).toString();
message.m_customHash = record.value(MSG_DB_CUSTOM_HASH_INDEX).toString();
if (result != NULL) { if (result != NULL) {
*result = true; *result = true;

View file

@ -59,6 +59,7 @@ class Message {
int m_accountId; int m_accountId;
int m_id; int m_id;
QString m_customId; QString m_customId;
QString m_customHash;
bool m_isRead; bool m_isRead;
bool m_isImportant; bool m_isImportant;

View file

@ -146,7 +146,8 @@ void MessagesModel::setupHeaderData() {
/*: Tooltip for "pdeleted" column in msg list.*/ tr("Permanently deleted") << /*: Tooltip for "pdeleted" column in msg list.*/ tr("Permanently deleted") <<
/*: Tooltip for attachments of message.*/ tr("Attachments") << /*: Tooltip for attachments of message.*/ tr("Attachments") <<
/*: Tooltip for account ID of message.*/ tr("Account ID") << /*: Tooltip for account ID of message.*/ tr("Account ID") <<
/*: Tooltip for custom ID of message.*/ tr("Custom ID"); /*: Tooltip for custom ID of message.*/ tr("Custom ID") <<
/*: Tooltip for custom hash string of message.*/ tr("Custom hash");
m_tooltipData << tr("Id of the message.") << tr("Is message read?") << m_tooltipData << tr("Id of the message.") << tr("Is message read?") <<
tr("Is message deleted?") << tr("Is message important?") << tr("Is message deleted?") << tr("Is message important?") <<
@ -154,7 +155,8 @@ void MessagesModel::setupHeaderData() {
tr("Title of the message.") << tr("Url of the message.") << tr("Title of the message.") << tr("Url of the message.") <<
tr("Author of the message.") << tr("Creation date of the message.") << tr("Author of the message.") << tr("Creation date of the message.") <<
tr("Contents of the message.") << tr("Is message permanently deleted from recycle bin?") << tr("Contents of the message.") << tr("Is message permanently deleted from recycle bin?") <<
tr("List of attachments.") << tr("Account ID of the message.") << tr("Custom ID of the message"); tr("List of attachments.") << tr("Account ID of the message.") << tr("Custom ID of the message") <<
tr("Custom hash of the message.");
} }
Qt::ItemFlags MessagesModel::flags(const QModelIndex &index) const { Qt::ItemFlags MessagesModel::flags(const QModelIndex &index) const {

View file

@ -176,20 +176,21 @@
#endif #endif
// Indexes of columns as they are DEFINED IN THE TABLE for MESSAGES. // Indexes of columns as they are DEFINED IN THE TABLE for MESSAGES.
#define MSG_DB_ID_INDEX 0 #define MSG_DB_ID_INDEX 0
#define MSG_DB_READ_INDEX 1 #define MSG_DB_READ_INDEX 1
#define MSG_DB_DELETED_INDEX 2 #define MSG_DB_DELETED_INDEX 2
#define MSG_DB_IMPORTANT_INDEX 3 #define MSG_DB_IMPORTANT_INDEX 3
#define MSG_DB_FEED_INDEX 4 #define MSG_DB_FEED_INDEX 4
#define MSG_DB_TITLE_INDEX 5 #define MSG_DB_TITLE_INDEX 5
#define MSG_DB_URL_INDEX 6 #define MSG_DB_URL_INDEX 6
#define MSG_DB_AUTHOR_INDEX 7 #define MSG_DB_AUTHOR_INDEX 7
#define MSG_DB_DCREATED_INDEX 8 #define MSG_DB_DCREATED_INDEX 8
#define MSG_DB_CONTENTS_INDEX 9 #define MSG_DB_CONTENTS_INDEX 9
#define MSG_DB_PDELETED_INDEX 10 #define MSG_DB_PDELETED_INDEX 10
#define MSG_DB_ENCLOSURES_INDEX 11 #define MSG_DB_ENCLOSURES_INDEX 11
#define MSG_DB_ACCOUNT_ID_INDEX 12 #define MSG_DB_ACCOUNT_ID_INDEX 12
#define MSG_DB_CUSTOM_ID_INDEX 13 #define MSG_DB_CUSTOM_ID_INDEX 13
#define MSG_DB_CUSTOM_HASH_INDEX 14
// Indexes of columns as they are DEFINED IN THE TABLE for CATEGORIES. // Indexes of columns as they are DEFINED IN THE TABLE for CATEGORIES.
#define CAT_DB_ID_INDEX 0 #define CAT_DB_ID_INDEX 0

View file

@ -564,6 +564,7 @@ void MessagesView::adjustColumns() {
hideColumn(MSG_DB_ENCLOSURES_INDEX); hideColumn(MSG_DB_ENCLOSURES_INDEX);
hideColumn(MSG_DB_ACCOUNT_ID_INDEX); hideColumn(MSG_DB_ACCOUNT_ID_INDEX);
hideColumn(MSG_DB_CUSTOM_ID_INDEX); hideColumn(MSG_DB_CUSTOM_ID_INDEX);
hideColumn(MSG_DB_CUSTOM_HASH_INDEX);
qDebug("Adjusting column resize modes for MessagesView."); qDebug("Adjusting column resize modes for MessagesView.");
} }

View file

@ -203,8 +203,8 @@ int Feed::updateMessages(const QList<Message> &messages) {
// Used to insert new messages. // Used to insert new messages.
query_insert.setForwardOnly(true); query_insert.setForwardOnly(true);
query_insert.prepare("INSERT INTO Messages " query_insert.prepare("INSERT INTO Messages "
"(feed, title, is_read, is_important, url, author, date_created, contents, enclosures, custom_id, account_id) " "(feed, title, is_read, is_important, url, author, date_created, contents, enclosures, custom_id, custom_hash, account_id) "
"VALUES (:feed, :title, :is_read, :is_important, :url, :author, :date_created, :contents, :enclosures, :custom_id, :account_id);"); "VALUES (:feed, :title, :is_read, :is_important, :url, :author, :date_created, :contents, :enclosures, :custom_id, :custom_hash, :account_id);");
// Used to update existing messages. // Used to update existing messages.
query_update.setForwardOnly(true); query_update.setForwardOnly(true);
@ -313,6 +313,7 @@ int Feed::updateMessages(const QList<Message> &messages) {
query_insert.bindValue(QSL(":contents"), message.m_contents); query_insert.bindValue(QSL(":contents"), message.m_contents);
query_insert.bindValue(QSL(":enclosures"), Enclosures::encodeEnclosuresToString(message.m_enclosures)); query_insert.bindValue(QSL(":enclosures"), Enclosures::encodeEnclosuresToString(message.m_enclosures));
query_insert.bindValue(QSL(":custom_id"), message.m_customId); query_insert.bindValue(QSL(":custom_id"), message.m_customId);
query_insert.bindValue(QSL(":custom_hash"), message.m_customHash);
query_insert.bindValue(QSL(":account_id"), account_id); query_insert.bindValue(QSL(":account_id"), account_id);
if (query_insert.exec() && query_insert.numRowsAffected() == 1) { if (query_insert.exec() && query_insert.numRowsAffected() == 1) {

View file

@ -299,7 +299,7 @@ QStringList ServiceRoot::textualFeedIds(const QList<Feed*> &feeds) const {
return stringy_ids; return stringy_ids;
} }
QStringList ServiceRoot::customIDsOfMessages(const QList<QPair<Message,RootItem::Importance> > &changes) { QStringList ServiceRoot::customIDsOfMessages(const QList<ImportanceChange> &changes) {
QStringList list; QStringList list;
for (int i = 0; i < changes.size(); i++) { for (int i = 0; i < changes.size(); i++) {
@ -360,14 +360,14 @@ bool ServiceRoot::onAfterSetMessagesRead(RootItem *selected_item, const QList<Me
return true; return true;
} }
bool ServiceRoot::onBeforeSwitchMessageImportance(RootItem *selected_item, const QList<QPair<Message, RootItem::Importance> > &changes) { bool ServiceRoot::onBeforeSwitchMessageImportance(RootItem *selected_item, const QList<ImportanceChange> &changes) {
Q_UNUSED(selected_item) Q_UNUSED(selected_item)
Q_UNUSED(changes) Q_UNUSED(changes)
return true; return true;
} }
bool ServiceRoot::onAfterSwitchMessageImportance(RootItem *selected_item, const QList<QPair<Message, RootItem::Importance> > &changes) { bool ServiceRoot::onAfterSwitchMessageImportance(RootItem *selected_item, const QList<ImportanceChange> &changes) {
Q_UNUSED(selected_item) Q_UNUSED(selected_item)
Q_UNUSED(changes) Q_UNUSED(changes)

View file

@ -33,6 +33,7 @@ class QSqlTableModel;
// Car here represents ID of the item. // Car here represents ID of the item.
typedef QList<QPair<int,RootItem*> > Assignment; typedef QList<QPair<int,RootItem*> > Assignment;
typedef QPair<int,RootItem*> AssignmentItem; typedef QPair<int,RootItem*> AssignmentItem;
typedef QPair<Message,RootItem::Importance> ImportanceChange;
// THIS IS the root node of the service. // THIS IS the root node of the service.
// NOTE: The root usually contains some core functionality of the // NOTE: The root usually contains some core functionality of the
@ -120,7 +121,7 @@ class ServiceRoot : public RootItem {
// some ONLINE service or something. // some ONLINE service or something.
// //
// "changes" - list of pairs - <message (integer id), new status> // "changes" - list of pairs - <message (integer id), new status>
virtual bool onBeforeSwitchMessageImportance(RootItem *selected_item, const QList<QPair<Message,RootItem::Importance> > &changes); virtual bool onBeforeSwitchMessageImportance(RootItem *selected_item, const QList<ImportanceChange> &changes);
// Called AFTER this importance switch update is stored in DB, // Called AFTER this importance switch update is stored in DB,
// when false is returned, change is aborted. // when false is returned, change is aborted.
@ -128,7 +129,7 @@ class ServiceRoot : public RootItem {
// which items are actually changed. // which items are actually changed.
// //
// "changes" - list of pairs - <message (integer id), new status> // "changes" - list of pairs - <message (integer id), new status>
virtual bool onAfterSwitchMessageImportance(RootItem *selected_item, const QList<QPair<Message,RootItem::Importance> > &changes); virtual bool onAfterSwitchMessageImportance(RootItem *selected_item, const QList<ImportanceChange> &changes);
// Called BEFORE the list of messages is about to be deleted // Called BEFORE the list of messages is about to be deleted
// by the user from message list. // by the user from message list.
@ -176,7 +177,7 @@ class ServiceRoot : public RootItem {
QStringList textualFeedIds(const QList<Feed*> &feeds) const; QStringList textualFeedIds(const QList<Feed*> &feeds) const;
QStringList customIDsOfMessages(const QList<QPair<Message,Importance> > &changes); QStringList customIDsOfMessages(const QList<ImportanceChange> &changes);
QStringList customIDsOfMessages(const QList<Message> &messages); QStringList customIDsOfMessages(const QList<Message> &messages);
// Takes lists of feeds/categories and assembles them into the tree structure. // Takes lists of feeds/categories and assembles them into the tree structure.

View file

@ -221,8 +221,47 @@ QNetworkReply::NetworkError OwnCloudNetworkFactory::markMessagesRead(RootItem::R
return (m_lastError = network_reply.first); return (m_lastError = network_reply.first);
} }
QNetworkReply::NetworkError OwnCloudNetworkFactory::markMessagesStarred(RootItem::Importance importance, const QStringList &custom_ids) { QNetworkReply::NetworkError OwnCloudNetworkFactory::markMessagesStarred(RootItem::Importance importance,
return QNetworkReply::NoError; const QStringList &feed_ids,
const QStringList &guid_hashes) {
QtJson::JsonObject json;
QtJson::JsonArray ids;
QByteArray raw_output;
QString final_url;
if (importance == RootItem::Important) {
final_url = m_fixedUrl + API_PATH + "items/star/multiple";
}
else {
final_url = m_fixedUrl + API_PATH + "items/unstar/multiple";
}
for (int i = 0; i < feed_ids.size(); i++) {
QVariantMap item;
item.insert(QSL("feedId"), feed_ids.at(i));
item.insert(QSL("guidHash"), guid_hashes.at(i));
ids.append(item);
}
json["items"] = ids;
NetworkResult network_reply = NetworkFactory::uploadData(final_url,
qApp->settings()->value(GROUP(Feeds),
SETTING(Feeds::UpdateTimeout)).toInt(),
QtJson::serialize(json),
"application/json",
raw_output,
QNetworkAccessManager::PutOperation,
true, m_authUsername, m_authPassword,
true);
if (network_reply.first != QNetworkReply::NoError) {
qWarning("ownCloud: Marking messages as (un)starred failed with error %d.", network_reply.first);
}
return (m_lastError = network_reply.first);
} }
QString OwnCloudNetworkFactory::userId() const { QString OwnCloudNetworkFactory::userId() const {
@ -398,6 +437,7 @@ QList<Message> OwnCloudGetMessagesResponse::messages() const {
msg.m_created = TextFactory::parseDateTime(message_map["pubDate"].value<qint64>() * 1000); msg.m_created = TextFactory::parseDateTime(message_map["pubDate"].value<qint64>() * 1000);
msg.m_createdFromFeed = true; msg.m_createdFromFeed = true;
msg.m_customId = message_map["id"].toString(); msg.m_customId = message_map["id"].toString();
msg.m_customHash = message_map["guidHash"].toString();
QString enclosure_link = message_map["enclosureLink"].toString(); QString enclosure_link = message_map["enclosureLink"].toString();

View file

@ -123,7 +123,8 @@ class OwnCloudNetworkFactory {
OwnCloudGetMessagesResponse getMessages(int feed_id); OwnCloudGetMessagesResponse getMessages(int feed_id);
QNetworkReply::NetworkError markMessagesRead(RootItem::ReadStatus status, const QStringList &custom_ids); QNetworkReply::NetworkError markMessagesRead(RootItem::ReadStatus status, const QStringList &custom_ids);
QNetworkReply::NetworkError markMessagesStarred(RootItem::Importance importance, const QStringList &custom_ids); QNetworkReply::NetworkError markMessagesStarred(RootItem::Importance importance, const QStringList &feed_ids,
const QStringList &guid_hashes);
private: private:
QString m_url; QString m_url;

View file

@ -121,24 +121,41 @@ bool OwnCloudServiceRoot::onBeforeSetMessagesRead(RootItem *selected_item, const
} }
bool OwnCloudServiceRoot::onBeforeSwitchMessageImportance(RootItem *selected_item, bool OwnCloudServiceRoot::onBeforeSwitchMessageImportance(RootItem *selected_item,
const QList<QPair<Message,RootItem::Importance> > &changes) { const QList<ImportanceChange> &changes) {
Q_UNUSED(selected_item) Q_UNUSED(selected_item)
QNetworkReply::NetworkError reply; // Now, we need to separate the changes because of ownCloud API limitations.
QStringList mark_starred_feed_ids, mark_starred_guid_hashes;
QStringList mark_unstarred_feed_ids, mark_unstarred_guid_hashes;
/*if (read == RootItem::Read) { foreach (const ImportanceChange &pair, changes) {
//reply = network()->markMessagesRead(); if (pair.second == RootItem::Important) {
mark_starred_feed_ids.append(pair.first.m_feedId);
mark_starred_guid_hashes.append(pair.first.m_customHash);
}
else {
mark_unstarred_feed_ids.append(pair.first.m_feedId);
mark_unstarred_guid_hashes.append(pair.first.m_customHash);
}
} }
else {
reply = network()->markMessagesUnread();
}*/
if (reply == QNetworkReply::NoError) { // OK, now perform the online update itself.
return true;
if (!mark_starred_feed_ids.isEmpty()) {
if (network()->markMessagesStarred(RootItem::Important, mark_starred_feed_ids, mark_starred_guid_hashes) !=
QNetworkReply::NoError) {
return false;
}
} }
else {
return false; if (!mark_unstarred_feed_ids.isEmpty()) {
if (network()->markMessagesStarred(RootItem::NotImportant, mark_unstarred_feed_ids, mark_unstarred_guid_hashes) !=
QNetworkReply::NoError) {
return false;
}
} }
return true;
} }
void OwnCloudServiceRoot::updateTitle() { void OwnCloudServiceRoot::updateTitle() {

View file

@ -46,7 +46,7 @@ class OwnCloudServiceRoot : public ServiceRoot {
OwnCloudNetworkFactory *network() const; OwnCloudNetworkFactory *network() const;
bool onBeforeSetMessagesRead(RootItem *selected_item, const QList<Message> &messages, ReadStatus read); bool onBeforeSetMessagesRead(RootItem *selected_item, const QList<Message> &messages, ReadStatus read);
bool onBeforeSwitchMessageImportance(RootItem *selected_item, const QList<QPair<Message,RootItem::Importance> > &changes); bool onBeforeSwitchMessageImportance(RootItem *selected_item, const QList<ImportanceChange> &changes);
void updateTitle(); void updateTitle();
void saveAccountDataToDatabase(); void saveAccountDataToDatabase();

View file

@ -200,7 +200,7 @@ bool TtRssServiceRoot::onBeforeSetMessagesRead(RootItem *selected_item, const QL
} }
} }
bool TtRssServiceRoot::onBeforeSwitchMessageImportance(RootItem *selected_item, const QList<QPair<Message,Importance> > &changes) { bool TtRssServiceRoot::onBeforeSwitchMessageImportance(RootItem *selected_item, const QList<ImportanceChange> &changes) {
Q_UNUSED(selected_item) Q_UNUSED(selected_item)
// NOTE: We just toggle it here, because we know, that there is only // NOTE: We just toggle it here, because we know, that there is only

View file

@ -50,7 +50,7 @@ class TtRssServiceRoot : public ServiceRoot {
RecycleBin *recycleBin() const; RecycleBin *recycleBin() const;
bool onBeforeSetMessagesRead(RootItem *selected_item, const QList<Message> &messages, ReadStatus read); bool onBeforeSetMessagesRead(RootItem *selected_item, const QList<Message> &messages, ReadStatus read);
bool onBeforeSwitchMessageImportance(RootItem *selected_item, const QList<QPair<Message,RootItem::Importance> > &changes); bool onBeforeSwitchMessageImportance(RootItem *selected_item, const QList<ImportanceChange> &changes);
// Access to network. // Access to network.
TtRssNetworkFactory *network() const; TtRssNetworkFactory *network() const;