File indexing completed on 2025-01-19 04:51:58

0001 /*
0002     Copyright (c) 2016 Michael Bohlender <michael.bohlender@kdemail.net>
0003     Copyright (c) 2016 Christian Mollekopf <mollekopf@kolabsys.com>
0004 
0005     This library is free software; you can redistribute it and/or modify it
0006     under the terms of the GNU Library General Public License as published by
0007     the Free Software Foundation; either version 2 of the License, or (at your
0008     option) any later version.
0009 
0010     This library is distributed in the hope that it will be useful, but WITHOUT
0011     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0012     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
0013     License for more details.
0014 
0015     You should have received a copy of the GNU Library General Public License
0016     along with this library; see the file COPYING.LIB.  If not, write to the
0017     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
0018     02110-1301, USA.
0019 */
0020 
0021 #include "folderlistmodel.h"
0022 #include <sink/store.h>
0023 #include <sink/log.h>
0024 #include <sink/notifier.h>
0025 #include <sink/notification.h>
0026 #include <settings/settings.h>
0027 
0028 using namespace Sink;
0029 using namespace Sink::ApplicationDomain;
0030 
0031 FolderListModel::FolderListModel(QObject *parent) : KRecursiveFilterProxyModel(parent)
0032 {
0033     setDynamicSortFilter(true);
0034     sort(0, Qt::AscendingOrder);
0035 
0036     //Automatically fetch all folders, otherwise the recursive filtering does not work.
0037     QObject::connect(this, &QSortFilterProxyModel::sourceModelChanged, [this] () {
0038         if (sourceModel()) {
0039             QObject::connect(sourceModel(), &QAbstractItemModel::rowsInserted, sourceModel(), [this] (QModelIndex parent, int first, int last) {
0040                 for (int row = first; row <= last; row++) {
0041                     auto idx = sourceModel()->index(row, 0, parent);
0042                     sourceModel()->fetchMore(idx);
0043                 }
0044             });
0045         }
0046     });
0047 }
0048 
0049 FolderListModel::~FolderListModel()
0050 {
0051 
0052 }
0053 
0054 QHash< int, QByteArray > FolderListModel::roleNames() const
0055 {
0056     QHash<int, QByteArray> roles;
0057 
0058     roles[Name] = "name";
0059     roles[Icon] = "icon";
0060     roles[Id] = "id";
0061     roles[DomainObject] = "domainObject";
0062     roles[Status] = "status";
0063     roles[Trash] = "trash";
0064     roles[Enabled] = "enabled";
0065     roles[HasNewData] = "hasNewData";
0066 
0067     return roles;
0068 }
0069 
0070 QVariant FolderListModel::data(const QModelIndex &idx, int role) const
0071 {
0072     auto srcIdx = mapToSource(idx);
0073     auto folder = srcIdx.data(Sink::Store::DomainObjectRole).value<Sink::ApplicationDomain::Folder::Ptr>();
0074     switch (role) {
0075         case Name:
0076             return folder->getName();
0077         case Icon:
0078             return folder->getIcon();
0079         case Id:
0080             return folder->identifier();
0081         case DomainObject:
0082             return QVariant::fromValue(folder);
0083         case Status: {
0084             switch (srcIdx.data(Sink::Store::StatusRole).toInt()) {
0085                 case Sink::ApplicationDomain::SyncStatus::SyncInProgress:
0086                     return InProgressStatus;
0087                 case Sink::ApplicationDomain::SyncStatus::SyncError:
0088                     return ErrorStatus;
0089                 case Sink::ApplicationDomain::SyncStatus::SyncSuccess:
0090                     return SuccessStatus;
0091             }
0092             return NoStatus;
0093         }
0094         case Trash:
0095             return folder->getSpecialPurpose().contains(Sink::ApplicationDomain::SpecialPurpose::Mail::trash);
0096         case Enabled:
0097             return folder->getEnabled();
0098         case HasNewData:
0099             return mHasNewData.contains(folder->identifier());
0100     }
0101     return QSortFilterProxyModel::data(idx, role);
0102 }
0103 
0104 static QModelIndex findRecursive(QAbstractItemModel *model, const QModelIndex &parent, int role, const QVariant &value)
0105 {
0106     for (auto row = 0; row < model->rowCount(parent); row++) {
0107         const auto idx = model->index(row, 0, parent);
0108         if (model->data(idx, role) == value) {
0109             return idx;
0110         }
0111         auto result = findRecursive(model, idx, role, value);
0112         if (result.isValid()) {
0113             return result;
0114         }
0115     }
0116     return {};
0117 }
0118 
0119 void FolderListModel::runQuery(const Query &query)
0120 {
0121     mModel = Store::loadModel<Folder>(query);
0122     QObject::connect(mModel.data(), &QAbstractItemModel::dataChanged, this, [this](const QModelIndex &, const QModelIndex &, const QVector<int> &roles) {
0123         if (roles.contains(Sink::Store::ChildrenFetchedRole)) {
0124             emit initialItemsLoaded();
0125         }
0126     });
0127     setSourceModel(mModel.data());
0128     if (!mModel->canFetchMore({})) {
0129         emit initialItemsLoaded();
0130     }
0131 
0132     Sink::Query resourceQuery;
0133     resourceQuery.setFilter(query.getResourceFilter());
0134     mNotifier.reset(new Sink::Notifier{resourceQuery});
0135     mNotifier->registerHandler([&](const Sink::Notification &notification) {
0136         if (notification.type == Sink::Notification::Info && notification.code == ApplicationDomain::NewContentAvailable) {
0137             if (!notification.entities.isEmpty()) {
0138                 mHasNewData.insert(notification.entities.first());
0139                 auto idx = findRecursive(this, {}, Id, QVariant::fromValue(notification.entities.first()));
0140                 if (idx.isValid()) {
0141                     emit dataChanged(idx, idx);
0142                 }
0143             }
0144         }
0145     });
0146 }
0147 
0148 void FolderListModel::setAccountId(const QVariant &accountId)
0149 {
0150     const auto account = accountId.toString().toUtf8();
0151 
0152     //Get all folders of an account
0153     auto query = Query();
0154     query.resourceFilter<SinkResource::Account>(account);
0155     query.setFlags(Sink::Query::LiveQuery | Sink::Query::UpdateStatus);
0156     query.request<Folder::Name>()
0157          .request<Folder::Icon>()
0158          .request<Folder::Parent>()
0159          .request<Folder::Enabled>()
0160          .request<Folder::SpecialPurpose>();
0161     query.requestTree<Folder::Parent>();
0162     query.setId("foldertree" + account);
0163     runQuery(query);
0164 }
0165 
0166 QVariant FolderListModel::accountId() const
0167 {
0168     return {};
0169 }
0170 
0171 static int getPriority(const Sink::ApplicationDomain::Folder &folder)
0172 {
0173     auto specialPurpose = folder.getSpecialPurpose();
0174     if (specialPurpose.contains(Sink::ApplicationDomain::SpecialPurpose::Mail::inbox)) {
0175         return 5;
0176     } else if (specialPurpose.contains(Sink::ApplicationDomain::SpecialPurpose::Mail::drafts)) {
0177         return 6;
0178     } else if (specialPurpose.contains(Sink::ApplicationDomain::SpecialPurpose::Mail::sent)) {
0179         return 7;
0180     } else if (specialPurpose.contains(Sink::ApplicationDomain::SpecialPurpose::Mail::trash)) {
0181         return 8;
0182     } else if (!specialPurpose.isEmpty()) {
0183         return 9;
0184     }
0185     return 10;
0186 }
0187 
0188 bool FolderListModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
0189 {
0190     const auto leftFolder = left.data(Sink::Store::DomainObjectRole).value<Sink::ApplicationDomain::Folder::Ptr>();
0191     const auto rightFolder = right.data(Sink::Store::DomainObjectRole).value<Sink::ApplicationDomain::Folder::Ptr>();
0192     const auto leftPriority = getPriority(*leftFolder);
0193     const auto rightPriority = getPriority(*rightFolder);
0194     if (leftPriority == rightPriority) {
0195         return leftFolder->getName() < rightFolder->getName();
0196     }
0197     return leftPriority < rightPriority;
0198 }
0199 
0200 bool FolderListModel::acceptRow(int sourceRow, const QModelIndex &sourceParent) const
0201 {
0202     auto index = sourceModel()->index(sourceRow, 0, sourceParent);
0203     Q_ASSERT(index.isValid());
0204     const auto folder = index.data(Sink::Store::DomainObjectRole).value<Sink::ApplicationDomain::Folder::Ptr>();
0205     Q_ASSERT(folder);
0206     const auto enabled = folder->getEnabled();
0207     return enabled;
0208 }
0209 
0210 void FolderListModel::fetchMore(const QModelIndex &parent)
0211 {
0212     mHasNewData.remove(parent.data(Id).toByteArray());
0213     QAbstractItemModel::fetchMore(parent);
0214 }
0215 
0216 void FolderListModel::setFolderId(const QVariant &folderId)
0217 {
0218     const auto folder = folderId.toString().toUtf8();
0219     if (folder.isEmpty()) {
0220         setSourceModel(nullptr);
0221         mModel.clear();
0222         return;
0223     }
0224 
0225     //Get all folders of an account
0226     auto query = Query();
0227     query.filter(folder);
0228     query.request<Folder::Name>()
0229          .request<Folder::Icon>()
0230          .request<Folder::Parent>()
0231          .request<Folder::Enabled>()
0232          .request<Folder::SpecialPurpose>();
0233     query.setId("folder" + folder);
0234     runQuery(query);
0235 }
0236 
0237 QVariant FolderListModel::folderId() const
0238 {
0239     return {};
0240 }