make important node work with marking as read/unred, able to clean messages, able to participate in newspaper view, also fixed bug in newspaper view where read/unread/starred changes were not propagated to message list

This commit is contained in:
Martin Rotter 2020-06-10 10:48:01 +02:00
parent 986a643a81
commit 88771a4f3d
11 changed files with 205 additions and 16 deletions

View File

@ -365,7 +365,7 @@ QList<Feed*>FeedsModel::feedsForScheduledUpdate(bool auto_update_now) {
return feeds_for_update;
}
QList<Message>FeedsModel::messagesForItem(RootItem* item) const {
QList<Message> FeedsModel::messagesForItem(RootItem* item) const {
return item->undeletedMessages();
}

View File

@ -362,6 +362,8 @@ bool MessagesModel::setMessageRead(int row_index, RootItem::ReadStatus read) {
}
bool MessagesModel::setMessageReadById(int id, RootItem::ReadStatus read) {
int a = 5;
for (int i = 0; i < rowCount(); i++) {
int found_id = data(i, MSG_DB_ID_INDEX, Qt::EditRole).toInt();
@ -415,7 +417,6 @@ bool MessagesModel::switchMessageImportance(int row_index) {
bool MessagesModel::switchBatchMessageImportance(const QModelIndexList& messages) {
QStringList message_ids;
QList<QPair<Message, RootItem::Importance>> message_states;
// Obtain IDs of all desired messages.
@ -423,6 +424,7 @@ bool MessagesModel::switchBatchMessageImportance(const QModelIndexList& messages
const Message msg = messageAt(message.row());
RootItem::Importance message_importance = messageImportance((message.row()));
message_states.append(QPair<Message, RootItem::Importance>(msg, message_importance == RootItem::Important ?
RootItem::NotImportant :
RootItem::Important));
@ -450,7 +452,6 @@ bool MessagesModel::switchBatchMessageImportance(const QModelIndexList& messages
bool MessagesModel::setBatchMessagesDeleted(const QModelIndexList& messages) {
QStringList message_ids;
QList<Message> msgs;
// Obtain IDs of all desired messages.
@ -493,7 +494,6 @@ bool MessagesModel::setBatchMessagesDeleted(const QModelIndexList& messages) {
bool MessagesModel::setBatchMessagesRead(const QModelIndexList& messages, RootItem::ReadStatus read) {
QStringList message_ids;
QList<Message> msgs;
// Obtain IDs of all desired messages.
@ -521,7 +521,6 @@ bool MessagesModel::setBatchMessagesRead(const QModelIndexList& messages, RootIt
bool MessagesModel::setBatchMessagesRestored(const QModelIndexList& messages) {
QStringList message_ids;
QList<Message> msgs;
// Obtain IDs of all desired messages.

View File

@ -85,10 +85,8 @@ class MessagesModel : public QSqlQueryModel, public MessagesModelSqlLayer {
MessageHighlighter m_messageHighlighter;
QString m_customDateFormat;
RootItem* m_selectedItem;
QList<QString> m_headerData;
QList<QString> m_tooltipData;
QFont m_normalFont;
QFont m_boldFont;
QFont m_normalStrikedFont;

View File

@ -29,7 +29,7 @@
FeedsView::FeedsView(QWidget* parent)
: QTreeView(parent), m_contextMenuService(nullptr), m_contextMenuBin(nullptr), m_contextMenuCategories(nullptr),
m_contextMenuFeeds(nullptr), m_contextMenuEmptySpace(nullptr), m_contextMenuOtherItems(nullptr) {
m_contextMenuFeeds(nullptr), m_contextMenuImportant(nullptr), m_contextMenuEmptySpace(nullptr), m_contextMenuOtherItems(nullptr) {
setObjectName(QSL("FeedsView"));
// Allocate models.
@ -95,7 +95,6 @@ void FeedsView::saveAllExpandStates() {
void FeedsView::saveExpandStates(RootItem* item) {
Settings* settings = qApp->settings();
QList<RootItem*> items = item->getSubTree(RootItemKind::Category | RootItemKind::ServiceRoot);
// Iterate all categories and save their expand statuses.
@ -112,8 +111,8 @@ void FeedsView::saveExpandStates(RootItem* item) {
void FeedsView::loadAllExpandStates() {
const Settings* settings = qApp->settings();
QList<RootItem*> expandable_items;
expandable_items.append(sourceModel()->rootItem()->getSubTree(RootItemKind::Category | RootItemKind::ServiceRoot));
// Iterate all categories and save their expand statuses.
@ -432,6 +431,7 @@ QMenu* FeedsView::initializeContextMenuBin(RootItem* clicked_item) {
}
QList<QAction*> specific_actions = clicked_item->contextMenu();
m_contextMenuBin->addActions(QList<QAction*>() <<
qApp->mainForm()->m_ui->m_actionViewSelectedItemsNewspaperMode <<
qApp->mainForm()->m_ui->m_actionMarkSelectedItemsAsRead <<
@ -454,6 +454,7 @@ QMenu* FeedsView::initializeContextMenuService(RootItem* clicked_item) {
}
QList<QAction*> specific_actions = clicked_item->contextMenu();
m_contextMenuService->addActions(QList<QAction*>() <<
qApp->mainForm()->m_ui->m_actionUpdateSelectedItems <<
qApp->mainForm()->m_ui->m_actionEditSelectedItem <<
@ -484,7 +485,7 @@ void FeedsView::focusInEvent(QFocusEvent* event) {
}
void FeedsView::expandItemDelayed(const QModelIndex& idx) {
QTimer::singleShot(100, this, [ = ] {
QTimer::singleShot(100, this, [=] {
setExpanded(m_proxyModel->mapFromSource(idx), true);
});
}
@ -498,6 +499,7 @@ QMenu* FeedsView::initializeContextMenuCategories(RootItem* clicked_item) {
}
QList<QAction*> specific_actions = clicked_item->contextMenu();
m_contextMenuCategories->addActions(QList<QAction*>() <<
qApp->mainForm()->m_ui->m_actionUpdateSelectedItems <<
qApp->mainForm()->m_ui->m_actionEditSelectedItem <<
@ -524,6 +526,7 @@ QMenu* FeedsView::initializeContextMenuFeeds(RootItem* clicked_item) {
}
QList<QAction*> specific_actions = clicked_item->contextMenu();
m_contextMenuFeeds->addActions(QList<QAction*>() <<
qApp->mainForm()->m_ui->m_actionUpdateSelectedItems <<
qApp->mainForm()->m_ui->m_actionEditSelectedItem <<
@ -541,6 +544,29 @@ QMenu* FeedsView::initializeContextMenuFeeds(RootItem* clicked_item) {
return m_contextMenuFeeds;
}
QMenu* FeedsView::initializeContextMenuImportant(RootItem* clicked_item) {
if (m_contextMenuImportant == nullptr) {
m_contextMenuImportant = new QMenu(tr("Context menu for important messages"), this);
}
else {
m_contextMenuImportant->clear();
}
QList<QAction*> specific_actions = clicked_item->contextMenu();
m_contextMenuImportant->addActions(QList<QAction*>() <<
qApp->mainForm()->m_ui->m_actionViewSelectedItemsNewspaperMode <<
qApp->mainForm()->m_ui->m_actionMarkSelectedItemsAsRead <<
qApp->mainForm()->m_ui->m_actionMarkSelectedItemsAsUnread);
if (!specific_actions.isEmpty()) {
m_contextMenuImportant->addSeparator();
m_contextMenuImportant->addActions(specific_actions);
}
return m_contextMenuImportant;
}
QMenu* FeedsView::initializeContextMenuEmptySpace() {
if (m_contextMenuEmptySpace == nullptr) {
m_contextMenuEmptySpace = new QMenu(tr("Context menu for empty space"), this);
@ -628,6 +654,9 @@ void FeedsView::contextMenuEvent(QContextMenuEvent* event) {
// Display context menu for feeds.
initializeContextMenuFeeds(clicked_item)->exec(event->globalPos());
}
else if (clicked_item->kind() == RootItemKind::Important) {
initializeContextMenuImportant(clicked_item)->exec(event->globalPos());
}
else if (clicked_item->kind() == RootItemKind::Bin) {
initializeContextMenuBin(clicked_item)->exec(event->globalPos());
}

View File

@ -114,18 +114,18 @@ class RSSGUARD_DLLSPEC FeedsView : public QTreeView {
QMenu* initializeContextMenuService(RootItem* clicked_item);
QMenu* initializeContextMenuCategories(RootItem* clicked_item);
QMenu* initializeContextMenuFeeds(RootItem* clicked_item);
QMenu* initializeContextMenuImportant(RootItem* clicked_item);
QMenu* initializeContextMenuEmptySpace();
QMenu* initializeContextMenuOtherItem(RootItem* clicked_item);
// Sets up appearance of this widget.
void setupAppearance();
void saveExpandStates(RootItem* item);
QMenu* m_contextMenuService;
QMenu* m_contextMenuBin;
QMenu* m_contextMenuCategories;
QMenu* m_contextMenuFeeds;
QMenu* m_contextMenuImportant;
QMenu* m_contextMenuEmptySpace;
QMenu* m_contextMenuOtherItems;
FeedsModel* m_sourceModel;

View File

@ -184,15 +184,27 @@ void TabWidget::closeAllTabs() {
int TabWidget::addNewspaperView(RootItem* root, const QList<Message>& messages) {
#if defined(USE_WEBENGINE)
WebBrowser* prev = new WebBrowser(this);
connect(prev, &WebBrowser::markMessageRead,
m_feedMessageViewer->messagesView()->sourceModel(), &MessagesModel::setMessageReadById);
connect(prev, &WebBrowser::markMessageImportant,
m_feedMessageViewer->messagesView()->sourceModel(), &MessagesModel::setMessageImportantById);
#else
NewspaperPreviewer* prev = new NewspaperPreviewer(root, messages, this);
#endif
int index = addTab(prev, qApp->icons()->fromTheme(QSL("format-justify-fill")), tr("Newspaper view"), TabBar::Closable);
connect(prev, &MessagePreviewer::markMessageRead,
m_feedMessageViewer->messagesView()->sourceModel(), &MessagesModel::setMessageReadById);
connect(prev, &MessagePreviewer::markMessageImportant,
m_feedMessageViewer->messagesView()->sourceModel(), &MessagesModel::setMessageImportantById);
#endif
int index = addTab(prev, qApp->icons()->fromTheme(QSL("format-justify-fill")), tr("Newspaper view"), TabBar::Closable);
setCurrentIndex(index);
#if defined(USE_WEBENGINE)
prev->loadMessages(messages, root);
#endif
return index;
}
@ -210,7 +222,6 @@ int TabWidget::addLinkedBrowser(const QString& initial_url) {
int TabWidget::addBrowser(bool move_after_current, bool make_active, const QUrl& initial_url) {
#if defined(USE_WEBENGINE)
// Create new WebBrowser.
WebBrowser* browser = new WebBrowser(this);
int final_index;

View File

@ -30,6 +30,17 @@
#include <QUrl>
#include <QVariant>
bool DatabaseQueries::markImportantMessagesReadUnread(const QSqlDatabase& db, int account_id, RootItem::ReadStatus read) {
QSqlQuery q(db);
q.setForwardOnly(true);
q.prepare("UPDATE Messages SET is_read = :read "
"WHERE is_important = 1 AND is_deleted = 0 AND is_pdeleted = 0 AND account_id = :account_id;");
q.bindValue(QSL(":read"), read == RootItem::Read ? 1 : 0);
q.bindValue(QSL(":account_id"), account_id);
return q.exec();
}
bool DatabaseQueries::markMessagesReadUnread(const QSqlDatabase& db, const QStringList& ids, RootItem::ReadStatus read) {
QSqlQuery q(db);
@ -366,6 +377,39 @@ int DatabaseQueries::getMessageCountsForBin(const QSqlDatabase& db, int account_
}
}
QList<Message> DatabaseQueries::getUndeletedImportantMessages(const QSqlDatabase& db, int account_id, bool* ok) {
QList<Message> messages;
QSqlQuery q(db);
q.setForwardOnly(true);
q.prepare("SELECT id, is_read, is_deleted, is_important, custom_id, title, url, author, date_created, contents, is_pdeleted, enclosures, account_id, custom_id, custom_hash, feed, CASE WHEN length(Messages.enclosures) > 10 THEN 'true' ELSE 'false' END AS has_enclosures "
"FROM Messages "
"WHERE is_important = 1 AND is_deleted = 0 AND is_pdeleted = 0 AND account_id = :account_id;");
q.bindValue(QSL(":account_id"), account_id);
if (q.exec()) {
while (q.next()) {
bool decoded;
Message message = Message::fromSqlRecord(q.record(), &decoded);
if (decoded) {
messages.append(message);
}
}
if (ok != nullptr) {
*ok = true;
}
}
else {
if (ok != nullptr) {
*ok = false;
}
}
return messages;
}
QList<Message> DatabaseQueries::getUndeletedMessagesForFeed(const QSqlDatabase& db, const QString& feed_custom_id, int account_id,
bool* ok) {
QList<Message> messages;
@ -766,6 +810,32 @@ bool DatabaseQueries::deleteAccountData(const QSqlDatabase& db, int account_id,
return result;
}
bool DatabaseQueries::cleanImportantMessages(const QSqlDatabase& db, bool clean_read_only, int account_id) {
QSqlQuery q(db);
q.setForwardOnly(true);
if (clean_read_only) {
q.prepare(QSL("UPDATE Messages SET is_deleted = :deleted "
"WHERE is_important = 1 AND is_deleted = 0 AND is_pdeleted = 0 AND is_read = 1 AND account_id = :account_id;"));
}
else {
q.prepare(QSL("UPDATE Messages SET is_deleted = :deleted "
"WHERE is_important = 1 AND is_deleted = 0 AND is_pdeleted = 0 AND account_id = :account_id;"));
}
q.bindValue(QSL(":deleted"), 1);
q.bindValue(QSL(":account_id"), account_id);
if (!q.exec()) {
qDebug("Cleaning of important messages failed: '%s'.", qPrintable(q.lastError().text()));
return false;
}
else {
return true;
}
}
bool DatabaseQueries::cleanFeeds(const QSqlDatabase& db, const QStringList& ids, bool clean_read_only, int account_id) {
QSqlQuery q(db);
@ -883,6 +953,29 @@ QStringList DatabaseQueries::customIdsOfMessagesFromAccount(const QSqlDatabase&
return ids;
}
QStringList DatabaseQueries::customIdsOfImportantMessages(const QSqlDatabase& db, int account_id, bool* ok) {
QSqlQuery q(db);
QStringList ids;
q.setForwardOnly(true);
q.prepare(QSL("SELECT custom_id FROM Messages "
"WHERE is_important = 1 AND is_deleted = 0 AND is_pdeleted = 0 AND account_id = :account_id;"));
q.bindValue(QSL(":account_id"), account_id);
if (ok != nullptr) {
*ok = q.exec();
}
else {
q.exec();
}
while (q.next()) {
ids.append(q.value(0).toString());
}
return ids;
}
QStringList DatabaseQueries::customIdsOfMessagesFromBin(const QSqlDatabase& db, int account_id, bool* ok) {
QSqlQuery q(db);
QStringList ids;

View File

@ -14,6 +14,7 @@ class DatabaseQueries {
public:
// Mark read/unread/starred/delete messages.
static bool markImportantMessagesReadUnread(const QSqlDatabase& db, int account_id, RootItem::ReadStatus read);
static bool markMessagesReadUnread(const QSqlDatabase& db, const QStringList& ids, RootItem::ReadStatus read);
static bool markMessageImportant(const QSqlDatabase& db, int id, RootItem::Importance importance);
static bool markFeedsReadUnread(const QSqlDatabase& db, const QStringList& ids, int account_id, RootItem::ReadStatus read);
@ -45,6 +46,9 @@ class DatabaseQueries {
static int getMessageCountsForBin(const QSqlDatabase& db, int account_id, bool including_total_counts, bool* ok = nullptr);
// Get messages (for newspaper view for example).
static QList<Message> getUndeletedImportantMessages(const QSqlDatabase& db,
int account_id,
bool* ok = nullptr);
static QList<Message> getUndeletedMessagesForFeed(const QSqlDatabase& db,
const QString& feed_custom_id,
int account_id,
@ -53,6 +57,7 @@ class DatabaseQueries {
static QList<Message> getUndeletedMessagesForAccount(const QSqlDatabase& db, int account_id, bool* ok = nullptr);
// Custom ID accumulators.
static QStringList customIdsOfImportantMessages(const QSqlDatabase& db, int account_id, bool* ok = nullptr);
static QStringList customIdsOfMessagesFromAccount(const QSqlDatabase& db, int account_id, bool* ok = nullptr);
static QStringList customIdsOfMessagesFromBin(const QSqlDatabase& db, int account_id, bool* ok = nullptr);
static QStringList customIdsOfMessagesFromFeed(const QSqlDatabase& db, const QString& feed_custom_id, int account_id,
@ -63,6 +68,7 @@ class DatabaseQueries {
int account_id, const QString& url, bool* any_message_changed, bool* ok = nullptr);
static bool deleteAccount(const QSqlDatabase& db, int account_id);
static bool deleteAccountData(const QSqlDatabase& db, int account_id, bool delete_messages_too);
static bool cleanImportantMessages(const QSqlDatabase& db, bool clean_read_only, int account_id);
static bool cleanFeeds(const QSqlDatabase& db, const QStringList& ids, bool clean_read_only, int account_id);
static bool storeAccountTree(const QSqlDatabase& db, RootItem* tree_root, int account_id);
static bool editBaseFeed(const QSqlDatabase& db, int feed_id, Feed::AutoUpdateType auto_update_type,

View File

@ -5,6 +5,7 @@
#include "miscellaneous/application.h"
#include "miscellaneous/databasequeries.h"
#include "miscellaneous/iconfactory.h"
#include "services/abstract/cacheforserviceroot.h"
#include "services/abstract/serviceroot.h"
#include <QThread>
@ -18,6 +19,12 @@ ImportantNode::ImportantNode(RootItem* parent_item) : RootItem(parent_item) {
setCreationDate(QDateTime::currentDateTime());
}
QList<Message> ImportantNode::undeletedMessages() const {
QSqlDatabase database = qApp->database()->connection(metaObject()->className());
return DatabaseQueries::getUndeletedImportantMessages(database, getParentServiceRoot()->accountId());
}
void ImportantNode::updateCounts(bool including_total_count) {
bool is_main_thread = QThread::currentThread() == qApp->thread();
QSqlDatabase database = is_main_thread ?
@ -32,6 +39,42 @@ void ImportantNode::updateCounts(bool including_total_count) {
m_unreadCount = DatabaseQueries::getImportantMessageCounts(database, account_id, false);
}
bool ImportantNode::cleanMessages(bool clean_read_only) {
ServiceRoot* service = getParentServiceRoot();
QSqlDatabase database = qApp->database()->connection(metaObject()->className());
if (DatabaseQueries::cleanImportantMessages(database, clean_read_only, service->accountId())) {
service->updateCounts(true);
service->itemChanged(getSubTree());
service->requestReloadMessageList(true);
return true;
}
else {
return false;
}
}
bool ImportantNode::markAsReadUnread(RootItem::ReadStatus status) {
ServiceRoot* service = getParentServiceRoot();
auto* cache = dynamic_cast<CacheForServiceRoot*>(service);
if (cache != nullptr) {
cache->addMessageStatesToCache(service->customIDSOfMessagesForItem(this), status);
}
QSqlDatabase database = qApp->database()->connection(metaObject()->className());
if (DatabaseQueries::markImportantMessagesReadUnread(database, service->accountId(), status)) {
service->updateCounts(true);
service->itemChanged(getSubTree());
service->requestReloadMessageList(status == RootItem::Read);
return true;
}
else {
return false;
}
}
int ImportantNode::countOfUnreadMessages() const {
return m_unreadCount;
}

View File

@ -12,7 +12,10 @@ class ImportantNode : public RootItem {
explicit ImportantNode(RootItem* parent_item = nullptr);
virtual ~ImportantNode() = default;
QList<Message> undeletedMessages() const;
bool cleanMessages(bool clean_read_only);
void updateCounts(bool including_total_count);
bool markAsReadUnread(ReadStatus status);
int countOfUnreadMessages() const;
int countOfAllMessages() const;

View File

@ -382,6 +382,13 @@ QStringList ServiceRoot::customIDSOfMessagesForItem(RootItem* item) {
break;
}
case RootItemKind::Important: {
QSqlDatabase database = qApp->database()->connection(metaObject()->className());
list = DatabaseQueries::customIdsOfImportantMessages(database, accountId());
break;
}
default:
break;
}