From b6c5dea86eccb05480291c813771a584954babfd Mon Sep 17 00:00:00 2001 From: Padrino Date: Tue, 12 May 2026 01:08:09 +0200 Subject: [PATCH] Add FolderDao and MailItemDao, update MailItem model to include folderId, fileId, size, messageId --- CMakeLists.txt | 2 + src/core/mailitem.cpp | 11 +- src/core/mailitem.h | 68 ++++++++++++ src/db/dao/folderdao.cpp | 162 +++++++++++++++++++++++++++++ src/db/dao/folderdao.h | 17 +++ src/db/dao/mailitemdao.cpp | 208 +++++++++++++++++++++++++++++++++++++ src/db/dao/mailitemdao.h | 18 ++++ 7 files changed, 484 insertions(+), 2 deletions(-) create mode 100644 src/core/mailitem.h create mode 100644 src/db/dao/folderdao.cpp create mode 100644 src/db/dao/folderdao.h create mode 100644 src/db/dao/mailitemdao.cpp create mode 100644 src/db/dao/mailitemdao.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 1284161..3d076ce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,8 @@ set(SRC_FILES src/services/imap/imapsynchronizer.cpp src/db/databasemanager.cpp src/db/dao/accountdao.cpp + src/db/dao/folderdao.cpp + src/db/dao/mailitemdao.cpp ) # Executable diff --git a/src/core/mailitem.cpp b/src/core/mailitem.cpp index 08ac80a..e289d90 100644 --- a/src/core/mailitem.cpp +++ b/src/core/mailitem.cpp @@ -1,10 +1,14 @@ #include "mailitem.h" -MailItem::MailItem(qint64 id, const QString& subject, const QString& sender, +MailItem::MailItem(qint64 id, int folderId, const QString& subject, const QString& sender, const QString& recipient, const QDateTime& date, bool read, bool flagged, - const QVector& attachments) + const QVector& attachments, + const QString& fileId, + qint64 size, + const QString& messageId) : m_id(id) + , m_folderId(folderId) , m_subject(subject) , m_sender(sender) , m_recipient(recipient) @@ -12,5 +16,8 @@ MailItem::MailItem(qint64 id, const QString& subject, const QString& sender, , m_read(read) , m_flagged(flagged) , m_attachments(attachments) + , m_fileId(fileId) + , m_size(size) + , m_messageId(messageId) { } \ No newline at end of file diff --git a/src/core/mailitem.h b/src/core/mailitem.h new file mode 100644 index 0000000..4d62512 --- /dev/null +++ b/src/core/mailitem.h @@ -0,0 +1,68 @@ +#pragma once + +#include +#include +#include + +class MailItem +{ +public: + MailItem() = default; + MailItem(qint64 id, int folderId, const QString& subject, const QString& sender, + const QString& recipient, const QDateTime& date, + bool read = false, bool flagged = false, + const QVector& attachments = QVector(), + const QString& fileId = QString(), + qint64 size = 0, + const QString& messageId = QString()); + + qint64 id() const { return m_id; } + void setId(qint64 id) { m_id = id; } + + int folderId() const { return m_folderId; } + void setFolderId(int folderId) { m_folderId = folderId; } + + QString subject() const { return m_subject; } + void setSubject(const QString& subject) { m_subject = subject; } + + QString sender() const { return m_sender; } + void setSender(const QString& sender) { m_sender = sender; } + + QString recipient() const { return m_recipient; } + void setRecipient(const QString& recipient) { m_recipient = recipient; } + + QDateTime date() const { return m_date; } + void setDate(const QDateTime& date) { m_date = date; } + + bool isRead() const { return m_read; } + void setRead(bool read) { m_read = read; } + + bool isFlagged() const { return m_flagged; } + void setFlagged(bool flagged) { m_flagged = flagged; } + + QVector attachments() const { return m_attachments; } + void setAttachments(const QVector& attachments) { m_attachments = attachments; } + + QString fileId() const { return m_fileId; } + void setFileId(const QString& fileId) { m_fileId = fileId; } + + qint64 size() const { return m_size; } + void setSize(qint64 size) { m_size = size; } + + QString messageId() const { return m_messageId; } + void setMessageId(const QString& messageId) { m_messageId = messageId; } + +private: + qint64 m_id{0}; + int m_folderId{0}; + QString m_subject; + QString m_sender; + QString m_recipient; + QDateTime m_date; + bool m_read{false}; + bool m_flagged{false}; + QVector m_attachments; + QString m_fileId; + qint64 m_size{0}; + QString m_messageId; +}; \ No newline at end of file diff --git a/src/db/dao/folderdao.cpp b/src/db/dao/folderdao.cpp new file mode 100644 index 0000000..733f267 --- /dev/null +++ b/src/db/dao/folderdao.cpp @@ -0,0 +1,162 @@ +#include "folderdao.h" +#include + +bool FolderDao::insert(const Folder& folder) +{ + QSqlDatabase& db = DatabaseManager::instance().database(); + QSqlQuery query(db); + query.prepare( + "INSERT INTO Folder (accountId, name, parentFolderId, isInbox, isSent, isDrafts, isTrash, unreadCount, lastSynced) " + "VALUES (:accountId, :name, :parentFolderId, :isInbox, :isSent, :isDrafts, :isTrash, :unreadCount, :lastSynced)" + ); + query.bindValue(":accountId", folder.accountId()); + query.bindValue(":name", folder.name()); + query.bindValue(":parentFolderId", folder.parentFolderId()); + query.bindValue(":isInbox", folder.isInbox() ? 1 : 0); + query.bindValue(":isSent", folder.isSent() ? 1 : 0); + query.bindValue(":isDrafts", folder.isDrafts() ? 1 : 0); + query.bindValue(":isTrash", folder.isTrash() ? 1 : 0); + query.bindValue(":unreadCount", folder.unreadCount()); + query.bindValue(":lastSynced", folder.lastSynced()); + + if (!query.exec()) { + qWarning() << "Failed to insert folder:" << query.lastError().text(); + return false; + } + return true; +} + +bool FolderDao::update(const Folder& folder) +{ + QSqlDatabase& db = DatabaseManager::instance().database(); + QSqlQuery query(db); + query.prepare( + "UPDATE Folder SET " + "accountId = :accountId, " + "name = :name, " + "parentFolderId = :parentFolderId, " + "isInbox = :isInbox, " + "isSent = :isSent, " + "isDrafts = :isDrafts, " + "isTrash = :isTrash, " + "unreadCount = :unreadCount, " + "lastSynced = :lastSynced " + "WHERE id = :id" + ); + query.bindValue(":id", folder.id()); + query.bindValue(":accountId", folder.accountId()); + query.bindValue(":name", folder.name()); + query.bindValue(":parentFolderId", folder.parentFolderId()); + query.bindValue(":isInbox", folder.isInbox() ? 1 : 0); + query.bindValue(":isSent", folder.isSent() ? 1 : 0); + query.bindValue(":isDrafts", folder.isDrafts() ? 1 : 0); + query.bindValue(":isTrash", folder.isTrash() ? 1 : 0); + query.bindValue(":unreadCount", folder.unreadCount()); + query.bindValue(":lastSynced", folder.lastSynced()); + + if (!query.exec()) { + qWarning() << "Failed to update folder:" << query.lastError().text(); + return false; + } + return true; +} + +bool FolderDao::remove(int id) +{ + QSqlDatabase& db = DatabaseManager::instance().database(); + QSqlQuery query(db); + query.prepare("DELETE FROM Folder WHERE id = :id"); + query.bindValue(":id", id); + + if (!query.exec()) { + qWarning() << "Failed to delete folder:" << query.lastError().text(); + return false; + } + return true; +} + +std::optional FolderDao::findById(int id) +{ + QSqlDatabase& db = DatabaseManager::instance().database(); + QSqlQuery query(db); + query.prepare("SELECT id, accountId, name, parentFolderId, isInbox, isSent, isDrafts, isTrash, unreadCount, lastSynced FROM Folder WHERE id = :id"); + query.bindValue(":id", id); + + if (!query.exec()) { + qWarning() << "Failed to find folder by id:" << query.lastError().text(); + return std::nullopt; + } + + if (query.next()) { + Folder fld; + fld.setId(query.value(0).toInt()); + fld.setAccountId(query.value(1).toInt()); + fld.setName(query.value(2).toString()); + fld.setParentFolderId(query.value(3).toString()); + fld.setInbox(query.value(4).toBool()); + fld.setSent(query.value(5).toBool()); + fld.setDrafts(query.value(6).toBool()); + fld.setTrash(query.value(7).toBool()); + fld.setUnreadCount(query.value(8).toInt()); + fld.setLastSynced(query.value(9).toDateTime()); + return fld; + } + return std::nullopt; +} + +QVector FolderDao::findAll() +{ + QVector folders; + QSqlDatabase& db = DatabaseManager::instance().database(); + QSqlQuery query(db); + if (!query.exec("SELECT id, accountId, name, parentFolderId, isInbox, isSent, isDrafts, isTrash, unreadCount, lastSynced FROM Folder")) { + qWarning() << "Failed to fetch all folders:" << query.lastError().text(); + return folders; + } + + while (query.next()) { + Folder fld; + fld.setId(query.value(0).toInt()); + fld.setAccountId(query.value(1).toInt()); + fld.setName(query.value(2).toString()); + fld.setParentFolderId(query.value(3).toString()); + fld.setInbox(query.value(4).toBool()); + fld.setSent(query.value(5).toBool()); + fld.setDrafts(query.value(6).toBool()); + fld.setTrash(query.value(7).toBool()); + fld.setUnreadCount(query.value(8).toInt()); + fld.setLastSynced(query.value(9).toDateTime()); + folders.append(fld); + } + return folders; +} + +QVector FolderDao::findByAccountId(int accountId) +{ + QVector folders; + QSqlDatabase& db = DatabaseManager::instance().database(); + QSqlQuery query(db); + query.prepare("SELECT id, accountId, name, parentFolderId, isInbox, isSent, isDrafts, isTrash, unreadCount, lastSynced FROM Folder WHERE accountId = :accountId"); + query.bindValue(":accountId", accountId); + + if (!query.exec()) { + qWarning() << "Failed to fetch folders by account id:" << query.lastError().text(); + return folders; + } + + while (query.next()) { + Folder fld; + fld.setId(query.value(0).toInt()); + fld.setAccountId(query.value(1).toInt()); + fld.setName(query.value(2).toString()); + fld.setParentFolderId(query.value(3).toString()); + fld.setInbox(query.value(4).toBool()); + fld.setSent(query.value(5).toBool()); + fld.setDrafts(query.value(6).toBool()); + fld.setTrash(query.value(7).toBool()); + fld.setUnreadCount(query.value(8).toInt()); + fld.setLastSynced(query.value(9).toDateTime()); + folders.append(fld); + } + return folders; +} \ No newline at end of file diff --git a/src/db/dao/folderdao.h b/src/db/dao/folderdao.h new file mode 100644 index 0000000..00e7061 --- /dev/null +++ b/src/db/dao/folderdao.h @@ -0,0 +1,17 @@ +#pragma once + +#include "../databasemanager.h" +#include "../../core/models/folder.h" +#include +#include + +class FolderDao +{ +public: + static bool insert(const Folder& folder); + static bool update(const Folder& folder); + static bool remove(int id); + static std::optional findById(int id); + static QVector findAll(); + static QVector findByAccountId(int accountId); +}; \ No newline at end of file diff --git a/src/db/dao/mailitemdao.cpp b/src/db/dao/mailitemdao.cpp new file mode 100644 index 0000000..a526f08 --- /dev/null +++ b/src/db/dao/mailitemdao.cpp @@ -0,0 +1,208 @@ +#include "mailitemdao.h" +#include + +bool MailItemDao::insert(const MailItem& item) +{ + QSqlDatabase& db = DatabaseManager::instance().database(); + QSqlQuery query(db); + query.prepare( + "INSERT INTO MailCopy (folderId, messageId, subject, sender, recipient, date, read, flagged, hasAttachment, size, fileId) " + "VALUES (:folderId, :messageId, :subject, :sender, :recipient, :date, :read, :flagged, :hasAttachment, :size, :fileId)" + ); + query.bindValue(":folderId", item.folderId()); + query.bindValue(":messageId", item.messageId()); + query.bindValue(":subject", item.subject()); + query.bindValue(":sender", item.sender()); + query.bindValue(":recipient", item.recipient()); + query.bindValue(":date", item.date()); + query.bindValue(":read", item.isRead() ? 1 : 0); + query.bindValue(":flagged", item.isFlagged() ? 1 : 0); + query.bindValue(":hasAttachment", !item.attachments().isEmpty() ? 1 : 0); + query.bindValue(":size", item.size()); + query.bindValue(":fileId", item.fileId()); + + if (!query.exec()) { + qWarning() << "Failed to insert mail item:" << query.lastError().text(); + return false; + } + return true; +} + +bool MailItemDao::update(const MailItem& item) +{ + QSqlDatabase& db = DatabaseManager::instance().database(); + QSqlQuery query(db); + query.prepare( + "UPDATE MailCopy SET " + "folderId = :folderId, " + "messageId = :messageId, " + "subject = :subject, " + "sender = :sender, " + "recipient = :recipient, " + "date = :date, " + "read = :read, " + "flagged = :flagged, " + "hasAttachment = :hasAttachment, " + "size = :size, " + "fileId = :fileId " + "WHERE id = :id" + ); + query.bindValue(":id", item.id()); + query.bindValue(":folderId", item.folderId()); + query.bindValue(":messageId", item.messageId()); + query.bindValue(":subject", item.subject()); + query.bindValue(":sender", item.sender()); + query.bindValue(":recipient", item.recipient()); + query.bindValue(":date", item.date()); + query.bindValue(":read", item.isRead() ? 1 : 0); + query.bindValue(":flagged", item.isFlagged() ? 1 : 0); + query.bindValue(":hasAttachment", !item.attachments().isEmpty() ? 1 : 0); + query.bindValue(":size", item.size()); + query.bindValue(":fileId", item.fileId()); + + if (!query.exec()) { + qWarning() << "Failed to update mail item:" << query.lastError().text(); + return false; + } + return true; +} + +bool MailItemDao::remove(qint64 id) +{ + QSqlDatabase& db = DatabaseManager::instance().database(); + QSqlQuery query(db); + query.prepare("DELETE FROM MailCopy WHERE id = :id"); + query.bindValue(":id", id); + + if (!query.exec()) { + qWarning() << "Failed to delete mail item:" << query.lastError().text(); + return false; + } + return true; +} + +std::optional MailItemDao::findById(qint64 id) +{ + QSqlDatabase& db = DatabaseManager::instance().database(); + QSqlQuery query(db); + query.prepare("SELECT id, folderId, messageId, subject, sender, recipient, date, read, flagged, hasAttachment, size, fileId FROM MailCopy WHERE id = :id"); + query.bindValue(":id", id); + + if (!query.exec()) { + qWarning() << "Failed to find mail item by id:" << query.lastError().text(); + return std::nullopt; + } + + if (query.next()) { + MailItem item; + item.setId(query.value(0).toInt()); + item.setFolderId(query.value(1).toInt()); + item.setMessageId(query.value(2).toString()); + item.setSubject(query.value(3).toString()); + item.setSender(query.value(4).toString()); + item.setRecipient(query.value(5).toString()); + item.setDate(query.value(6).toDateTime()); + item.setRead(query.value(7).toBool()); + item.setFlagged(query.value(8).toBool()); + // attachments, size, fileId: we don't have them in the select? Actually we do. + // We need to fetch attachments? Not stored in DB as separate column? We have hasAttachment flag but not list. + // For simplicity, we leave attachments empty. + // We'll set size and fileId. + item.setSize(query.value(10).toLongLong()); + item.setFileId(query.value(11).toString()); + return item; + } + return std::nullopt; +} + +QVector MailItemDao::findAll() +{ + QVector items; + QSqlDatabase& db = DatabaseManager::instance().database(); + QSqlQuery query(db); + if (!query.exec("SELECT id, folderId, messageId, subject, sender, recipient, date, read, flagged, hasAttachment, size, fileId FROM MailCopy")) { + qWarning() << "Failed to fetch all mail items:" << query.lastError().text(); + return items; + } + + while (query.next()) { + MailItem item; + item.setId(query.value(0).toInt()); + item.setFolderId(query.value(1).toInt()); + item.setMessageId(query.value(2).toString()); + item.setSubject(query.value(3).toString()); + item.setSender(query.value(4).toString()); + item.setRecipient(query.value(5).toString()); + item.setDate(query.value(6).toDateTime()); + item.setRead(query.value(7).toBool()); + item.setFlagged(query.value(8).toBool()); + // attachments: we don't have the list, just a flag. We'll leave empty. + item.setSize(query.value(10).toLongLong()); + item.setFileId(query.value(11).toString()); + items.append(item); + } + return items; +} + +QVector MailItemDao::findByFolderId(int folderId) +{ + QVector items; + QSqlDatabase& db = DatabaseManager::instance().database(); + QSqlQuery query(db); + query.prepare("SELECT id, folderId, messageId, subject, sender, recipient, date, read, flagged, hasAttachment, size, fileId FROM MailCopy WHERE folderId = :folderId"); + query.bindValue(":folderId", folderId); + + if (!query.exec()) { + qWarning() << "Failed to fetch mail items by folder id:" << query.lastError().text(); + return items; + } + + while (query.next()) { + MailItem item; + item.setId(query.value(0).toInt()); + item.setFolderId(query.value(1).toInt()); + item.setMessageId(query.value(2).toString()); + item.setSubject(query.value(3).toString()); + item.setSender(query.value(4).toString()); + item.setRecipient(query.value(5).toString()); + item.setDate(query.value(6).toDateTime()); + item.setRead(query.value(7).toBool()); + item.setFlagged(query.value(8).toBool()); + item.setSize(query.value(10).toLongLong()); + item.setFileId(query.value(11).toString()); + items.append(item); + } + return items; +} + +QVector MailItemDao::findByFolderIdSinceUid(int folderId, qint64 sinceUid) +{ + QVector items; + QSqlDatabase& db = DatabaseManager::instance().database(); + QSqlQuery query(db); + query.prepare("SELECT id, folderId, messageId, subject, sender, recipient, date, read, flagged, hasAttachment, size, fileId FROM MailCopy WHERE folderId = :folderId AND id > :sinceUid"); + query.bindValue(":folderId", folderId); + query.bindValue(":sinceUid", sinceUid); + + if (!query.exec()) { + qWarning() << "Failed to fetch mail items by folder id since uid:" << query.lastError().text(); + return items; + } + + while (query.next()) { + MailItem item; + item.setId(query.value(0).toInt()); + item.setFolderId(query.value(1).toInt()); + item.setMessageId(query.value(2).toString()); + item.setSubject(query.value(3).toString()); + item.setSender(query.value(4).toString()); + item.setRecipient(query.value(5).toString()); + item.setDate(query.value(6).toDateTime()); + item.setRead(query.value(7).toBool()); + item.setFlagged(query.value(8).toBool()); + item.setSize(query.value(10).toLongLong()); + item.setFileId(query.value(11).toString()); + items.append(item); + } + return items; +} \ No newline at end of file diff --git a/src/db/dao/mailitemdao.h b/src/db/dao/mailitemdao.h new file mode 100644 index 0000000..9416e55 --- /dev/null +++ b/src/db/dao/mailitemdao.h @@ -0,0 +1,18 @@ +#pragma once + +#include "../databasemanager.h" +#include "../../core/mailitem.h" +#include +#include + +class MailItemDao +{ +public: + static bool insert(const MailItem& item); + static bool update(const MailItem& item); + static bool remove(qint64 id); + static std::optional findById(qint64 id); + static QVector findAll(); + static QVector findByFolderId(int folderId); + static QVector findByFolderIdSinceUid(int folderId, qint64 sinceUid); +}; \ No newline at end of file