diff --git a/CMakeLists.txt b/CMakeLists.txt
index a2d2258..164a06f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -17,10 +17,9 @@ include_directories(${PROJECT_SOURCE_DIR}/src)
# Resources
qt6_add_resources(WinoMailQtResources
- PREFIX
- /
FILES
resources/qml/main.qml
+ resources/qml/Shell.qml
)
# Source files
@@ -32,10 +31,18 @@ set(SRC_FILES
src/core/models/folder.cpp
src/services/synchronizer.cpp
src/services/imap/imapsynchronizer.cpp
+ src/services/outlook/outlooksynchronizer.cpp
+ src/services/gmail/gmailsynchronizer.cpp
+ src/services/request.cpp
+ src/services/concreterequests.cpp
+ src/services/requestprocessor.cpp
+ src/services/changetype.cpp
+ src/services/changprocessor.cpp
src/db/databasemanager.cpp
src/db/dao/accountdao.cpp
src/db/dao/folderdao.cpp
src/db/dao/mailitemdao.cpp
+ src/db/dbchangeprocessor.cpp
)
# Executable
diff --git a/resources.qrc b/resources.qrc
index 3d86720..1d39c46 100644
--- a/resources.qrc
+++ b/resources.qrc
@@ -1,5 +1,6 @@
resources/qml/main.qml
+ resources/qml/Shell.qml
\ No newline at end of file
diff --git a/resources/qml/MailListPage.qml b/resources/qml/MailListPage.qml
new file mode 100644
index 0000000..9f2d976
--- /dev/null
+++ b/resources/qml/MailListPage.qml
@@ -0,0 +1,136 @@
+import QtQuick 2.15
+import QtQuick.Controls 2.15
+import QtQuick.Layouts 1.15
+
+Item {
+ id: mailListPage
+ anchors.fill: parent
+
+ // Layout: SplitView for resizable panes
+ SplitView {
+ anchors.fill: parent
+
+ // Left pane: Folder list
+ Rectangle {
+ width: 200
+ color: "#fafafa"
+ ListView {
+ id: folderListView
+ anchors.fill: parent
+ clip: true
+ model: FolderModel {}
+ delegate: Item {
+ height: 40
+ Layout.fillWidth: true
+ Rectangle {
+ anchors.fill: parent
+ color: ListView.isCurrentItem ? "#e3f2fd" : "transparent"
+ Text {
+ text: folderName
+ anchors.left: parent.left
+ anchors.leftMargin: 16
+ anchors.verticalCenter: parent.verticalCenter
+ font.pointSize: 14
+ color: ListView.isCurrentItem ? "#1976d2" : "#666"
+ }
+ }
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ folderListView.currentIndex = index
+ // TODO: Load emails for selected folder
+ }
+ }
+ }
+ }
+ }
+
+ // Right pane: Email list and preview
+ Rectangle {
+ color: "#fafafa"
+ SplitView {
+ orientation: Qt.Vertical
+ anchors.fill: parent
+
+ // Email list (top)
+ Rectangle {
+ Layout.minimumHeight: 200
+ color: "#fafafa"
+ ListView {
+ id: emailListView
+ anchors.fill: parent
+ clip: true
+ model: EmailModel {}
+ delegate: Item {
+ height: 60
+ Layout.fillWidth: true
+ Rectangle {
+ anchors.fill: parent
+ color: ListView.isCurrentItem ? "#e3f2fd" : "transparent"
+ RowLayout {
+ anchors.fill: parent
+ anchors.margins: 8
+ // Sender avatar/initial
+ Rectangle {
+ width: 40
+ height: 40
+ color: "#cccccc"
+ Text {
+ text: senderInitial
+ anchors.centerIn: parent
+ font.pointSize: 16
+ color: "#666"
+ }
+ }
+ ColumnLayout {
+ Layout.fillWidth: true
+ spacing: 4
+ Text {
+ text: senderName
+ font.pointSize: 14
+ color: "#333"
+ elide: Text.ElideRight
+ Layout.fillWidth: true
+ }
+ Text {
+ text: subject
+ font.pointSize: 13
+ color: "#666"
+ elide: Text.ElideRight
+ Layout.fillWidth: true
+ }
+ Text {
+ text: time
+ font.pointSize: 12
+ color: "#999"
+ }
+ }
+ }
+ }
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ emailListView.currentIndex = index
+ // TODO: Show email preview in the bottom pane
+ }
+ }
+ }
+ }
+ }
+
+ // Email preview (bottom)
+ Rectangle {
+ Layout.minimumHeight: 200
+ color: "#ffffff"
+ Text {
+ id: emailPreview
+ text: "Select an email to preview"
+ anchors.centerIn: parent
+ font.pointSize: 14
+ color: "#666"
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/resources/qml/Shell.qml b/resources/qml/Shell.qml
new file mode 100644
index 0000000..24946d1
--- /dev/null
+++ b/resources/qml/Shell.qml
@@ -0,0 +1,174 @@
+import QtQuick 2.15
+import QtQuick.Window 2.15
+import QtQuick.Controls 2.15
+import QtQuick.Layouts 1.15
+
+ApplicationWindow {
+ id: shell
+ visible: true
+ width: 1024
+ height: 768
+ title: qsTr("Wino Mail Qt")
+
+ // Set the title from translator in Component.onCompleted
+ Component.onCompleted: {
+ title = translator.tr("appName")
+ }
+
+ // Drawer for navigation
+ Drawer {
+ id: drawer
+ width: 200
+ height: shell.height
+ Container {
+ anchors.fill: parent
+ Layout.alignment: Qt.AlignTop
+ ScrollView {
+ anchors.fill: parent
+ ColumnLayout {
+ spacing: 0
+ // App logo/name
+ Rectangle {
+ Layout.fillWidth: true
+ height: 60
+ color: "#1976D2"
+ Text {
+ text: qsTr("Wino Mail")
+ color: "white"
+ font.pointSize: 18
+ anchors.centerIn: parent
+ }
+ }
+ // Navigation items
+ NavigationItem {
+ text: qsTr("Mail")
+ icon: "mail"
+ isChecked: stackView.currentIndex === 0
+ onClicked: {
+ drawer.close()
+ stackView.currentIndex = 0
+ }
+ }
+ NavigationItem {
+ text: qsTr("Calendar")
+ icon: "calendar"
+ isChecked: stackView.currentIndex === 1
+ onClicked: {
+ drawer.close()
+ stackView.currentIndex = 1
+ }
+ }
+ NavigationItem {
+ text: qsTr("Contacts")
+ icon: "contacts"
+ isChecked: stackView.currentIndex === 2
+ onClicked: {
+ drawer.close()
+ stackView.currentIndex = 2
+ }
+ }
+ NavigationItem {
+ text: qsTr("Settings")
+ icon: "settings"
+ isChecked: stackView.currentIndex === 3
+ onClicked: {
+ drawer.close()
+ stackView.currentIndex = 3
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Main content area with StackView for pages
+ StackView {
+ id: stackView
+ anchors.fill: parent
+ initialItem: mailListPage
+ }
+
+ // Define pages
+ MailListPage {
+ id: mailListPage
+ }
+ // Placeholder for other pages
+ Rectangle {
+ color: "#f0f0f0"
+ Text {
+ text: qsTr("Calendar Page")
+ anchors.centerIn: parent
+ }
+ }
+ Rectangle {
+ color: "#f0f0f0"
+ Text {
+ text: qsTr("Contacts Page")
+ anchors.centerIn: parent
+ }
+ }
+ Rectangle {
+ color: "#f0f0f0"
+ Text {
+ text: qsTr("Settings Page")
+ anchors.centerIn: parent
+ }
+ }
+
+ // Hamburger button to open drawer
+ MenuButton {
+ icon: "menu"
+ anchors.left: parent.left
+ anchors.top: parent.top
+ anchors.margins: 10
+ onClicked: drawer.open()
+ }
+}
+
+// Helper components
+component NavigationItem: Button {
+ Layout.fillWidth: true
+ height: 48
+ padding: 0
+ contentItem: RowLayout {
+ spacing: 16
+ anchors.fill: parent
+ anchors.margins: 24
+ Icon {
+ source: icon
+ width: 24
+ height: 24
+ color: isChecked ? "#1976D2" : "#666"
+ }
+ Text {
+ text: text
+ font.pointSize: 14
+ verticalAlignment: Text.AlignVCenter
+ color: isChecked ? "#1976D2" : "#666"
+ }
+ }
+ background: Rectangle {
+ color: isChecked ? "#E3F2FD" : "transparent"
+ radius: 0
+ }
+}
+
+component MenuButton: IconButton {
+ iconSource: icon
+}
+
+// Placeholder for MailListPage - we'll replace this with a real component later
+component MailListPage: Item {
+ id: mailListPage
+ anchors.fill: parent
+ Rectangle {
+ anchors.fill: parent
+ color: "#fafafa"
+ Text {
+ text: qsTr("Mail List Placeholder")
+ anchors.centerIn: parent
+ font.pointSize: 16
+ color: "#666"
+ }
+ }
+}
\ No newline at end of file
diff --git a/resources/qml/main.qml b/resources/qml/main.qml
index 8f0654d..e765464 100644
--- a/resources/qml/main.qml
+++ b/resources/qml/main.qml
@@ -2,28 +2,6 @@ import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
-Window {
- width: 1024
- height: 768
- visible: true
- title: qsTr("Wino Mail Qt")
-
- // Use the translator to set the title
- // Note: we can't directly call translator.tr here because it's a C++ object.
- // We'll set the title in Component.onCompleted by accessing the context property.
- Component.onCompleted: {
- title = translator.tr("appName")
- }
-
- Rectangle {
- anchors.fill: parent
- color: "#f0f0f0"
-
- Text {
- text: qsTr("Welcome to Wino Mail Qt!")
- anchors.centerIn: parent
- font.pointSize: 24
- color: "#333"
- }
- }
+// We'll load the Shell component from Shell.qml
+Shell {
}
\ No newline at end of file
diff --git a/resources/qml/models/FolderModel.qml b/resources/qml/models/FolderModel.qml
new file mode 100644
index 0000000..06f3b0e
--- /dev/null
+++ b/resources/qml/models/FolderModel.qml
@@ -0,0 +1,24 @@
+import QtQuick 2.15
+
+ListModel {
+ ListElement {
+ folderName: "Inbox"
+ unreadCount: 5
+ }
+ ListElement {
+ folderName: "Sent"
+ unreadCount: 0
+ }
+ ListElement {
+ folderName: "Drafts"
+ unreadCount: 0
+ }
+ ListElement {
+ folderName: "Trash"
+ unreadCount: 0
+ }
+ ListElement {
+ folderName: "Spam"
+ unreadCount: 0
+ }
+}
\ No newline at end of file
diff --git a/src/core/events.h b/src/core/events.h
index f13cf5b..69a85ba 100644
--- a/src/core/events.h
+++ b/src/core/events.h
@@ -3,9 +3,9 @@
#include
#include
#include
-#include "../models/account.h"
-#include "../models/folder.h"
-#include "../core/mailitem.h"
+#include "models/account.h"
+#include "models/folder.h"
+#include "mailitem.h"
namespace WinoMail {
namespace Events {
@@ -38,7 +38,7 @@ struct FolderAddedEvent : public BaseEvent {
};
struct FolderRemovedEvent : public BaseEvent {
- String folderId;
+ QString folderId;
};
struct FolderUpdatedEvent : public BaseEvent {
@@ -63,12 +63,12 @@ struct AccountUpdatedEvent : public BaseEvent {
// Sync events
struct SyncStartedEvent : public BaseEvent {
int accountId;
- String folderId; // Optional, empty for full account sync
+ QString folderId; // Optional, empty for full account sync
};
struct SyncFinishedEvent : public BaseEvent {
int accountId;
- String folderId; // Optional, empty for full account sync
+ QString folderId; // Optional, empty for full account sync
bool success;
QString errorMessage; // If success is false
};
diff --git a/src/core/translator.h b/src/core/translator.h
index 508a254..b120cc9 100644
--- a/src/core/translator.h
+++ b/src/core/translator.h
@@ -3,9 +3,11 @@
#include
#include
#include
+#include
-class Translator
+class Translator : public QObject
{
+ Q_OBJECT
public:
static Translator& instance();
~Translator() = default;
diff --git a/src/db/dao/accountdao.cpp b/src/db/dao/accountdao.cpp
index 7142f8a..6c15edb 100644
--- a/src/db/dao/accountdao.cpp
+++ b/src/db/dao/accountdao.cpp
@@ -1,5 +1,4 @@
#include "accountdao.h"
-#include
bool AccountDao::insert(const Account& account)
{
@@ -69,7 +68,7 @@ bool AccountDao::remove(int id)
return true;
}
-std::optional AccountDao::findById(int id)
+Account* AccountDao::findById(int id)
{
QSqlDatabase& db = DatabaseManager::instance().database();
QSqlQuery query(db);
@@ -78,21 +77,22 @@ std::optional AccountDao::findById(int id)
if (!query.exec()) {
qWarning() << "Failed to find account by id:" << query.lastError().text();
- return std::nullopt;
+ return nullptr;
}
if (query.next()) {
- Account acc;
- acc.setId(query.value(0).toInt());
- acc.setEmail(query.value(1).toString());
- acc.setDisplayName(query.value(2).toString());
- acc.setType(static_cast(query.value(3).toInt()));
- acc.setAccessToken(query.value(4).toString());
- acc.setRefreshToken(query.value(5).toString());
- acc.setTokenExpires(query.value(6).toDateTime());
- return acc;
+ Account* account = new Account();
+ account->setId(query.value("id").toLongLong());
+ account->setEmail(query.value("email").toString());
+ account->setDisplayName(query.value("displayName").toString());
+ account->setType(static_cast(query.value("type").toInt()));
+ account->setAccessToken(query.value("accessToken").toString());
+ account->setRefreshToken(query.value("refreshToken").toString());
+ account->setTokenExpires(query.value("tokenExpires").toDateTime());
+ return account;
}
- return std::nullopt;
+
+ return nullptr;
}
QVector AccountDao::findAll()
@@ -119,7 +119,7 @@ QVector AccountDao::findAll()
return accounts;
}
-std::optional AccountDao::findByEmail(const QString& email)
+Account* AccountDao::findByEmail(const QString& email)
{
QSqlDatabase& db = DatabaseManager::instance().database();
QSqlQuery query(db);
@@ -128,19 +128,19 @@ std::optional AccountDao::findByEmail(const QString& email)
if (!query.exec()) {
qWarning() << "Failed to find account by email:" << query.lastError().text();
- return std::nullopt;
+ return nullptr;
}
if (query.next()) {
- Account acc;
- acc.setId(query.value(0).toInt());
- acc.setEmail(query.value(1).toString());
- acc.setDisplayName(query.value(2).toString());
- acc.setType(static_cast(query.value(3).toInt()));
- acc.setAccessToken(query.value(4).toString());
- acc.setRefreshToken(query.value(5).toString());
- acc.setTokenExpires(query.value(6).toDateTime());
+ Account* acc = new Account();
+ acc->setId(query.value(0).toLongLong());
+ acc->setEmail(query.value(1).toString());
+ acc->setDisplayName(query.value(2).toString());
+ acc->setType(static_cast(query.value(3).toInt()));
+ acc->setAccessToken(query.value(4).toString());
+ acc->setRefreshToken(query.value(5).toString());
+ acc->setTokenExpires(query.value(6).toDateTime());
return acc;
}
- return std::nullopt;
-}
\ No newline at end of file
+ return nullptr;
+}
diff --git a/src/db/dao/folderdao.cpp b/src/db/dao/folderdao.cpp
index 733f267..37108cb 100644
--- a/src/db/dao/folderdao.cpp
+++ b/src/db/dao/folderdao.cpp
@@ -1,4 +1,7 @@
+#include
#include "folderdao.h"
+#include
+#include
#include
bool FolderDao::insert(const Folder& folder)
diff --git a/src/db/dao/mailitemdao.cpp b/src/db/dao/mailitemdao.cpp
index a526f08..9760790 100644
--- a/src/db/dao/mailitemdao.cpp
+++ b/src/db/dao/mailitemdao.cpp
@@ -1,3 +1,5 @@
+#include
+#include
#include "mailitemdao.h"
#include
diff --git a/src/db/dbchangeprocessor.cpp b/src/db/dbchangeprocessor.cpp
new file mode 100644
index 0000000..8058e73
--- /dev/null
+++ b/src/db/dbchangeprocessor.cpp
@@ -0,0 +1,214 @@
+#include "dbchangeprocessor.h"
+#include
+#include
+
+DbChangeProcessor::DbChangeProcessor(QObject* parent)
+ : QObject(parent),
+ m_db(DatabaseManager::instance()),
+ m_mailItemDao(MailItemDao::instance()),
+ m_folderDao(FolderDao::instance()),
+ m_accountDao(AccountDao::instance()),
+ m_batchTimer(new QTimer(this))
+{
+ // Configurar el timer para procesar batch cada 5 segundos
+ m_batchTimer->setInterval(5000); // 5 segundos
+ connect(m_batchTimer, &QTimer::timeout, this, &DbChangeProcessor::processBatch);
+ m_batchTimer->start();
+
+ // Suscribirse a todos los eventos relevantes
+ SUBSCRIBE(WinoMail::Events::MailItemAddedEvent,
+ [this](const WinoMail::Events::MailItemAddedEvent& event) {
+ handleMailItemAdded(event);
+ });
+
+ SUBSCRIBE(WinoMail::Events::MailItemRemovedEvent,
+ [this](const WinoMail::Events::MailItemRemovedEvent& event) {
+ handleMailItemRemoved(event);
+ });
+
+ SUBSCRIBE(WinoMail::Events::MailItemUpdatedEvent,
+ [this](const WinoMail::Events::MailItemUpdatedEvent& event) {
+ handleMailItemUpdated(event);
+ });
+
+ SUBSCRIBE(WinoMail::Events::FolderAddedEvent,
+ [this](const WinoMail::Events::FolderAddedEvent& event) {
+ handleFolderAdded(event);
+ });
+
+ SUBSCRIBE(WinoMail::Events::FolderRemovedEvent,
+ [this](const WinoMail::Events::FolderRemovedEvent& event) {
+ handleFolderRemoved(event);
+ });
+
+ SUBSCRIBE(WinoMail::Events::FolderUpdatedEvent,
+ [this](const WinoMail::Events::FolderUpdatedEvent& event) {
+ handleFolderUpdated(event);
+ });
+
+ SUBSCRIBE(WinoMail::Events::AccountAddedEvent,
+ [this](const WinoMail::Events::AccountAddedEvent& event) {
+ handleAccountAdded(event);
+ });
+
+ SUBSCRIBE(WinoMail::Events::AccountRemovedEvent,
+ [this](const WinoMail::Events::AccountRemovedEvent& event) {
+ handleAccountRemoved(event);
+ });
+
+ SUBSCRIBE(WinoMail::Events::AccountUpdatedEvent,
+ [this](const WinoMail::Events::AccountUpdatedEvent& event) {
+ handleAccountUpdated(event);
+ });
+}
+
+void DbChangeProcessor::handleMailItemAdded(const WinoMail::Events::MailItemAddedEvent& event)
+{
+ qDebug() << "DbChangeProcessor: Queuing MailItemAddedEvent for UID:" << event.item.uid;
+ m_mailItemAddedQueue.append(event);
+}
+
+void DbChangeProcessor::handleMailItemRemoved(const WinoMail::Events::MailItemRemovedEvent& event)
+{
+ qDebug() << "DbChangeProcessor: Queuing MailItemRemovedEvent for UID:" << event.itemUid;
+ m_mailItemRemovedQueue.append(event);
+}
+
+void DbChangeProcessor::handleMailItemUpdated(const WinoMail::Events::MailItemUpdatedEvent& event)
+{
+ qDebug() << "DbChangeProcessor: Queuing MailItemUpdatedEvent for UID:" << event.item.uid;
+ m_mailItemUpdatedQueue.append(event);
+}
+
+void DbChangeProcessor::handleFolderAdded(const WinoMail::Events::FolderAddedEvent& event)
+{
+ qDebug() << "DbChangeProcessor: Queuing FolderAddedEvent for folder:" << event.folder.name;
+ m_folderAddedQueue.append(event);
+}
+
+void DbChangeProcessor::handleFolderRemoved(const WinoMail::Events::FolderRemovedEvent& event)
+{
+ qDebug() << "DbChangeProcessor: Queuing FolderRemovedEvent for folder ID:" << event.folderId;
+ m_folderRemovedQueue.append(event);
+}
+
+void DbChangeProcessor::handleFolderUpdated(const WinoMail::Events::FolderUpdatedEvent& event)
+{
+ qDebug() << "DbChangeProcessor: Queuing FolderUpdatedEvent for folder ID:" << event.folder.id();
+ m_folderUpdatedQueue.append(event);
+}
+
+void DbChangeProcessor::handleAccountAdded(const WinoMail::Events::AccountAddedEvent& event)
+{
+ qDebug() << "DbChangeProcessor: Queuing AccountAddedEvent for account:" << event.account.email();
+ m_accountAddedQueue.append(event);
+}
+
+void DbChangeProcessor::handleAccountRemoved(const WinoMail::Events::AccountRemovedEvent& event)
+{
+ qDebug() << "DbChangeProcessor: Queuing AccountRemovedEvent for account ID:" << event.accountId;
+ m_accountRemovedQueue.append(event);
+}
+
+void DbChangeProcessor::handleAccountUpdated(const WinoMail::Events::AccountUpdatedEvent& event)
+{
+ qDebug() << "DbChangeProcessor: Queuing AccountUpdatedEvent for account ID:" << event.account.id();
+ m_accountUpdatedQueue.append(event);
+}
+
+void DbChangeProcessor::processBatch()
+{
+ qDebug() << "DbChangeProcessor: Starting batch processing at" << QDateTime::currentDateTime().toString();
+
+ // Procesar eventos de MailItem añadidos
+ if (!m_mailItemAddedQueue.isEmpty()) {
+ qDebug() << "DbChangeProcessor: Processing" << m_mailItemAddedQueue.size() << "MailItemAdded events";
+ for (const auto& event : m_mailItemAddedQueue) {
+ m_mailItemDao.insert(event.item);
+ }
+ m_mailItemAddedQueue.clear();
+ }
+
+ // Procesar eventos de MailItem eliminados
+ if (!m_mailItemRemovedQueue.isEmpty()) {
+ qDebug() << "DbChangeProcessor: Processing" << m_mailItemRemovedQueue.size() << "MailItemRemoved events";
+ for (const auto& event : m_mailItemRemovedQueue) {
+ m_mailItemDao.removeByUid(event.itemUid, event.folderId);
+ }
+ m_mailItemRemovedQueue.clear();
+ }
+
+ // Procesar eventos de MailItem actualizados
+ if (!m_mailItemUpdatedQueue.isEmpty()) {
+ qDebug() << "DbChangeProcessor: Processing" << m_mailItemUpdatedQueue.size() << "MailItemUpdated events";
+ for (const auto& event : m_mailItemUpdatedQueue) {
+ m_mailItemDao.update(event.item, event.changedFields);
+ }
+ m_mailItemUpdatedQueue.clear();
+ }
+
+ // Procesar eventos de carpetas añadidas
+ if (!m_folderAddedQueue.isEmpty()) {
+ qDebug() << "DbChangeProcessor: Processing" << m_folderAddedQueue.size() << "FolderAdded events";
+ for (const auto& event : m_folderAddedQueue) {
+ m_folderDao.insert(event.folder);
+ }
+ m_folderAddedQueue.clear();
+ }
+
+ // Procesar eventos de carpetas eliminadas
+ if (!m_folderRemovedQueue.isEmpty()) {
+ qDebug() << "DbChangeProcessor: Processing" << m_folderRemovedQueue.size() << "FolderRemoved events";
+ for (const auto& event : m_folderRemovedQueue) {
+ m_folderDao.remove(event.folderId.toInt());
+ }
+ m_folderRemovedQueue.clear();
+ }
+
+ // Procesar eventos de carpetas actualizadas
+ if (!m_folderUpdatedQueue.isEmpty()) {
+ qDebug() << "DbChangeProcessor: Processing" << m_folderUpdatedQueue.size() << "FolderUpdated events";
+ for (const auto& event : m_folderUpdatedQueue) {
+ m_folderDao.update(event.folder, event.changedFields);
+ }
+ m_folderUpdatedQueue.clear();
+ }
+
+ // Procesar eventos de cuentas añadidas
+ if (!m_accountAddedQueue.isEmpty()) {
+ qDebug() << "DbChangeProcessor: Processing" << m_accountAddedQueue.size() << "AccountAdded events";
+ for (const auto& event : m_accountAddedQueue) {
+ m_accountDao.insert(event.account);
+ }
+ m_accountAddedQueue.clear();
+ }
+
+ // Procesar eventos de cuentas eliminadas
+ if (!m_accountRemovedQueue.isEmpty()) {
+ qDebug() << "DbChangeProcessor: Processing" << m_accountRemovedQueue.size() << "AccountRemoved events";
+ for (const auto& event : m_accountRemovedQueue) {
+ m_accountDao.remove(event.accountId);
+ }
+ m_accountRemovedQueue.clear();
+ }
+
+ // Procesar eventos de cuentas actualizadas
+ if (!m_accountUpdatedQueue.isEmpty()) {
+ qDebug() << "DbChangeProcessor: Processing" << m_accountUpdatedQueue.size() << "AccountUpdated events";
+ for (const auto& event : m_accountUpdatedQueue) {
+ m_accountDao.update(event.account, event.changedFields);
+ }
+ m_accountUpdatedQueue.clear();
+ }
+
+ qDebug() << "DbChangeProcessor: Batch processing completed at" << QDateTime::currentDateTime().toString();
+}
+
+DbChangeProcessor::~DbChangeProcessor()
+{
+ if (m_batchTimer->isActive()) {
+ m_batchTimer->stop();
+ }
+ // Procesar cualquier evento restante antes de destruir
+ processBatch();
+}
\ No newline at end of file
diff --git a/src/db/dbchangeprocessor.h b/src/db/dbchangeprocessor.h
new file mode 100644
index 0000000..41620f3
--- /dev/null
+++ b/src/db/dbchangeprocessor.h
@@ -0,0 +1,51 @@
+#ifndef DBCHANGEPROCESSOR_H
+#define DBCHANGEPROCESSOR_H
+
+#include
+#include
+#include
+#include "core/eventbus.h"
+#include "../core/events.h"
+#include "../db/databasemanager.h"
+#include "../db/dao/accountdao.h"
+#include "../db/dao/folderdao.h"
+#include "../db/dao/mailitemdao.h"
+
+class DbChangeProcessor : public QObject
+{
+ Q_OBJECT
+public:
+ explicit DbChangeProcessor(QObject* parent = nullptr);
+ ~DbChangeProcessor() override = default;
+
+private slots:
+ void handleMailItemAdded(const WinoMail::Events::MailItemAddedEvent& event);
+ void handleMailItemRemoved(const WinoMail::Events::MailItemRemovedEvent& event);
+ void handleMailItemUpdated(const WinoMail::Events::MailItemUpdatedEvent& event);
+ void handleFolderAdded(const WinoMail::Events::FolderAddedEvent& event);
+ void handleFolderRemoved(const WinoMail::Events::FolderRemovedEvent& event);
+ void handleFolderUpdated(const WinoMail::Events::FolderUpdatedEvent& event);
+ void handleAccountAdded(const WinoMail::Events::AccountAddedEvent& event);
+ void handleAccountRemoved(const WinoMail::Events::AccountRemovedEvent& event);
+ void handleAccountUpdated(const WinoMail::Events::AccountUpdatedEvent& event);
+ void processBatch();
+
+private:
+ DatabaseManager& m_db;
+ MailItemDao& m_mailItemDao;
+ FolderDao& m_folderDao;
+ AccountDao& m_accountDao;
+
+ QTimer* m_batchTimer;
+ QVector m_mailItemAddedQueue;
+ QVector m_mailItemRemovedQueue;
+ QVector m_mailItemUpdatedQueue;
+ QVector m_folderAddedQueue;
+ QVector m_folderRemovedQueue;
+ QVector m_folderUpdatedQueue;
+ QVector m_accountAddedQueue;
+ QVector m_accountRemovedQueue;
+ QVector m_accountUpdatedQueue;
+};
+
+#endif // DBCHANGEPROCESSOR_H
\ No newline at end of file
diff --git a/src/main.cpp b/src/main.cpp
index 4f665a1..3c5e8ff 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -2,6 +2,7 @@
#include
#include
#include "core/translator.h"
+#include "db/dbchangeprocessor.h"
int main(int argc, char *argv[])
{
@@ -13,8 +14,11 @@ int main(int argc, char *argv[])
qWarning() << "Failed to load translation";
}
+ // Initialize the DbChangeProcessor to start processing events in batches
+ DbChangeProcessor dbChangeProcessor(&app);
+
QQmlApplicationEngine engine;
- engine.rootContext()->setContextProperty("translator", &translator);
+ engine.rootContext()->setContextProperty("translator", static_cast(&translator));
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
diff --git a/src/services/gmail/gmailsynchronizer.cpp b/src/services/gmail/gmailsynchronizer.cpp
index 066243d..78d8a00 100644
--- a/src/services/gmail/gmailsynchronizer.cpp
+++ b/src/services/gmail/gmailsynchronizer.cpp
@@ -7,6 +7,7 @@
#include
#include
#include
+#include
#include
#include
@@ -25,11 +26,6 @@ GmailSynchronizer::GmailSynchronizer(QObject* parent)
m_historyTimer->setInterval(2 * 60 * 1000); // 2 minutos
}
-GmailSynchronizer::~GmailSynchronizer()
-{
- m_historyTimer->stop();
-}
-
bool GmailSynchronizer::initialize(const Account& account)
{
// Guardar información de la cuenta
@@ -60,10 +56,10 @@ bool GmailSynchronizer::syncFolder(const Folder& folder)
}
qDebug() << "Starting Gmail sync for folder (label):" << folder.name();
- emit folderSyncStarted(folder.id());
+ emit folderSyncStarted(QString::number(folder.id()));
// Obtener elementos de correo desde Gmail API
- QVector items = fetchMailItems(folder.id());
+ QVector items = fetchMailItems(QString::number(folder.id()));
// En una implementación real, aquí compararíamos con la base de datos local
// y emitiríamos las señales apropiadas para elementos nuevos/actualizados/eliminados
@@ -75,7 +71,7 @@ bool GmailSynchronizer::syncFolder(const Folder& folder)
}
}
- emit folderSyncFinished(folder.id(), true);
+ emit folderSyncFinished(QString::number(folder.id()), true);
return true;
}
@@ -113,7 +109,7 @@ QVector GmailSynchronizer::fetchMailItems(const QString& folderId,
// Construir la URL para Gmail API
// Nota: En Gmail, las carpetas se identifican por su nombre de label (ej: INBOX, DRAFTS, etc.)
- QString endpoint = String.format("/me/mailFolders/%1/messages", folderId);
+ QString endpoint = QString("/me/mailFolders/%1/messages").arg(folderId);
QString url = buildGmailUrl(endpoint);
// Parámetros de consulta
@@ -257,7 +253,7 @@ void GmailSynchronizer::onHistoryTimer()
// de todas las etiquetas (esto sería ineficiente en producción)
QVector folders = getFolders();
for (const Folder& folder : folders) {
- syncFolder(folder.id());
+ syncFolder(Folder(folder.id(), 0, QString()));
}
}
@@ -270,7 +266,7 @@ QString GmailSynchronizer::buildGmailUrl(const QString& endpoint) const
QNetworkRequest GmailSynchronizer::createAuthRequest(const QString& url) const
{
- QNetworkRequest request(QUrl(url));
+ QNetworkRequest request{QUrl(url)};
request.setRawHeader("Authorization",
QString("Bearer %1").arg(m_accessToken).toUtf8());
request.setHeader(QNetworkRequest::ContentTypeHeader,
diff --git a/src/services/imap/imapsynchronizer.cpp b/src/services/imap/imapsynchronizer.cpp
index ee81603..51c3d54 100644
--- a/src/services/imap/imapsynchronizer.cpp
+++ b/src/services/imap/imapsynchronizer.cpp
@@ -1,5 +1,6 @@
#include "imapsynchronizer.h"
#include
+#include
ImapSynchronizer::ImapSynchronizer(QObject* parent)
: Synchronizer(parent)
@@ -11,6 +12,15 @@ bool ImapSynchronizer::initialize(const Account& account)
Q_UNUSED(account);
qDebug() << "IMAP Synchronizer initialize (stub)";
m_connected = true; // pretend success
+
+ // Publish AccountConnectedEvent
+ WinoMail::Events::AccountConnectedEvent event;
+ event.eventId = generateEventId();
+ event.timestamp = QDateTime::currentDateTimeUtc();
+ event.accountId = account.id();
+ event.provider = "imap";
+ PUBLISH(event);
+
return true;
}
@@ -18,9 +28,36 @@ bool ImapSynchronizer::syncFolder(const Folder& folder)
{
Q_UNUSED(folder);
qDebug() << "IMAP Synchronizer syncFolder (stub)";
- emit folderSyncStarted(folder.name());
+
+ // Publish SyncStartedEvent
+ WinoMail::Events::SyncStartedEvent event;
+ event.eventId = generateEventId();
+ event.timestamp = QDateTime::currentDateTimeUtc();
+ event.accountId = m_account.id();
+ event.folderId = QString::number(folder.id()); // Assuming folder.id() returns int
+ PUBLISH(event);
+
// In reality, we would connect to IMAP, list messages, etc.
- emit folderSyncFinished(folder.name(), true);
+ // For now, simulate some mail items being added
+ QVector items = fetchMailItems(QString::number(folder.id()));
+ for (const MailItem& item : items) {
+ WinoMail::Events::MailItemAddedEvent mailEvent;
+ mailEvent.eventId = generateEventId();
+ mailEvent.timestamp = QDateTime::currentDateTimeUtc();
+ mailEvent.item = item;
+ PUBLISH(mailEvent);
+ }
+
+ // Publish SyncFinishedEvent
+ WinoMail::Events::SyncFinishedEvent finishEvent;
+ finishEvent.eventId = generateEventId();
+ finishEvent.timestamp = QDateTime::currentDateTimeUtc();
+ finishEvent.accountId = m_account.id();
+ finishEvent.folderId = QString::number(folder.id());
+ finishEvent.success = true;
+ finishEvent.errorMessage = "";
+ PUBLISH(finishEvent);
+
return true;
}
@@ -41,7 +78,7 @@ QVector ImapSynchronizer::fetchMailItems(const QString& folderId,
qDebug() << "IMAP Synchronizer fetchMailItems (stub)";
// Return a dummy mail item for testing
QVector items;
- items.append(MailItem(1, "Test Subject", "sender@example.com", "me@example.com",
+ items.append(MailItem(1, 1, "Test Subject", "sender@example.com", "me@example.com",
QDateTime::currentDateTime(), false, false));
return items;
}
@@ -51,6 +88,13 @@ bool ImapSynchronizer::appendMailItem(const QString& folderId, const MailItem& i
Q_UNUSED(folderId);
Q_UNUSED(item);
qDebug() << "IMAP Synchronizer appendMailItem (stub)";
+ // In a real implementation, this would send the mail via IMAP APPEND
+ // For now, we'll simulate success and publish an event
+ WinoMail::Events::MailItemAddedEvent event;
+ event.eventId = generateEventId();
+ event.timestamp = QDateTime::currentDateTimeUtc();
+ event.item = item;
+ PUBLISH(event);
return true;
}
@@ -63,6 +107,18 @@ bool ImapSynchronizer::updateMailItemFlags(const QString& folderId,
Q_UNUSED(read);
Q_UNUSED(flagged);
qDebug() << "IMAP Synchronizer updateMailItemFlags (stub)";
+ // In a real implementation, this would update flags via IMAP STORE
+ // For now, we'll simulate success and publish an update event
+ // We need to fetch the item first to know what changed
+ MailItem item; // This would be fetched from storage
+ item.setId(itemUid.toLongLong()); // Assuming there's a setter
+
+ WinoMail::Events::MailItemUpdatedEvent event;
+ event.eventId = generateEventId();
+ event.timestamp = QDateTime::currentDateTimeUtc();
+ event.item = item;
+ event.changedFields = QStringList() << "read" << "flagged"; // Simplified
+ PUBLISH(event);
return true;
}
@@ -72,5 +128,21 @@ bool ImapSynchronizer::deleteMailItem(const QString& folderId,
Q_UNUSED(folderId);
Q_UNUSED(itemUid);
qDebug() << "IMAP Synchronizer deleteMailItem (stub)";
+ // In a real implementation, this would delete the mail via IMAP STORE +FLAGS or EXPUNGE
+ // For now, we'll simulate success and publish a removal event
+ WinoMail::Events::MailItemRemovedEvent event;
+ event.eventId = generateEventId();
+ event.timestamp = QDateTime::currentDateTimeUtc();
+ event.itemUid = itemUid;
+ event.folderId = folderId.toInt(); // Assuming folderId is numeric
+ PUBLISH(event);
return true;
+}
+
+// Helper para generar IDs únicos de eventos
+QString ImapSynchronizer::generateEventId() const
+{
+ // Simple implementation using timestamp and random component
+ return QString::number(QDateTime::currentMSecsSinceEpoch()) + "_" +
+ QString::number(std::rand());
}
\ No newline at end of file
diff --git a/src/services/imap/imapsynchronizer.h b/src/services/imap/imapsynchronizer.h
index 3325f53..79c23e9 100644
--- a/src/services/imap/imapsynchronizer.h
+++ b/src/services/imap/imapsynchronizer.h
@@ -2,6 +2,10 @@
#include "../synchronizer.h"
#include
+#include "../../core/eventbus.h"
+#include "../../core/models/account.h"
+#include "../../core/models/folder.h"
+#include "../../core/mailitem.h"
class ImapSynchronizer : public Synchronizer
{
@@ -31,4 +35,7 @@ private:
QString m_username;
QString m_password;
bool m_useSsl{true};
+
+ // Helper para generar IDs únicos de eventos
+ QString generateEventId() const;
};
\ No newline at end of file
diff --git a/src/services/outlook/outlooksynchronizer.cpp b/src/services/outlook/outlooksynchronizer.cpp
index 5f8e05e..b684165 100644
--- a/src/services/outlook/outlooksynchronizer.cpp
+++ b/src/services/outlook/outlooksynchronizer.cpp
@@ -7,6 +7,7 @@
#include
#include
#include
+#include
#include
#include
@@ -25,11 +26,6 @@ OutlookSynchronizer::OutlookSynchronizer(QObject* parent)
m_deltaTimer->setInterval(5 * 60 * 1000); // 5 minutos
}
-OutlookSynchronizer::~OutlookSynchronizer()
-{
- m_deltaTimer->stop();
-}
-
bool OutlookSynchronizer::initialize(const Account& account)
{
// Guardar información de la cuenta
@@ -60,10 +56,10 @@ bool OutlookSynchronizer::syncFolder(const Folder& folder)
}
qDebug() << "Starting Outlook sync for folder:" << folder.name();
- emit folderSyncStarted(folder.id());
+ emit folderSyncStarted(QString::number(folder.id()));
// Obtener elementos de correo desde Microsoft Graph
- QVector items = fetchMailItems(folder.id());
+ QVector items = fetchMailItems(QString::number(folder.id()));
// En una implementación real, aquí compararíamos con la base de datos local
// y emitiríamos las señales apropiadas para elementos nuevos/actualizados/eliminados
@@ -75,7 +71,7 @@ bool OutlookSynchronizer::syncFolder(const Folder& folder)
}
}
- emit folderSyncFinished(folder.id(), true);
+ emit folderSyncFinished(QString::number(folder.id()), true);
return true;
}
@@ -264,7 +260,7 @@ QString OutlookSynchronizer::buildGraphUrl(const QString& endpoint) const
QNetworkRequest OutlookSynchronizer::createAuthRequest(const QString& url) const
{
- QNetworkRequest request(QUrl(url));
+ QNetworkRequest request{QUrl(url)};
request.setRawHeader("Authorization",
QString("Bearer %1").arg(m_accessToken).toUtf8());
request.setHeader(QNetworkRequest::ContentTypeHeader,
diff --git a/src/ui/models/FolderListModel.cpp b/src/ui/models/FolderListModel.cpp
new file mode 100644
index 0000000..a71d57c
--- /dev/null
+++ b/src/ui/models/FolderListModel.cpp
@@ -0,0 +1,46 @@
+#include "FolderListModel.h"
+#include
+
+FolderListModel::FolderListModel(QObject *parent)
+ : QAbstractListModel(parent),
+ m_folderDao(FolderDao::instance())
+{
+ refresh();
+}
+
+int FolderListModel::rowCount(const QModelIndex &parent) const
+{
+ if (parent.isValid())
+ return 0;
+ return m_folderNames.size();
+}
+
+QVariant FolderListModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid() || index.row() >= m_folderNames.size())
+ return QVariant();
+
+ if (role == FolderNameRole)
+ return m_folderNames.at(index.row());
+ else if (role == UnreadCountRole)
+ return m_unreadCounts.at(index.row());
+ return QVariant();
+}
+
+QHash FolderListModel::roleNames() const
+{
+ QHash roles;
+ roles[FolderNameRole] = "folderName";
+ roles[UnreadCountRole] = "unreadCount";
+ return roles;
+}
+
+void FolderListModel::refresh()
+{
+ beginResetModel();
+ // For now, we'll just use hardcoded folders until we implement the DAO properly
+ m_folderNames = {"Inbox", "Sent", "Drafts", "Trash", "Spam"};
+ m_unreadCounts = {5, 0, 0, 0, 0};
+ endResetModel();
+ qDebug() << "FolderListModel refreshed with" << m_folderNames.size() << "folders";
+}
\ No newline at end of file
diff --git a/src/ui/models/FolderListModel.h b/src/ui/models/FolderListModel.h
new file mode 100644
index 0000000..22a713b
--- /dev/null
+++ b/src/ui/models/FolderListModel.h
@@ -0,0 +1,34 @@
+#ifndef FOLDERLISTMODEL_H
+#define FOLDERLISTMODEL_H
+
+#include
+#include
+#include
+#include "../db/dao/folderdao.h"
+
+class FolderListModel : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ explicit FolderListModel(QObject *parent = nullptr);
+ ~FolderListModel() override = default;
+
+ enum FolderRoles {
+ FolderNameRole = Qt::UserRole + 1,
+ UnreadCountRole
+ };
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ QHash roleNames() const override;
+
+ // Optional: method to refresh the model from the database
+ void refresh();
+
+private:
+ QVector m_folderNames;
+ QVector m_unreadCounts;
+ FolderDao& m_folderDao;
+};
+
+#endif // FOLDERLISTMODEL_H
\ No newline at end of file