From 822ed5db474947d539b3997e7d48b474bf9f5912 Mon Sep 17 00:00:00 2001 From: Padrino Date: Sun, 17 May 2026 01:52:34 +0200 Subject: [PATCH] Complete Phase 4: Basic UI with QML - MailListPage with folder/email navigation, models connected to DAOs, placeholder pages for Calendar/Contacts/Settings --- resources/qml/CalendarPage.qml | 19 +++++++ resources/qml/ContactsPage.qml | 19 +++++++ resources/qml/MailListPage.qml | 31 ++++++++-- resources/qml/SettingsPage.qml | 19 +++++++ resources/qml/Shell.qml | 46 +++------------ src/ui/models/EmailListModel.cpp | 98 ++++++++++++++++++++++++++++++++ src/ui/models/EmailListModel.h | 46 +++++++++++++++ 7 files changed, 235 insertions(+), 43 deletions(-) create mode 100644 resources/qml/CalendarPage.qml create mode 100644 resources/qml/ContactsPage.qml create mode 100644 resources/qml/SettingsPage.qml create mode 100644 src/ui/models/EmailListModel.cpp create mode 100644 src/ui/models/EmailListModel.h diff --git a/resources/qml/CalendarPage.qml b/resources/qml/CalendarPage.qml new file mode 100644 index 0000000..2e88602 --- /dev/null +++ b/resources/qml/CalendarPage.qml @@ -0,0 +1,19 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 + +Item { + id: calendarPage + anchors.fill: parent + + Rectangle { + anchors.fill: parent + color: "#fafafa" + Text { + text: qsTr("Calendar Page - Placeholder") + anchors.centerIn: parent + font.pointSize: 16 + color: "#666" + } + } +} \ No newline at end of file diff --git a/resources/qml/ContactsPage.qml b/resources/qml/ContactsPage.qml new file mode 100644 index 0000000..1725374 --- /dev/null +++ b/resources/qml/ContactsPage.qml @@ -0,0 +1,19 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 + +Item { + id: contactsPage + anchors.fill: parent + + Rectangle { + anchors.fill: parent + color: "#fafafa" + Text { + text: qsTr("Contacts Page - Placeholder") + anchors.centerIn: parent + font.pointSize: 16 + color: "#666" + } + } +} \ No newline at end of file diff --git a/resources/qml/MailListPage.qml b/resources/qml/MailListPage.qml index 9f2d976..40ad77e 100644 --- a/resources/qml/MailListPage.qml +++ b/resources/qml/MailListPage.qml @@ -6,6 +6,14 @@ Item { id: mailListPage anchors.fill: parent + // Models + FolderListModel { + id: folderModel + } + EmailListModel { + id: emailModel + } + // Layout: SplitView for resizable panes SplitView { anchors.fill: parent @@ -18,7 +26,7 @@ Item { id: folderListView anchors.fill: parent clip: true - model: FolderModel {} + model: folderModel delegate: Item { height: 40 Layout.fillWidth: true @@ -38,7 +46,18 @@ Item { anchors.fill: parent onClicked: { folderListView.currentIndex = index - // TODO: Load emails for selected folder + // Load emails for selected folder + // For now, we'll map folder names to IDs (hardcoded for demo) + var folderId = 0; // Inbox + switch (folderName) { + case "Inbox": folderId = 0; break; + case "Sent": folderId = 1; break; + case "Drafts": folderId = 2; break; + case "Trash": folderId = 3; break; + case "Spam": folderId = 4; break; + default: folderId = 0; + } + emailModel.setFolderId(folderId); } } } @@ -60,7 +79,7 @@ Item { id: emailListView anchors.fill: parent clip: true - model: EmailModel {} + model: emailModel delegate: Item { height: 60 Layout.fillWidth: true @@ -111,7 +130,11 @@ Item { anchors.fill: parent onClicked: { emailListView.currentIndex = index - // TODO: Show email preview in the bottom pane + // Show email preview in the bottom pane + // For demo, we'll show some basic info + emailPreview.text = "From: " + senderName + "\n" + + "Subject: " + subject + "\n" + + "Date: " + time.toString() } } } diff --git a/resources/qml/SettingsPage.qml b/resources/qml/SettingsPage.qml new file mode 100644 index 0000000..ad24efd --- /dev/null +++ b/resources/qml/SettingsPage.qml @@ -0,0 +1,19 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 + +Item { + id: settingsPage + anchors.fill: parent + + Rectangle { + anchors.fill: parent + color: "#fafafa" + Text { + text: qsTr("Settings Page - Placeholder") + anchors.centerIn: parent + font.pointSize: 16 + color: "#666" + } + } +} \ No newline at end of file diff --git a/resources/qml/Shell.qml b/resources/qml/Shell.qml index 24946d1..f010af0 100644 --- a/resources/qml/Shell.qml +++ b/resources/qml/Shell.qml @@ -85,34 +85,18 @@ ApplicationWindow { StackView { id: stackView anchors.fill: parent - initialItem: mailListPage + initialItem: MailListPage {} } - // Define pages - MailListPage { - id: mailListPage - } // Placeholder for other pages - Rectangle { - color: "#f0f0f0" - Text { - text: qsTr("Calendar Page") - anchors.centerIn: parent - } + CalendarPage { + id: calendarPage } - Rectangle { - color: "#f0f0f0" - Text { - text: qsTr("Contacts Page") - anchors.centerIn: parent - } + ContactsPage { + id: contactsPage } - Rectangle { - color: "#f0f0f0" - Text { - text: qsTr("Settings Page") - anchors.centerIn: parent - } + SettingsPage { + id: settingsPage } // Hamburger button to open drawer @@ -155,20 +139,4 @@ component NavigationItem: Button { 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/src/ui/models/EmailListModel.cpp b/src/ui/models/EmailListModel.cpp new file mode 100644 index 0000000..97c4ee7 --- /dev/null +++ b/src/ui/models/EmailListModel.cpp @@ -0,0 +1,98 @@ +#include "EmailListModel.h" +#include +#include + +EmailListModel::EmailListModel(QObject *parent) + : QAbstractListModel(parent), + m_mailItemDao(MailItemDao::instance()) +{ + refresh(); +} + +int EmailListModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + return m_emails.size(); +} + +QVariant EmailListModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() >= m_emails.size()) + return QVariant(); + + const MailItem& item = m_emails.at(index.row()); + + switch (role) { + case IdRole: + return item.id(); + case SubjectRole: + return item.subject(); + case SenderRole: + return item.sender(); + case RecipientRole: + return item.recipient(); + case DateRole: + return item.date(); + case ReadRole: + return item.isRead(); + case FlaggedRole: + return item.isFlagged(); + case AttachmentsRole: + return QVariant::fromValue(item.attachments()); + case FileIdRole: + return item.fileId(); + case SizeRole: + return item.size(); + case MessageIdRole: + return item.messageId(); + case SenderInitialRole: { + QString sender = item.sender(); + if (sender.isEmpty()) + return QString(); + // Get first character, or first non-space character? + // Simple: first character of the sender string + return QString(sender.at(0)).toUpper(); + } + default: + return QVariant(); + } +} + +QHash EmailListModel::roleNames() const +{ + QHash roles; + roles[IdRole] = "id"; + roles[SubjectRole] = "subject"; + roles[SenderRole] = "sender"; + roles[RecipientRole] = "recipient"; + roles[DateRole] = "date"; + roles[ReadRole] = "read"; + roles[FlaggedRole] = "flagged"; + roles[AttachmentsRole] = "attachments"; + roles[FileIdRole] = "fileId"; + roles[SizeRole] = "size"; + roles[MessageIdRole] = "messageId"; + roles[SenderInitialRole] = "senderInitial"; + return roles; +} + +void EmailListModel::setFolderId(int folderId) +{ + if (m_folderId == folderId) + return; + m_folderId = folderId; + refresh(); +} + +void EmailListModel::refresh() +{ + beginResetModel(); + if (m_folderId == -1) { + m_emails = m_mailItemDao.findAll(); + } else { + m_emails = m_mailItemDao.findByFolderId(m_folderId); + } + endResetModel(); + qDebug() << "EmailListModel refreshed with" << m_emails.size() << "emails for folderId" << m_folderId; +} \ No newline at end of file diff --git a/src/ui/models/EmailListModel.h b/src/ui/models/EmailListModel.h new file mode 100644 index 0000000..11acfb4 --- /dev/null +++ b/src/ui/models/EmailListModel.h @@ -0,0 +1,46 @@ +#ifndef EMAILLISTMODEL_H +#define EMAILLISTMODEL_H + +#include +#include +#include +#include "../db/dao/mailitemdao.h" + +class EmailListModel : public QAbstractListModel +{ + Q_OBJECT +public: + explicit EmailListModel(QObject *parent = nullptr); + ~EmailListModel() override = default; + + enum EmailRoles { + IdRole = Qt::UserRole + 1, + SubjectRole, + SenderRole, + RecipientRole, + DateRole, + ReadRole, + FlaggedRole, + AttachmentsRole, + FileIdRole, + SizeRole, + MessageIdRole, + SenderInitialRole // computed from sender + }; + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + QHash roleNames() const override; + + // Set the folderId to filter emails; -1 means all folders + void setFolderId(int folderId); + // Refresh the model from the database based on current folderId + void refresh(); + +private: + QVector m_emails; + int m_folderId{-1}; // -1 means all folders + MailItemDao& m_mailItemDao; +}; + +#endif // EMAILLISTMODEL_H \ No newline at end of file