Fix Cache logic to support labels.

This commit is contained in:
Martin Rotter 2020-10-22 11:30:45 +02:00
parent 9b8ad9546b
commit 3cf7d6edea
9 changed files with 105 additions and 38 deletions

View File

@ -12,20 +12,32 @@
CacheForServiceRoot::CacheForServiceRoot() : m_cacheSaveMutex(new QMutex(QMutex::NonRecursive)) {} CacheForServiceRoot::CacheForServiceRoot() : m_cacheSaveMutex(new QMutex(QMutex::NonRecursive)) {}
void CacheForServiceRoot::addMessageStatesToCache(const QList<Message>& ids_of_messages, Label* lbl, bool assign) { void CacheForServiceRoot::addLabelsAssignmentsToCache(const QList<Message>& ids_of_messages, Label* lbl, bool assign) {
auto custom_ids = lbl->getParentServiceRoot()->customIDsOfMessages(ids_of_messages); auto custom_ids = lbl->getParentServiceRoot()->customIDsOfMessages(ids_of_messages);
if (assign) { if (assign) {
m_cachedLabelAssignments[lbl->customId()].append(custom_ids); for (const QString& custom_id : custom_ids) {
m_cachedLabelAssignments[lbl->customId()].removeDuplicates(); if (m_cachedLabelDeassignments[lbl->customId()].contains(custom_id)) {
// We want to assign this ID but it was marked for deassignment, remove from deassignment.
// Remove the same messages from "deassign" list. m_cachedLabelDeassignments[lbl->customId()].removeAll(custom_id);
auto deassign = m_cachedLabelDeassignments[lbl->customId()]; }
auto list = boolinq::from(deassign.begin(), deassign.end()).where([custom_ids](const QString& id) { else {
return !custom_ids.contains(id); m_cachedLabelAssignments[lbl->customId()].append(custom_id);
}).toStdList(); m_cachedLabelAssignments[lbl->customId()].removeDuplicates();
}
m_cachedLabelDeassignments[lbl->customId()] = FROM_STD_LIST(QStringList, list); }
}
else {
for (const QString& custom_id : custom_ids) {
if (m_cachedLabelAssignments[lbl->customId()].contains(custom_id)) {
// We want to deassign this ID but it was marked for assignment, remove from assignment.
m_cachedLabelAssignments[lbl->customId()].removeAll(custom_id);
}
else {
m_cachedLabelDeassignments[lbl->customId()].append(custom_id);
m_cachedLabelDeassignments[lbl->customId()].removeDuplicates();
}
}
} }
} }
@ -108,6 +120,8 @@ void CacheForServiceRoot::saveCacheToFile(int acc_id) {
void CacheForServiceRoot::clearCache() { void CacheForServiceRoot::clearCache() {
m_cachedStatesRead.clear(); m_cachedStatesRead.clear();
m_cachedStatesImportant.clear(); m_cachedStatesImportant.clear();
m_cachedLabelAssignments.clear();
m_cachedLabelDeassignments.clear();
} }
void CacheForServiceRoot::loadCacheFromFile(int acc_id) { void CacheForServiceRoot::loadCacheFromFile(int acc_id) {
@ -132,25 +146,34 @@ void CacheForServiceRoot::loadCacheFromFile(int acc_id) {
} }
} }
QPair<QMap<RootItem::ReadStatus, QStringList>, QMap<RootItem::Importance, QList<Message>>> CacheForServiceRoot::takeMessageCache() { CacheSnapshot CacheForServiceRoot::takeMessageCache() {
QMutexLocker lck(m_cacheSaveMutex.data()); QMutexLocker lck(m_cacheSaveMutex.data());
if (isEmpty()) { if (isEmpty()) {
return QPair<QMap<RootItem::ReadStatus, QStringList>, QMap<RootItem::Importance, QList<Message>>>(); return CacheSnapshot();
} }
// Make copy of changes. // Make copy of changes.
QMap<RootItem::ReadStatus, QStringList> cached_data_read = m_cachedStatesRead; auto cached_data_read = m_cachedStatesRead;
auto cached_data_imp = m_cachedStatesImportant;
auto cached_ass_lbl = m_cachedLabelAssignments;
auto cached_deass_lbl = m_cachedLabelDeassignments;
cached_data_read.detach(); cached_data_read.detach();
QMap<RootItem::Importance, QList<Message>> cached_data_imp = m_cachedStatesImportant;
cached_data_imp.detach(); cached_data_imp.detach();
cached_ass_lbl.detach();
cached_deass_lbl.detach();
clearCache(); clearCache();
return QPair<QMap<RootItem::ReadStatus, QStringList>, QMap<RootItem::Importance, QList<Message>>>(cached_data_read, cached_data_imp); CacheSnapshot c;
c.m_cachedLabelAssignments = cached_ass_lbl;
c.m_cachedLabelDeassignments = cached_deass_lbl;
c.m_cachedStatesImportant = cached_data_imp;
c.m_cachedStatesRead = cached_data_read;
return c;
} }
bool CacheForServiceRoot::isEmpty() const { bool CacheForServiceRoot::isEmpty() const {

View File

@ -11,11 +11,18 @@
class QMutex; class QMutex;
struct CacheSnapshot {
QMap<QString, QStringList> m_cachedLabelAssignments;
QMap<QString, QStringList> m_cachedLabelDeassignments;
QMap<RootItem::ReadStatus, QStringList> m_cachedStatesRead;
QMap<RootItem::Importance, QList<Message>> m_cachedStatesImportant;
};
class CacheForServiceRoot { class CacheForServiceRoot {
public: public:
explicit CacheForServiceRoot(); explicit CacheForServiceRoot();
void addMessageStatesToCache(const QList<Message>& ids_of_messages, Label* lbl, bool assign); void addLabelsAssignmentsToCache(const QList<Message>& ids_of_messages, Label* lbl, bool assign);
void addMessageStatesToCache(const QList<Message>& ids_of_messages, RootItem::Importance importance); void addMessageStatesToCache(const QList<Message>& ids_of_messages, RootItem::Importance importance);
void addMessageStatesToCache(const QStringList& ids_of_messages, RootItem::ReadStatus read); void addMessageStatesToCache(const QStringList& ids_of_messages, RootItem::ReadStatus read);
@ -27,7 +34,7 @@ class CacheForServiceRoot {
virtual void saveAllCachedData(bool async = true) = 0; virtual void saveAllCachedData(bool async = true) = 0;
protected: protected:
QPair<QMap<RootItem::ReadStatus, QStringList>, QMap<RootItem::Importance, QList<Message>>> takeMessageCache(); CacheSnapshot takeMessageCache();
QScopedPointer<QMutex> m_cacheSaveMutex; QScopedPointer<QMutex> m_cacheSaveMutex;

View File

@ -104,19 +104,21 @@ QIcon Label::generateIcon(const QColor& color) {
void Label::assignToMessage(const Message& msg) { void Label::assignToMessage(const Message& msg) {
QSqlDatabase database = qApp->database()->connection(metaObject()->className()); QSqlDatabase database = qApp->database()->connection(metaObject()->className());
DatabaseQueries::assignLabelToMessage(database, this, msg); if (getParentServiceRoot()->onBeforeLabelMessageAssignmentChanged({this}, {msg}, true)) {
DatabaseQueries::assignLabelToMessage(database, this, msg);
updateCounts(true); getParentServiceRoot()->onAfterLabelMessageAssignmentChanged({this}, {msg}, true);
getParentServiceRoot()->itemChanged({ this }); }
} }
void Label::deassignFromMessage(const Message& msg) { void Label::deassignFromMessage(const Message& msg) {
QSqlDatabase database = qApp->database()->connection(metaObject()->className()); QSqlDatabase database = qApp->database()->connection(metaObject()->className());
DatabaseQueries::deassignLabelFromMessage(database, this, msg); if (getParentServiceRoot()->onBeforeLabelMessageAssignmentChanged({this}, {msg}, false)) {
DatabaseQueries::deassignLabelFromMessage(database, this, msg);
updateCounts(true); getParentServiceRoot()->onAfterLabelMessageAssignmentChanged({this}, {msg}, false);
getParentServiceRoot()->itemChanged({ this }); }
} }
void Label::setCountOfAllMessages(int totalCount) { void Label::setCountOfAllMessages(int totalCount) {

View File

@ -2,6 +2,7 @@
#include "services/abstract/serviceroot.h" #include "services/abstract/serviceroot.h"
#include "3rd-party/boolinq/boolinq.h"
#include "core/feedsmodel.h" #include "core/feedsmodel.h"
#include "core/messagesmodel.h" #include "core/messagesmodel.h"
#include "miscellaneous/application.h" #include "miscellaneous/application.h"
@ -616,6 +617,34 @@ bool ServiceRoot::onAfterMessagesDelete(RootItem* selected_item, const QList<Mes
return true; return true;
} }
bool ServiceRoot::onBeforeLabelMessageAssignmentChanged(const QList<Label*> labels, const QList<Message>& messages, bool assign) {
auto cache = dynamic_cast<CacheForServiceRoot*>(this);
if (cache != nullptr) {
boolinq::from(labels).for_each([cache, messages, assign](Label* lbl) {
cache->addLabelsAssignmentsToCache(messages, lbl, assign);
});
}
return true;
}
bool ServiceRoot::onAfterLabelMessageAssignmentChanged(const QList<Label*> labels, const QList<Message>& messages, bool assign) {
Q_UNUSED(messages)
Q_UNUSED(assign)
boolinq::from(labels).for_each([](Label* lbl) {
lbl->updateCounts(true);
});
auto list = boolinq::from(labels).select([](Label* lbl) {
return static_cast<RootItem*>(lbl);
}).toStdList();
getParentServiceRoot()->itemChanged(FROM_STD_LIST(QList<RootItem*>, list));
return true;
}
bool ServiceRoot::onBeforeMessagesRestoredFromBin(RootItem* selected_item, const QList<Message>& messages) { bool ServiceRoot::onBeforeMessagesRestoredFromBin(RootItem* selected_item, const QList<Message>& messages) {
Q_UNUSED(selected_item) Q_UNUSED(selected_item)
Q_UNUSED(messages) Q_UNUSED(messages)

View File

@ -123,6 +123,12 @@ class ServiceRoot : public RootItem {
// by the user from message list. // by the user from message list.
virtual bool onAfterMessagesDelete(RootItem* selected_item, const QList<Message>& messages); virtual bool onAfterMessagesDelete(RootItem* selected_item, const QList<Message>& messages);
// Called BEFORE some labels are assigned/deassigned from/to messages.
virtual bool onBeforeLabelMessageAssignmentChanged(const QList<Label*> labels, const QList<Message>& messages, bool assign);
// Called AFTER some labels are assigned/deassigned from/to messages.
virtual bool onAfterLabelMessageAssignmentChanged(const QList<Label*> labels, const QList<Message>& messages, bool assign);
// Called BEFORE the list of messages is about to be restored from recycle bin // Called BEFORE the list of messages is about to be restored from recycle bin
// by the user from message list. // by the user from message list.
// Selected item is naturally recycle bin. // Selected item is naturally recycle bin.

View File

@ -209,8 +209,8 @@ QString GmailServiceRoot::additionalTooltip() const {
} }
void GmailServiceRoot::saveAllCachedData(bool async) { void GmailServiceRoot::saveAllCachedData(bool async) {
QPair<QMap<RootItem::ReadStatus, QStringList>, QMap<RootItem::Importance, QList<Message>>> msgCache = takeMessageCache(); auto msg_cache = takeMessageCache();
QMapIterator<RootItem::ReadStatus, QStringList> i(msgCache.first); QMapIterator<RootItem::ReadStatus, QStringList> i(msg_cache.m_cachedStatesRead);
// Save the actual data read/unread. // Save the actual data read/unread.
while (i.hasNext()) { while (i.hasNext()) {
@ -223,7 +223,7 @@ void GmailServiceRoot::saveAllCachedData(bool async) {
} }
} }
QMapIterator<RootItem::Importance, QList<Message>> j(msgCache.second); QMapIterator<RootItem::Importance, QList<Message>> j(msg_cache.m_cachedStatesImportant);
// Save the actual data important/not important. // Save the actual data important/not important.
while (j.hasNext()) { while (j.hasNext()) {

View File

@ -133,8 +133,8 @@ RootItem* InoreaderServiceRoot::obtainNewTreeForSyncIn() const {
} }
void InoreaderServiceRoot::saveAllCachedData(bool async) { void InoreaderServiceRoot::saveAllCachedData(bool async) {
QPair<QMap<RootItem::ReadStatus, QStringList>, QMap<RootItem::Importance, QList<Message>>> msgCache = takeMessageCache(); auto msg_cache = takeMessageCache();
QMapIterator<RootItem::ReadStatus, QStringList> i(msgCache.first); QMapIterator<RootItem::ReadStatus, QStringList> i(msg_cache.m_cachedStatesRead);
// Save the actual data read/unread. // Save the actual data read/unread.
while (i.hasNext()) { while (i.hasNext()) {
@ -147,7 +147,7 @@ void InoreaderServiceRoot::saveAllCachedData(bool async) {
} }
} }
QMapIterator<RootItem::Importance, QList<Message>> j(msgCache.second); QMapIterator<RootItem::Importance, QList<Message>> j(msg_cache.m_cachedStatesImportant);
// Save the actual data important/not important. // Save the actual data important/not important.
while (j.hasNext()) { while (j.hasNext()) {

View File

@ -86,8 +86,8 @@ OwnCloudNetworkFactory* OwnCloudServiceRoot::network() const {
} }
void OwnCloudServiceRoot::saveAllCachedData(bool async) { void OwnCloudServiceRoot::saveAllCachedData(bool async) {
QPair<QMap<RootItem::ReadStatus, QStringList>, QMap<RootItem::Importance, QList<Message>>> msgCache = takeMessageCache(); auto msg_cache = takeMessageCache();
QMapIterator<RootItem::ReadStatus, QStringList> i(msgCache.first); QMapIterator<RootItem::ReadStatus, QStringList> i(msg_cache.m_cachedStatesRead);
// Save the actual data read/unread. // Save the actual data read/unread.
while (i.hasNext()) { while (i.hasNext()) {
@ -100,7 +100,7 @@ void OwnCloudServiceRoot::saveAllCachedData(bool async) {
} }
} }
QMapIterator<RootItem::Importance, QList<Message>> j(msgCache.second); QMapIterator<RootItem::Importance, QList<Message>> j(msg_cache.m_cachedStatesImportant);
// Save the actual data important/not important. // Save the actual data important/not important.
while (j.hasNext()) { while (j.hasNext()) {

View File

@ -117,8 +117,8 @@ bool TtRssServiceRoot::canBeDeleted() const {
} }
void TtRssServiceRoot::saveAllCachedData(bool async) { void TtRssServiceRoot::saveAllCachedData(bool async) {
QPair<QMap<RootItem::ReadStatus, QStringList>, QMap<RootItem::Importance, QList<Message>>> msgCache = takeMessageCache(); auto msg_cache = takeMessageCache();
QMapIterator<RootItem::ReadStatus, QStringList> i(msgCache.first); QMapIterator<RootItem::ReadStatus, QStringList> i(msg_cache.m_cachedStatesRead);
// Save the actual data read/unread. // Save the actual data read/unread.
while (i.hasNext()) { while (i.hasNext()) {
@ -136,7 +136,7 @@ void TtRssServiceRoot::saveAllCachedData(bool async) {
} }
} }
QMapIterator<RootItem::Importance, QList<Message>> j(msgCache.second); QMapIterator<RootItem::Importance, QList<Message>> j(msg_cache.m_cachedStatesImportant);
// Save the actual data important/not important. // Save the actual data important/not important.
while (j.hasNext()) { while (j.hasNext()) {