Commit bf4a8783 authored by Carl Schwan's avatar Carl Schwan 🚴 Committed by Claudio Cambra
Browse files

Start adding mail integration



Signed-off-by: Carl Schwan's avatarCarl Schwan <carl@carlschwan.eu>
parent 90d16ff2
......@@ -14,10 +14,7 @@ project(kalendar VERSION ${RELEASE_SERVICE_VERSION})
set(QT_MIN_VERSION "5.15.2")
set(KF5_MIN_VERSION "5.92.0")
set(AKONADI_VERSION "5.19.0")
set(AKONADI_CONTACT_VERSION "5.19.0")
set(CALENDARSUPPORT_LIB_VERSION "5.19")
set(EVENTVIEW_LIB_VERSION "5.19.0")
set(PIM_VERSION "5.19.0")
include(FeatureSummary)
......@@ -57,10 +54,7 @@ find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Kirigami2 DBusAddons I18
set_package_properties(KF5QQC2DesktopStyle PROPERTIES
TYPE RUNTIME
)
find_package(KF5Akonadi ${AKONADI_VERSION} CONFIG REQUIRED)
find_package(KF5AkonadiContact ${AKONADI_CONTACT_VERSION} CONFIG REQUIRED)
find_package(KF5CalendarSupport ${CALENDARSUPPORT_LIB_VERSION} CONFIG REQUIRED)
find_package(KF5EventViews ${EVENTVIEW_LIB_VERSION} CONFIG REQUIRED)
find_package(KF5 ${PIM_VERSION} REQUIRED COMPONENTS Akonadi AkonadiContact AkonadiCalendar MailCommon PimCommonAkonadi CalendarSupport EventViews)
option(USE_UNITY_CMAKE_SUPPORT "Use UNITY cmake support (speedup compile time)" OFF)
......
......@@ -5,6 +5,7 @@
add_subdirectory(lib)
add_subdirectory(quick)
add_subdirectory(contacts)
add_subdirectory(mail)
add_executable(kalendar)
......
......@@ -19,6 +19,7 @@ Kirigami.ScrollablePage {
topPadding: 0
property AddresseeWrapper addressee: AddresseeWrapper {
id: addressee
addresseeItem: ContactManager.getItem(page.itemId)
}
......
This diff is collapsed.
// SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org>
// SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
import org.kde.kalendar.mail 1.0
import org.kde.kalendar 1.0
FolderView {
objectName: "mailView"
property int mode: KalendarApplication.Mail
}
......@@ -51,6 +51,7 @@ Kirigami.ApplicationWindow {
readonly property var scheduleViewAction: KalendarApplication.action("open_schedule_view")
readonly property var contactViewAction: KalendarApplication.action("open_contact_view")
readonly property var todoViewAction: KalendarApplication.action("open_todo_view")
readonly property var mailViewAction: KalendarApplication.action("open_mail_view")
readonly property var moveViewForwardsAction: KalendarApplication.action("move_view_forwards")
readonly property var moveViewBackwardsAction: KalendarApplication.action("move_view_backwards")
readonly property var moveViewToTodayAction: KalendarApplication.action("move_view_to_today")
......@@ -125,6 +126,9 @@ Kirigami.ApplicationWindow {
case Config.ContactView:
contactViewAction.trigger();
break;
case Config.MailView:
mailViewAction.trigger();
break;
default:
Kirigami.Settings.isMobile ? scheduleViewAction.trigger() : monthViewAction.trigger();
break;
......@@ -197,6 +201,12 @@ Kirigami.ApplicationWindow {
}
}
function onOpenMailView() {
if(pageStack.currentItem.objectName !== "mailView" || root.ignoreCurrentPage) {
KalendarUiUtils.switchView("qrc:/LazyMailView.qml");
}
}
function onOpenTodoView() {
if(pageStack.currentItem.objectName !== "todoView") {
filterHeaderBar.active = true;
......@@ -439,6 +449,9 @@ Kirigami.ApplicationWindow {
case "contactView":
return i18n("Contacts View");
break;
case "mailView":
return i18n("Mail View");
break;
default:
return i18n("Calendar");
}
......
......@@ -169,7 +169,23 @@ void KalendarApplication::setupActions()
KalendarConfig::self()->save();
}
});
mCollection.setDefaultShortcut(openContactAction, QKeySequence(i18n("Ctrl+5")));
mCollection.setDefaultShortcut(openContactAction, QKeySequence(i18n("Ctrl+6")));
}
actionName = QLatin1String("open_mail_view");
if (KAuthorized::authorizeAction(actionName)) {
auto mailAction = mCollection.addAction(actionName, this, &KalendarApplication::openMailView);
mailAction->setText(i18n("Mail View"));
mailAction->setIcon(QIcon::fromTheme(QStringLiteral("gnumeric-link-email")));
mailAction->setCheckable(true);
mailAction->setActionGroup(m_viewGroup);
connect(mailAction, &QAction::toggled, this, [](bool checked) {
if (checked) {
KalendarConfig::setLastOpenedView(KalendarConfig::ThreeDayView);
KalendarConfig::self()->save();
}
});
mCollection.setDefaultShortcut(mailAction, QKeySequence(i18n("Ctrl+7")));
}
actionName = QLatin1String("open_month_view");
......
......@@ -38,6 +38,7 @@ public:
Event,
Todo,
Contact,
Mail,
};
Q_ENUM(Mode)
......@@ -69,6 +70,7 @@ Q_SIGNALS:
void openDayView();
void openScheduleView();
void openTodoView();
void openMailView();
void openContactView();
void openAboutPage();
void moveViewForwards();
......
......@@ -45,6 +45,7 @@ SPDX-License-Identifier: LGPL-2.0-or-later
<choice name="ThreeDayView"/>
<choice name="DayView"/>
<choice name="ContactView"/>
<choice name="MailView"/>
</choices>
<default>ScheduleView</default>
</entry>
......
# SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu>
# SPDX-License-Identifier: BSD-2-Clause
ecm_add_qml_module(kalendar_mail_plugin URI "org.kde.kalendar.mail" VERSION 1.0)
target_sources(kalendar_mail_plugin PRIVATE
mailplugin.cpp
mailplugin.h
mailmanager.cpp
mailmanager.h
mailmodel.cpp
mailmodel.h
)
ecm_target_qml_sources(kalendar_mail_plugin SOURCES
qml/MailSidebar.qml
qml/FolderView.qml
)
#ecm_target_qml_sources(kalendar_contact_plugin
# PRIVATE PATH private SOURCES
# qml/private/ContactListItem.qml
# qml/private/ContactPage.qml
# qml/private/ContactsPage.qml
# qml/private/Header.qml
# qml/private/PhoneNumberDialog.qml
# qml/private/QrCodePage.qml
#)
ecm_qt_declare_logging_category(kalendar_contact_plugin
HEADER kalendar_mail_debug.h
IDENTIFIER KALENDAR_MAIL_LOG
CATEGORY_NAME org.kde.kalendar.mail
DESCRIPTION "kalendar mail"
EXPORT KALENDAR
)
target_link_libraries(kalendar_mail_plugin PRIVATE kalendar_lib KF5::MailCommon KF5::AkonadiMime)
ecm_qt_install_logging_categories(
EXPORT KALENDAR
FILE kalendar.contact.categories
DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR}
)
#if (BUILD_TESTING)
# add_subdirectory(autotests)
#endif()
ecm_finalize_qml_module(kalendar_mail_plugin
DESTINATION ${KDE_INSTALL_QMLDIR}
BUILD_SHARED_LIBS OFF)
\ No newline at end of file
// SPDX-FileCopyrightText: 2020 Carl Schwan <carlschwan@kde.org>
// SPDX-License-Identifier: LGPL-2.0-or-later
#include "mailmanager.h"
#include "mailmodel.h"
#include <QTimer>
// Akonadi
#include <Akonadi/CollectionFilterProxyModel>
#include <Akonadi/ItemFetchScope>
#include <Akonadi/MessageModel>
#include <Akonadi/Monitor>
#include <Akonadi/Session>
#include <Akonadi/ChangeRecorder>
#include <MailCommon/FolderCollectionMonitor>
#include <KMime/Message>
#include <KDescendantsProxyModel>
#include <Akonadi/EntityMimeTypeFilterModel>
#include <Akonadi/EntityTreeModel>
#include <Akonadi/SelectionProxyModel>
#include <Akonadi/ServerManager>
#include <QApplication>
#include <QtCore/QItemSelectionModel>
#include <KItemModels/KDescendantsProxyModel>
MailManager::MailManager(QObject *parent)
: QObject(parent)
, m_loading(true)
{
using namespace Akonadi;
// mailModel
// ^
// |
// itemModel
// |
// flatModel
// |
// descendantsProxyModel ------> selectionModel
// ^ ^
// | |
// collectionFilter |
// \__________________treemodel
m_session = new Session(QByteArrayLiteral("KMailManager Kernel ETM"), this);
auto folderCollectionMonitor = new MailCommon::FolderCollectionMonitor(m_session, this);
// setup collection model
auto treeModel = new Akonadi::EntityTreeModel(folderCollectionMonitor->monitor(), this);
treeModel->setItemPopulationStrategy(Akonadi::EntityTreeModel::LazyPopulation);
m_foldersModel = new Akonadi::CollectionFilterProxyModel(this);
m_foldersModel->setSourceModel(treeModel);
m_foldersModel->addMimeTypeFilter(KMime::Message::mimeType());
// Setup selection model
m_collectionSelectionModel = new QItemSelectionModel(m_foldersModel);
auto selectionModel = new SelectionProxyModel(m_collectionSelectionModel, this);
selectionModel->setSourceModel(treeModel);
selectionModel->setFilterBehavior(KSelectionProxyModel::ChildrenOfExactSelection);
// Setup mail model
auto folderFilterModel = new EntityMimeTypeFilterModel(this);
folderFilterModel->setSourceModel(selectionModel);
folderFilterModel->setHeaderGroup(EntityTreeModel::ItemListHeaders);
folderFilterModel->addMimeTypeInclusionFilter(KMime::Message::mimeType());
folderFilterModel->addMimeTypeExclusionFilter(Collection::mimeType());
// Proxy for QML roles
m_folderModel = new MailModel(this);
m_folderModel->setSourceModel(folderFilterModel);
if (Akonadi::ServerManager::isRunning()) {
m_loading = false;
} else {
connect(Akonadi::ServerManager::self(), &Akonadi::ServerManager::stateChanged,
this, [this](Akonadi::ServerManager::State state) {
if (state == Akonadi::ServerManager::State::Broken) {
qApp->exit(-1);
return;
}
bool loading = state != Akonadi::ServerManager::State::Running;
if (loading == m_loading) {
return;
}
m_loading = loading;
Q_EMIT loadingChanged();
disconnect(Akonadi::ServerManager::self(), &Akonadi::ServerManager::stateChanged, this, nullptr);
});
}
Q_EMIT folderModelChanged();
Q_EMIT loadingChanged();
}
MailModel *MailManager::folderModel() const
{
return m_folderModel;
}
void MailManager::loadMailCollection(const QModelIndex &modelIndex)
{
if (!modelIndex.isValid()) {
qDebug() << "hello";
return;
}
m_collectionSelectionModel->select(modelIndex, QItemSelectionModel::ClearAndSelect);
}
bool MailManager::loading() const
{
return m_loading;
}
Akonadi::CollectionFilterProxyModel *MailManager::foldersModel() const
{
return m_foldersModel;
}
Akonadi::Session *MailManager::session() const
{
return m_session;
}
// SPDX-FileCopyrightText: 2020 Carl Schwan <carlschwan@kde.org>
// SPDX-License-Identifier: LGPL-2.0-or-later
#include <QObject>
namespace Akonadi {
class CollectionFilterProxyModel;
class EntityMimeTypeFilterModel;
class Session;
}
class QAbstractListModel;
class QItemSelectionModel;
class MailModel;
/// Class responsible for exposing the email folder selected by the user
class MailManager : public QObject
{
Q_OBJECT
Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged)
Q_PROPERTY(Akonadi::CollectionFilterProxyModel *foldersModel READ foldersModel CONSTANT)
Q_PROPERTY(MailModel *folderModel READ folderModel NOTIFY folderModelChanged)
public:
MailManager(QObject *parent = nullptr);
~MailManager() override = default;
bool loading() const;
Akonadi::CollectionFilterProxyModel *foldersModel() const;
MailModel *folderModel() const;
Akonadi::Session *session() const;
Q_INVOKABLE void loadMailCollection(const QModelIndex &index);
Q_SIGNALS:
void loadingChanged();
void folderModelChanged();
private:
bool m_loading;
Akonadi::Session *m_session;
Akonadi::CollectionFilterProxyModel *m_foldersModel;
//folders
QItemSelectionModel *m_collectionSelectionModel;
MailModel *m_folderModel;
};
// SPDX-FileCopyrightText: 2021 Simon Schmeisser <s.schmeisser@gmx.net>
// SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org>
// SPDX-License-Identifier: GPL-3.0-or-later
#include "mailmodel.h"
//#include "messagewrapper.h"
//#include "messageviewer/viewer.h"
#include <Akonadi/EntityTreeModel>
#include <KMime/Message>
#include <KLocalizedString>
#include <QQmlEngine>
MailModel::MailModel(QObject *parent)
: QIdentityProxyModel(parent)
// , m_viewerHelper(new ViewerHelper(this))
{
}
QHash<int, QByteArray> MailModel::roleNames() const
{
return {
{TitleRole, QByteArrayLiteral("title")},
{SenderRole, QByteArrayLiteral("sender")},
{UnreadRole, QByteArrayLiteral("unread")},
{FavoriteRole, QByteArrayLiteral("favorite")},
{TextColorRole, QByteArrayLiteral("textColor")},
{BackgroundColorRole, QByteArrayLiteral("backgroudColor")}
};
}
QVariant MailModel::data(const QModelIndex &index, int role) const
{
QVariant itemVariant = sourceModel()->data(mapToSource(index), Akonadi::EntityTreeModel::ItemRole);
Akonadi::Item item = itemVariant.value<Akonadi::Item>();
if (!item.hasPayload<KMime::Message::Ptr>()) {
return {};
}
const KMime::Message::Ptr mail = item.payload<KMime::Message::Ptr>();
//const Collection parentCol = parentCollectionForRow(row);
QString sender;
if (mail->from()) {
sender = mail->from()->asUnicodeString();
}
QString receiver;
if (mail->to()) {
receiver = mail->to()->asUnicodeString();
}
// Static for speed reasons
static const QString noSubject = i18nc("displayed as subject when the subject of a mail is empty", "No Subject");
static const QString unknown(i18nc("displayed when a mail has unknown sender, receiver or date", "Unknown"));
if (sender.isEmpty()) {
sender = unknown;
}
if (receiver.isEmpty()) {
receiver = unknown;
}
//mi->initialSetup(mail->date()->dateTime().toSecsSinceEpoch(), item.size(), sender, receiver, bUseReceiver);
//mi->setItemId(item.id());
//mi->setParentCollectionId(parentCol.id());
QString subject = mail->subject()->asUnicodeString();
if (subject.isEmpty()) {
subject = QLatin1Char('(') + noSubject + QLatin1Char(')');
}
//mi->setSubject(subject);
//auto it = d->mFolderHash.find(item.storageCollectionId());
//if (it == d->mFolderHash.end()) {
// QString folder;
// Collection collection = collectionForId(item.storageCollectionId());
// while (collection.parentCollection().isValid()) {
// folder = collection.displayName() + QLatin1Char('/') + folder;
// collection = collection.parentCollection();
// }
// folder.chop(1);
// it = d->mFolderHash.insert(item.storageCollectionId(), folder);
//}
//mi->setFolder(it.value());
// NOTE: remember to update AkonadiBrowserSortModel::lessThan if you insert/move columns
switch (role) {
case TitleRole:
if (mail->subject()) {
return mail->subject()->asUnicodeString();
} else {
return QStringLiteral("(No subject)");
}
case SenderRole:
if (mail->from()) {
return mail->from()->asUnicodeString();
} else {
return QString();
}
case DateRole:
if (mail->date()) {
return mail->date()->asUnicodeString();
} else {
return QString();
}
case MailRole:
{
//auto wrapper = new MessageWrapper(item);
//QQmlEngine::setObjectOwnership(wrapper, QQmlEngine::JavaScriptOwnership);
//return QVariant::fromValue(wrapper);
}
}
return {};
}
void MailModel::loadItem(int row)
{
//if (!m_viewerHelper) {
// return;
//}
QVariant itemVariant = sourceModel()->data(mapToSource(index(row, 0)), Akonadi::EntityTreeModel::ItemRole);
Akonadi::Item item = itemVariant.value<Akonadi::Item>();
//m_viewerHelper->setMessageItem(item);
}
//void MailModel::setViewerHelper(ViewerHelper *viewerHelper)
//{
// if (m_viewerHelper == viewerHelper) {
// return;
// }
// m_viewerHelper = viewerHelper;
// Q_EMIT viewerHelperChanged();
//}
//
//ViewerHelper *MailModel::viewerHelper() const
//{
// return m_viewerHelper;
//}
// SPDX-FileCopyrightText: 2021 Simon Schmeisser <s.schmeisser@gmx.net>
// SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org>
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <QObject>
#include <QIdentityProxyModel>
#include <QItemSelectionModel>
//class ViewerHelper;
class MailModel : public QIdentityProxyModel
{
Q_OBJECT
// Q_PROPERTY(ViewerHelper *viewerHelper READ viewerHelper WRITE setViewerHelper NOTIFY viewerHelperChanged)
public:
enum AnimalRoles {
TitleRole = Qt::UserRole + 1,
SenderRole,
TextColorRole,
DateRole,
BackgroundColorRole,
UnreadRole,
MailRole,
FavoriteRole,
};
// ViewerHelper *viewerHelper() const;
// void setViewerHelper(ViewerHelper *viewerHelper);
explicit MailModel(QObject *parent = nullptr);
QHash<int, QByteArray> roleNames() const override;
virtual QVariant data(const QModelIndex &index, int role) const override;
Q_INVOKABLE void loadItem(int row);
///Q_SIGNALS:
/// void viewerHelperChanged();
///
///private:
/// ViewerHelper *m_viewerHelper = nullptr;
};
// SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu>
// SPDX-License-Identifier: LGPL-2.0-or-later
#include "mailplugin.h"
#include <QAbstractListModel>
#include <QQmlEngine>
#include <QtQml>
#include "mailmanager.h"
#include "mailmodel.h"
void CalendarPlugin::registerTypes(const char *uri)
{
Q_ASSERT(uri == QByteArray("org.kde.kalendar.mail"));
qmlRegisterSingletonType<MailManager>("org.kde.kalendar.mail", 1, 0, "MailManager", [](QQmlEngine *engine, QJSEngine *scriptEngine) {
Q_UNUSED(engine)
Q_UNUSED(scriptEngine)
return new MailManager;
});
qRegisterMetaType<MailModel*>("MailModel*");
}
// SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu>
// SPDX-License-Identifier: LGPL-2.0-or-later
#pragma once
#include <QQmlExtensionPlugin>
class CalendarPlugin : public QQmlExtensionPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
public:
void registerTypes(const char *uri) override;
};
\ No newline at end of file
// SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org>
// SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
import QtQuick 2.15
import QtQuick.Layouts 1.15
import org.kde.kirigami 2.14 as Kirigami
import QtQuick.Controls 2.15 as Controls
import org.kde.kalendar.mail 1.0
import org.kde.kitemmodels 1.0 as KItemModels
Kirigami.ScrollablePage {