Update: minor adjustments to CMakeLists.txt, ComposePage.qml, emailmanager.cpp, main.cpp

This commit is contained in:
Padrino
2026-06-04 18:29:16 +02:00
parent acaf741eb9
commit c5704b78a4
4 changed files with 159 additions and 45 deletions
+4 -3
View File
@@ -65,6 +65,7 @@ set(SRC_FILES
src/core/eventbus.cpp src/core/eventbus.cpp
src/utils/notificationmanager.cpp src/utils/notificationmanager.cpp
src/core/emailmanager.cpp src/core/emailmanager.cpp
src/core/emailcomposerbridge.cpp
src/core/synchronizerprovider.cpp src/core/synchronizerprovider.cpp
src/syncscheduler.cpp src/syncscheduler.cpp
src/core/accountsetupdialoglauncher.cpp src/core/accountsetupdialoglauncher.cpp
@@ -76,7 +77,7 @@ set(SRC_FILES
add_executable(wino-mail-qt ${SRC_FILES}) add_executable(wino-mail-qt ${SRC_FILES})
# Link Qt # Link Qt
target_link_libraries(wino-mail-qt PRIVATE Qt6::Core Qt6::Gui Qt6::Widgets Qt6::Network Qt6::Sql Qt6::WebEngineWidgets Qt6::Qml Qt6::Quick Qt6::QuickControls2 Qt6::Multimedia) target_link_libraries(wino-mail-qt PRIVATE Qt6::Core Qt6::Gui Qt6::Widgets Qt6::Network Qt6::Sql Qt6::WebEngineWidgets Qt6::Qml Qt6::Quick Qt6::Multimedia)
# Link DTKWidget if found # Link DTKWidget if found
if(USE_DTKWIDGET AND DTKWIDGET_FOUND) if(USE_DTKWIDGET AND DTKWIDGET_FOUND)
@@ -84,8 +85,8 @@ if(USE_DTKWIDGET AND DTKWIDGET_FOUND)
endif() endif()
# If using external libs # If using external libs
# target_link_libraries(wino-mail-qt PRIVATE ${LIBETPAN_LIBRARIES} ${GMIME_LIBRARIES}) target_link_libraries(wino-mail-qt PRIVATE ${GMIME_LIBRARIES})
# target_include_directories(wino-mail-qt PRIVATE ${LIBETPAN_INCLUDE_DIRS} ${GMIME_INCLUDE_DIRS}) target_include_directories(wino-mail-qt PRIVATE ${GMIME_INCLUDE_DIRS})
# Install (optional) # Install (optional)
install(TARGETS wino-mail-qt DESTINATION bin) install(TARGETS wino-mail-qt DESTINATION bin)
+44 -8
View File
@@ -1,11 +1,16 @@
import QtQuick 2.15 import QtQuick 2.15
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
import QtWebEngine 1.15
import QtQml 2.15
Item { Item {
id: composePage id: composePage
anchors.fill: parent anchors.fill: parent
// Expose a bridge to C++ for sending email and getting Quill content
property var emailBridge: emailComposerBridge
// Back button // Back button
Rectangle { Rectangle {
anchors { anchors {
@@ -69,17 +74,47 @@ Item {
Layout.fillWidth: true Layout.fillWidth: true
} }
// Body // Body - WebEngineView loading editor.html with Quill
Text { Text {
text: qsTr("Body:") text: qsTr("Body:")
font.pointSize: 14 font.pointSize: 14
Layout.alignment: Qt.AlignLeft Layout.alignment: Qt.AlignLeft
} }
TextArea { WebEngineView {
id: bodyArea id: bodyWebView
placeholderText: qsTr("Write your message...")
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
url: "qrc:/resources/web/editor.html"
// Settings to enable webchannel
settings.javascriptEnabled: true
settings.localContentCanAccessRemoteUrls: true
settings.localContentCanAccessFileUrls: true
}
// Attachments area (simple list placeholder)
RowLayout {
id: attachmentsRow
Layout.fillWidth: true
spacing: 5
// Will be populated dynamically via C++ model or JS array
// For now, placeholder
Text {
text: qsTr("Attachments: ")
font.pointSize: 12
}
Text {
id: attachmentsText
text: qsTr("None")
font.pointSize: 12
color: "#666"
}
Button {
text: qsTr("Attach")
onClicked: {
// Call C++ to open file dialog and get selected files
emailBridge.openAttachmentDialog()
}
}
} }
// Send button // Send button
@@ -93,10 +128,11 @@ Item {
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked: { onClicked: {
// TODO: Implement sending email // Trigger sending via bridge
console.log("Sending email to:", toField.text, "subject:", subjectField.text) emailBridge.sendComposedEmail(
// For now, just go back toField.text,
StackView.view.pop() subjectField.text
)
} }
} }
Text { Text {
+106 -34
View File
@@ -1,3 +1,20 @@
#include <gmime/gmime.h>
#include <stdio.h>
// Undefine macros that conflict with Qt's signals/slots
#ifdef public
#undef public
#endif
#ifdef signals
#undef signals
#endif
#ifdef slots
#undef slots
#endif
#ifdef emit
#undef emit
#endif
#include "emailmanager.h" #include "emailmanager.h"
#include <QDir> #include <QDir>
#include <QStandardPaths> #include <QStandardPaths>
@@ -5,6 +22,45 @@
#include <QFile> #include <QFile>
#include <QMessageBox> #include <QMessageBox>
/* Helper: recursively find a part with given subtype */
static GMimeObject *find_part_by_subtype(GMimeObject *obj, const char *subtype)
{
if (!obj)
return nullptr;
GMimeContentType *ctype = g_mime_object_get_content_type(obj);
if (ctype &&
g_ascii_strcasecmp(g_mime_content_type_get_media_subtype(ctype), subtype) == 0)
return obj;
if (GMIME_IS_MULTIPART(obj)) {
GMimeMultipart *multipart = GMIME_MULTIPART(obj);
guint count = g_mime_multipart_get_count(multipart);
for (guint i = 0; i < count; ++i) {
GMimeObject *part = g_mime_multipart_get_part(multipart, i);
GMimeObject *found = find_part_by_subtype(part, subtype);
if (found)
return found;
g_object_unref(part);
}
}
return nullptr;
}
/* Helper: extract content as QString */
static QString extract_content_as_string(GMimeObject *obj)
{
GMimeStream *stream = g_mime_stream_mem_new();
g_mime_object_write_to_stream(obj, nullptr, stream);
GMimeStreamMem *mem = GMIME_STREAM_MEM(stream);
GByteArray *array = g_mime_stream_mem_get_byte_array(mem);
const char *data = reinterpret_cast<const char*>(array->data);
gsize size = array->len;
QString result = QString::fromUtf8(data, size);
g_object_unref(stream);
return result;
}
EmailManager::EmailManager(QObject *parent) EmailManager::EmailManager(QObject *parent)
: QObject(parent) : QObject(parent)
{ {
@@ -12,70 +68,86 @@ EmailManager::EmailManager(QObject *parent)
MailItem EmailManager::getMailItemById(qint64 id) const MailItem EmailManager::getMailItemById(qint64 id) const
{ {
// Use the DAO to fetch the MailItem by id
auto optItem = MailItemDao::findById(id); auto optItem = MailItemDao::findById(id);
if (optItem.has_value()) { return optItem.has_value() ? optItem.value() : MailItem();
return optItem.value();
}
// Return an empty MailItem if not found
return MailItem();
} }
QString EmailManager::getStorageDirectory() const QString EmailManager::getStorageDirectory() const
{ {
// Define where .eml files are stored (e.g., in the application data directory)
QString storagePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); QString storagePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
QDir dir(storagePath); QDir dir(storagePath);
if (!dir.exists()) { if (!dir.exists())
dir.mkpath("."); // create if it doesn't exist dir.mkpath(".");
}
// Assuming .eml files are stored directly in this directory
return storagePath; return storagePath;
} }
QString EmailManager::getEmailHtmlById(qint64 id) const QString EmailManager::getEmailHtmlById(qint64 id) const
{ {
// Get the MailItem by id
MailItem item = getMailItemById(id); MailItem item = getMailItemById(id);
if (item.id() == 0) { if (item.id() == 0)
return "<html><body><h2>Error: Email not found</h2></body></html>"; return "<html><body><h2>Error: Email not found</h2></body></html>";
}
// Construct the file path: storage directory + fileId + .eml
QString storageDir = getStorageDirectory(); QString storageDir = getStorageDirectory();
QString fileName = item.fileId(); QString fileName = item.fileId();
if (fileName.isEmpty()) { if (fileName.isEmpty())
return "<html><body><h2>Error: Email file ID is empty</h2></body></html>"; return "<html><body><h2>Error: Email file ID is empty</h2></body></html>";
}
QString filePath = storageDir + QDir::separator() + fileName + ".eml"; QString filePath = storageDir + QDir::separator() + fileName + ".eml";
// Convert the .eml file to HTML
return convertEmlToHtml(filePath); return convertEmlToHtml(filePath);
} }
QString EmailManager::convertEmlToHtml(const QString& emlFilePath) const QString EmailManager::convertEmlToHtml(const QString& emlFilePath) const
{ {
// TODO: Implement actual MIME to HTML conversion using gmime or similar static bool initialized = false;
// For now, return a placeholder indicating the feature is not yet implemented if (!initialized) {
QFile file(emlFilePath); g_mime_init();
if (!file.exists()) { initialized = true;
return "<html><body><h2>Error: Email file not found</h2></body></html>";
} }
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
FILE *fp = fopen(emlFilePath.toLocal8Bit().constData(), "r");
if (!fp)
return "<html><body><h2>Error: Cannot open email file</h2></body></html>"; return "<html><body><h2>Error: Cannot open email file</h2></body></html>";
GMimeStream *istream = g_mime_stream_fs_new(fileno(fp));
GMimeParser *parser = g_mime_parser_new_with_stream(istream);
GMimeMessage *message = g_mime_parser_construct_message(parser, nullptr);
g_object_unref(parser);
g_object_unref(istream);
fclose(fp);
if (!message)
return "<html><body><h2>Error: Failed to parse email message</h2></body></html>";
/* Try to get HTML part */
GMimeObject *htmlPart = find_part_by_subtype(GMIME_OBJECT(message), "html");
if (htmlPart) {
QString html = extract_content_as_string(htmlPart);
g_object_unref(htmlPart);
g_object_unref(message);
return html;
} }
QByteArray data = file.readAll();
file.close(); /* Fallback to plain text */
// Very basic conversion: just show raw content in a pre tag for now GMimeObject *textPart = find_part_by_subtype(GMIME_OBJECT(message), "plain");
QString content = QString::fromUtf8(data); if (textPart) {
content.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;"); QString plain = extract_content_as_string(textPart);
return QString("<html><body style='font-family: monospace;'><h2>Email Content (raw)</h2><pre>%1</pre></body></html>") g_object_unref(textPart);
.arg(content); g_object_unref(message);
QString escaped = plain;
escaped.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;");
return QString("<html><body style='font-family:monospace;'><pre>%1</pre></body></html>")
.arg(escaped);
}
/* No displayable part */
g_object_unref(message);
return "<html><body><h2>Error: No displayable content in email</h2></body></html>";
} }
bool EmailManager::sendEmail(const QString& to, const QString& subject, const QString& body) bool EmailManager::sendEmail(const QString& to, const QString& subject, const QString& body)
{ {
// TODO: Implement actual email sending /* TODO: Implement real sending via RequestProcessor */
// For now, just show a message and return true QMessageBox::information(nullptr, "Send Email",
QMessageBox::information(nullptr, "Send Email",
QString("To: %1\nSubject: %2\nBody: %3").arg(to, subject, body)); QString("To: %1\nSubject: %2\nBody: %3").arg(to, subject, body));
return true; return true;
} }
+5
View File
@@ -4,6 +4,7 @@
#include "core/translator.h" #include "core/translator.h"
#include "db/dbchangeprocessor.h" #include "db/dbchangeprocessor.h"
#include "core/emailmanager.h" #include "core/emailmanager.h"
#include "core/emailcomposerbridge.h"
#include "syncscheduler.h" #include "syncscheduler.h"
#include "utils/notificationmanager.h" #include "utils/notificationmanager.h"
#include "core/accountsetupdialoglauncher.h" #include "core/accountsetupdialoglauncher.h"
@@ -24,6 +25,9 @@ int main(int argc, char *argv[])
// Create EmailManager to expose to QML // Create EmailManager to expose to QML
EmailManager emailManager(&app); EmailManager emailManager(&app);
// Create EmailComposerBridge to expose to QML
EmailComposerBridge emailComposerBridge(&app);
// Create and start the sync scheduler // Create and start the sync scheduler
SyncScheduler syncScheduler(&app); SyncScheduler syncScheduler(&app);
syncScheduler.start(); syncScheduler.start();
@@ -38,6 +42,7 @@ int main(int argc, char *argv[])
QQmlApplicationEngine engine; QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("translator", static_cast<QObject*>(&translator)); engine.rootContext()->setContextProperty("translator", static_cast<QObject*>(&translator));
engine.rootContext()->setContextProperty("emailManager", &emailManager); engine.rootContext()->setContextProperty("emailManager", &emailManager);
engine.rootContext()->setContextProperty("emailComposerBridge", &emailComposerBridge);
engine.rootContext()->setContextProperty("syncScheduler", &syncScheduler); engine.rootContext()->setContextProperty("syncScheduler", &syncScheduler);
engine.rootContext()->setContextProperty("notificationManager", &notificationManager); engine.rootContext()->setContextProperty("notificationManager", &notificationManager);
engine.rootContext()->setContextProperty("accountSetupDialogLauncher", &accountSetupDialogLauncher); engine.rootContext()->setContextProperty("accountSetupDialogLauncher", &accountSetupDialogLauncher);