feat: move-to-folder dialog, batch actions, reader source view, Focused/Other pivot
- MoveToFolderDialog: modal folder picker per account (excludes current) - Wire 'Mover a...' in reader ⋯ menu + batch move (emit -1 -> dialog) - Connect batch delete/mark-unread/flag handlers (were unconnected) - Reader: 'Ver codigo fuente' reads .eml via MimeStorageService::readEmlFile - Reader: 'Marcar como no leido' emits markUnreadRequested - Pivot Focused/Other: EmailListModel::setShowFocusedOnly heuristic (flagged/pinned/frequent senders), reset to Focused on inbox select
This commit is contained in:
@@ -108,6 +108,7 @@ set(SRC_FILES
|
|||||||
src/core/synchronizerprovider.cpp
|
src/core/synchronizerprovider.cpp
|
||||||
src/core/accountsetupdialoglauncher.cpp
|
src/core/accountsetupdialoglauncher.cpp
|
||||||
src/core/oauthcallbackserver.cpp
|
src/core/oauthcallbackserver.cpp
|
||||||
|
src/core/icloudauthenticator.cpp
|
||||||
src/ui/mainmainwindow.cpp
|
src/ui/mainmainwindow.cpp
|
||||||
src/ui/mainmainwindow.h
|
src/ui/mainmainwindow.h
|
||||||
src/ui/newmessagedialog.cpp
|
src/ui/newmessagedialog.cpp
|
||||||
@@ -138,6 +139,8 @@ set(SRC_FILES
|
|||||||
src/ui/categorytreewidget.h
|
src/ui/categorytreewidget.h
|
||||||
src/ui/rulesmanagerdialog.cpp
|
src/ui/rulesmanagerdialog.cpp
|
||||||
src/ui/rulesmanagerdialog.h
|
src/ui/rulesmanagerdialog.h
|
||||||
|
src/ui/movetofolderdialog.cpp
|
||||||
|
src/ui/movetofolderdialog.h
|
||||||
src/ui/ruleditordialog.cpp
|
src/ui/ruleditordialog.cpp
|
||||||
src/ui/ruleditordialog.h
|
src/ui/ruleditordialog.h
|
||||||
src/ui/templateeditordialog.cpp
|
src/ui/templateeditordialog.cpp
|
||||||
|
|||||||
+19
-9
@@ -660,9 +660,8 @@ void MailListView::onBatchMarkUnreadClicked()
|
|||||||
void MailListView::onBatchMoveClicked()
|
void MailListView::onBatchMoveClicked()
|
||||||
{
|
{
|
||||||
if (m_selectedMailIds.isEmpty()) return;
|
if (m_selectedMailIds.isEmpty()) return;
|
||||||
// TODO: Show folder picker dialog
|
// targetFolderId == -1 signals the UI layer to present a folder picker
|
||||||
// For now, just emit signal
|
emit batchMoveRequested(m_selectedMailIds, -1);
|
||||||
// emit batchMoveRequested(m_selectedMailIds, targetFolderId);
|
|
||||||
exitMultiSelectMode();
|
exitMultiSelectMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -678,19 +677,30 @@ void MailListView::setFolderType(const QString& folderType)
|
|||||||
m_pivotCombo->setVisible(folderType == "inbox");
|
m_pivotCombo->setVisible(folderType == "inbox");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MailListView::resetPivot()
|
||||||
|
{
|
||||||
|
// Default to Focused (index 0). blockSignals to avoid recursive refresh.
|
||||||
|
m_pivotCombo->blockSignals(true);
|
||||||
|
m_pivotCombo->setCurrentIndex(0);
|
||||||
|
m_pivotCombo->blockSignals(false);
|
||||||
|
if (m_sourceModel) {
|
||||||
|
m_sourceModel->setShowFocusedOnly(true);
|
||||||
|
m_sourceModel->refresh();
|
||||||
|
}
|
||||||
|
refreshViews();
|
||||||
|
}
|
||||||
|
|
||||||
void MailListView::onPivotChanged(int index)
|
void MailListView::onPivotChanged(int index)
|
||||||
{
|
{
|
||||||
// 0 = Focused (Principal), 1 = Other (Otros)
|
// 0 = Focused (Principal), 1 = Other (Otros)
|
||||||
// TODO: Implement pivot filtering logic
|
|
||||||
// For now, just update the model filter
|
|
||||||
if (!m_sourceModel) return;
|
if (!m_sourceModel) return;
|
||||||
|
|
||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
// Focused: important emails (could be based on sender frequency, AI, etc.)
|
// Focused: important emails (flagged, pinned, or frequent senders)
|
||||||
// m_sourceModel->setShowFocusedOnly(true);
|
m_sourceModel->setShowFocusedOnly(true);
|
||||||
} else {
|
} else {
|
||||||
// Other: promotional, notifications, etc.
|
// Other: show everything (promotional, notifications, etc.)
|
||||||
// m_sourceModel->setShowFocusedOnly(false);
|
m_sourceModel->setShowFocusedOnly(false);
|
||||||
}
|
}
|
||||||
refreshViews();
|
refreshViews();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,9 @@ signals:
|
|||||||
void onlineSearchRequested(const QString& query);
|
void onlineSearchRequested(const QString& query);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void setFolderType(const QString& folderType); // "inbox", "sent", "drafts", etc.
|
void setFolderType(const QString& folderType);
|
||||||
|
/// Reset pivot to Focused and hide for non-inbox folders.
|
||||||
|
void resetPivot(); // "inbox", "sent", "drafts", etc.
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onRowSelected(const QModelIndex ¤t, const QModelIndex &previous);
|
void onRowSelected(const QModelIndex ¤t, const QModelIndex &previous);
|
||||||
|
|||||||
+110
-1
@@ -8,6 +8,7 @@
|
|||||||
#include "services/rulesengine.h"
|
#include "services/rulesengine.h"
|
||||||
#include "ui/rulesmanagerdialog.h"
|
#include "ui/rulesmanagerdialog.h"
|
||||||
#include "ui/accountsetupdialog.h"
|
#include "ui/accountsetupdialog.h"
|
||||||
|
#include "ui/movetofolderdialog.h"
|
||||||
#include "ui/delegates/FolderTreeDelegate.h"
|
#include "ui/delegates/FolderTreeDelegate.h"
|
||||||
#include "services/preferencesservice.h"
|
#include "services/preferencesservice.h"
|
||||||
#include "services/startupbehaviorservice.h"
|
#include "services/startupbehaviorservice.h"
|
||||||
@@ -268,6 +269,10 @@ void MainMainWindow::setupMailPage()
|
|||||||
connect(m_mailListView, &MailListView::replyRequested, this, &MainMainWindow::onReplyRequested);
|
connect(m_mailListView, &MailListView::replyRequested, this, &MainMainWindow::onReplyRequested);
|
||||||
connect(m_mailListView, &MailListView::forwardRequested, this, &MainMainWindow::onForwardRequested);
|
connect(m_mailListView, &MailListView::forwardRequested, this, &MainMainWindow::onForwardRequested);
|
||||||
connect(m_mailListView, &MailListView::markUnreadRequested, this, &MainMainWindow::onMarkUnreadRequested);
|
connect(m_mailListView, &MailListView::markUnreadRequested, this, &MainMainWindow::onMarkUnreadRequested);
|
||||||
|
connect(m_mailListView, &MailListView::batchMoveRequested, this, &MainMainWindow::onBatchMoveRequested);
|
||||||
|
connect(m_mailListView, &MailListView::batchDeleteRequested, this, &MainMainWindow::onBatchDeleteRequested);
|
||||||
|
connect(m_mailListView, &MailListView::batchMarkUnreadRequested, this, &MainMainWindow::onBatchMarkUnreadRequested);
|
||||||
|
connect(m_mailListView, &MailListView::batchFlagRequested, this, &MainMainWindow::onBatchFlagRequested);
|
||||||
m_folderSplitter->addWidget(m_mailListView);
|
m_folderSplitter->addWidget(m_mailListView);
|
||||||
|
|
||||||
// Viewer stack: placeholder, reader, compose
|
// Viewer stack: placeholder, reader, compose
|
||||||
@@ -299,6 +304,7 @@ void MainMainWindow::setupMailPage()
|
|||||||
connect(m_emailViewer, &ReaderView::replyRequested, this, &MainMainWindow::onReaderReplyRequested);
|
connect(m_emailViewer, &ReaderView::replyRequested, this, &MainMainWindow::onReaderReplyRequested);
|
||||||
connect(m_emailViewer, &ReaderView::forwardRequested, this, &MainMainWindow::onReaderForwardRequested);
|
connect(m_emailViewer, &ReaderView::forwardRequested, this, &MainMainWindow::onReaderForwardRequested);
|
||||||
connect(m_emailViewer, &ReaderView::deleteRequested, this, &MainMainWindow::onReaderDeleteRequested);
|
connect(m_emailViewer, &ReaderView::deleteRequested, this, &MainMainWindow::onReaderDeleteRequested);
|
||||||
|
connect(m_emailViewer, &ReaderView::markUnreadRequested, this, &MainMainWindow::onMarkUnreadRequested);
|
||||||
connect(m_emailViewer, &ReaderView::detachRequested, this, [this]() {
|
connect(m_emailViewer, &ReaderView::detachRequested, this, [this]() {
|
||||||
if (m_currentMailId >= 0) {
|
if (m_currentMailId >= 0) {
|
||||||
openMailInIndependentWindow(m_currentMailId);
|
openMailInIndependentWindow(m_currentMailId);
|
||||||
@@ -412,6 +418,8 @@ void MainMainWindow::onFolderSelected(const QModelIndex &index)
|
|||||||
m_mailService->fetchMails(QString::number(account->id()), QString::number(m_currentFolderId));
|
m_mailService->fetchMails(QString::number(account->id()), QString::number(m_currentFolderId));
|
||||||
// Tell MailListView the folder type for Focused/Other pivot
|
// Tell MailListView the folder type for Focused/Other pivot
|
||||||
m_mailListView->setFolderType(folder.type().toLower()); // "inbox", "sent", etc.
|
m_mailListView->setFolderType(folder.type().toLower()); // "inbox", "sent", etc.
|
||||||
|
// Reset pivot to Focused for inbox folders
|
||||||
|
m_mailListView->resetPivot();
|
||||||
delete account;
|
delete account;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -525,6 +533,107 @@ void MainMainWindow::onDeleteRequested(int mailId)
|
|||||||
statusBar()->showMessage(tr("Mensaje eliminado"), 3000);
|
statusBar()->showMessage(tr("Mensaje eliminado"), 3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainMainWindow::onMoveMailToFolderRequested(int mailId)
|
||||||
|
{
|
||||||
|
// Determine the account of the mail (via its current folder).
|
||||||
|
std::optional<MailItem> optMail = MailItemDao::findById(mailId);
|
||||||
|
if (!optMail) {
|
||||||
|
statusBar()->showMessage(tr("Mensaje no encontrado"), 3000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int currentFolderId = optMail->folderId();
|
||||||
|
std::optional<Folder> optFolder = FolderDao::findById(currentFolderId);
|
||||||
|
if (!optFolder) {
|
||||||
|
statusBar()->showMessage(tr("Carpeta no encontrada"), 3000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int accountId = optFolder->accountId();
|
||||||
|
|
||||||
|
QVector<Folder> folders = FolderDao::findByAccountId(accountId);
|
||||||
|
MoveToFolderDialog dlg(folders, currentFolderId, this);
|
||||||
|
if (dlg.exec() != QDialog::Accepted) return;
|
||||||
|
int targetFolderId = dlg.selectedFolderId();
|
||||||
|
if (targetFolderId < 0) return;
|
||||||
|
|
||||||
|
m_mailService->moveMail(QString::number(mailId), QString::number(targetFolderId));
|
||||||
|
// MailService::moveMail emits mailMoved; here we also refresh locally.
|
||||||
|
if (m_currentMailId == mailId) m_currentMailId = -1;
|
||||||
|
m_emailModel->refresh();
|
||||||
|
m_mailListView->treeView()->clearSelection();
|
||||||
|
m_viewerStack->setCurrentIndex(0);
|
||||||
|
statusBar()->showMessage(tr("Correo movido"), 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainMainWindow::onBatchMoveRequested(const QVector<int> &mailIds, int targetFolderId)
|
||||||
|
{
|
||||||
|
if (mailIds.isEmpty()) return;
|
||||||
|
|
||||||
|
// Resolve target folder. targetFolderId == -1 → ask the user via dialog.
|
||||||
|
int resolvedTarget = targetFolderId;
|
||||||
|
if (resolvedTarget < 0) {
|
||||||
|
// Use the account of the first mail.
|
||||||
|
std::optional<MailItem> optMail = MailItemDao::findById(mailIds.first());
|
||||||
|
if (!optMail) return;
|
||||||
|
std::optional<Folder> optFolder = FolderDao::findById(optMail->folderId());
|
||||||
|
if (!optFolder) return;
|
||||||
|
QVector<Folder> folders = FolderDao::findByAccountId(optFolder->accountId());
|
||||||
|
MoveToFolderDialog dlg(folders, optMail->folderId(), this);
|
||||||
|
if (dlg.exec() != QDialog::Accepted) return;
|
||||||
|
resolvedTarget = dlg.selectedFolderId();
|
||||||
|
if (resolvedTarget < 0) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int mailId : mailIds) {
|
||||||
|
m_mailService->moveMail(QString::number(mailId), QString::number(resolvedTarget));
|
||||||
|
if (m_currentMailId == mailId) m_currentMailId = -1;
|
||||||
|
}
|
||||||
|
m_emailModel->refresh();
|
||||||
|
m_mailListView->treeView()->clearSelection();
|
||||||
|
m_viewerStack->setCurrentIndex(0);
|
||||||
|
statusBar()->showMessage(tr("%1 correo(s) movidos").arg(mailIds.size()), 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainMainWindow::onBatchDeleteRequested(const QVector<int> &mailIds)
|
||||||
|
{
|
||||||
|
if (mailIds.isEmpty()) return;
|
||||||
|
for (int mailId : mailIds) {
|
||||||
|
MailItemDao::remove(mailId);
|
||||||
|
if (m_currentMailId == mailId) m_currentMailId = -1;
|
||||||
|
}
|
||||||
|
m_emailModel->refresh();
|
||||||
|
m_mailListView->treeView()->clearSelection();
|
||||||
|
m_viewerStack->setCurrentIndex(0);
|
||||||
|
statusBar()->showMessage(tr("%1 correo(s) eliminados").arg(mailIds.size()), 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainMainWindow::onBatchMarkUnreadRequested(const QVector<int> &mailIds)
|
||||||
|
{
|
||||||
|
if (mailIds.isEmpty()) return;
|
||||||
|
for (int mailId : mailIds) {
|
||||||
|
auto item = MailItemDao::findById(mailId);
|
||||||
|
if (item.has_value()) {
|
||||||
|
item->setRead(false);
|
||||||
|
MailItemDao::update(*item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_emailModel->refresh();
|
||||||
|
statusBar()->showMessage(tr("%1 correo(s) marcados como no leídos").arg(mailIds.size()), 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainMainWindow::onBatchFlagRequested(const QVector<int> &mailIds, bool flagged)
|
||||||
|
{
|
||||||
|
if (mailIds.isEmpty()) return;
|
||||||
|
for (int mailId : mailIds) {
|
||||||
|
auto item = MailItemDao::findById(mailId);
|
||||||
|
if (item.has_value()) {
|
||||||
|
item->setFlagged(flagged);
|
||||||
|
MailItemDao::update(*item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_emailModel->refresh();
|
||||||
|
statusBar()->showMessage(tr("%1 correo(s) actualizados").arg(mailIds.size()), 3000);
|
||||||
|
}
|
||||||
|
|
||||||
void MainMainWindow::onCategoryRequested(int mailId)
|
void MainMainWindow::onCategoryRequested(int mailId)
|
||||||
{
|
{
|
||||||
// Show category context menu at cursor position
|
// Show category context menu at cursor position
|
||||||
@@ -538,7 +647,7 @@ void MainMainWindow::onMoreRequested(int mailId, const QPoint &globalPos)
|
|||||||
QMenu menu;
|
QMenu menu;
|
||||||
menu.addAction("Responder", [this, mailId]() { onReaderReplyRequested(mailId); });
|
menu.addAction("Responder", [this, mailId]() { onReaderReplyRequested(mailId); });
|
||||||
menu.addAction("Reenviar", [this, mailId]() { onReaderForwardRequested(mailId); });
|
menu.addAction("Reenviar", [this, mailId]() { onReaderForwardRequested(mailId); });
|
||||||
menu.addAction("Mover a...", [this, mailId]() { /* TODO: move dialog */ });
|
menu.addAction("Mover a...", [this, mailId]() { onMoveMailToFolderRequested(mailId); });
|
||||||
menu.addAction("Marcar como no leído", [this, mailId]() {
|
menu.addAction("Marcar como no leído", [this, mailId]() {
|
||||||
auto item = MailItemDao::findById(mailId);
|
auto item = MailItemDao::findById(mailId);
|
||||||
if (item.has_value()) {
|
if (item.has_value()) {
|
||||||
|
|||||||
@@ -81,6 +81,11 @@ private slots:
|
|||||||
void onMoreRequested(int mailId, const QPoint &globalPos);
|
void onMoreRequested(int mailId, const QPoint &globalPos);
|
||||||
void onReplyRequested(int mailId);
|
void onReplyRequested(int mailId);
|
||||||
void onForwardRequested(int mailId);
|
void onForwardRequested(int mailId);
|
||||||
|
void onMoveMailToFolderRequested(int mailId);
|
||||||
|
void onBatchMoveRequested(const QVector<int> &mailIds, int targetFolderId);
|
||||||
|
void onBatchDeleteRequested(const QVector<int> &mailIds);
|
||||||
|
void onBatchMarkUnreadRequested(const QVector<int> &mailIds);
|
||||||
|
void onBatchFlagRequested(const QVector<int> &mailIds, bool flagged);
|
||||||
void onMarkUnreadRequested(int mailId);
|
void onMarkUnreadRequested(int mailId);
|
||||||
// Online search
|
// Online search
|
||||||
void onOnlineSearchRequested(const QString& query);
|
void onOnlineSearchRequested(const QString& query);
|
||||||
|
|||||||
@@ -173,6 +173,22 @@ void EmailListModel::refresh()
|
|||||||
}
|
}
|
||||||
m_emails = firstBatch;
|
m_emails = firstBatch;
|
||||||
m_loadedCount = firstBatch.size();
|
m_loadedCount = firstBatch.size();
|
||||||
|
|
||||||
|
// Apply Focused pivot heuristic in memory (important/frequent senders first).
|
||||||
|
if (m_focusedOnly) {
|
||||||
|
QVector<MailItem> focused;
|
||||||
|
// Count occurrences of each sender.
|
||||||
|
QHash<QString, int> senderCount;
|
||||||
|
for (const MailItem &m : m_emails)
|
||||||
|
senderCount[m.sender()]++;
|
||||||
|
for (const MailItem &m : m_emails) {
|
||||||
|
if (m.isFlagged() || m.isPinned() || senderCount.value(m.sender()) >= 2)
|
||||||
|
focused.append(m);
|
||||||
|
}
|
||||||
|
m_emails = focused;
|
||||||
|
m_loadedCount = focused.size();
|
||||||
|
}
|
||||||
|
|
||||||
endResetModel();
|
endResetModel();
|
||||||
|
|
||||||
qDebug() << "EmailListModel refreshed: total=" << m_totalCount << "loaded=" << m_loadedCount << "for folderId" << m_folderId;
|
qDebug() << "EmailListModel refreshed: total=" << m_totalCount << "loaded=" << m_loadedCount << "for folderId" << m_folderId;
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ public:
|
|||||||
void setShowHasAttachments(bool show);
|
void setShowHasAttachments(bool show);
|
||||||
// Set filter for pinned emails
|
// Set filter for pinned emails
|
||||||
void setShowPinnedOnly(bool show);
|
void setShowPinnedOnly(bool show);
|
||||||
|
// Focused/Other pivot heuristic
|
||||||
|
void setShowFocusedOnly(bool show) { m_focusedOnly = show; refresh(); }
|
||||||
|
|
||||||
// Get all emails currently in the model (after filtering)
|
// Get all emails currently in the model (after filtering)
|
||||||
const QVector<MailItem>& emails() const { return m_emails; }
|
const QVector<MailItem>& emails() const { return m_emails; }
|
||||||
@@ -70,6 +72,7 @@ private:
|
|||||||
bool m_flaggedOnly{false};
|
bool m_flaggedOnly{false};
|
||||||
bool m_hasAttachments{false};
|
bool m_hasAttachments{false};
|
||||||
bool m_pinnedOnly{false};
|
bool m_pinnedOnly{false};
|
||||||
|
bool m_focusedOnly{false};
|
||||||
int m_batchSize{50}; // emails per fetch
|
int m_batchSize{50}; // emails per fetch
|
||||||
int m_loadedCount{0}; // how many emails currently loaded
|
int m_loadedCount{0}; // how many emails currently loaded
|
||||||
int m_totalCount{0}; // total matching emails in DB
|
int m_totalCount{0}; // total matching emails in DB
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
#include "ui/movetofolderdialog.h"
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QListWidgetItem>
|
||||||
|
|
||||||
|
MoveToFolderDialog::MoveToFolderDialog(const QVector<Folder> &folders, int excludeFolderId,
|
||||||
|
QWidget *parent)
|
||||||
|
: QDialog(parent)
|
||||||
|
, m_list(new QListWidget(this))
|
||||||
|
{
|
||||||
|
setWindowTitle(tr("Mover a carpeta"));
|
||||||
|
resize(320, 400);
|
||||||
|
|
||||||
|
QVBoxLayout *lay = new QVBoxLayout(this);
|
||||||
|
|
||||||
|
QLabel *title = new QLabel(tr("Selecciona la carpeta de destino:"), this);
|
||||||
|
lay->addWidget(title);
|
||||||
|
|
||||||
|
m_list->setStyleSheet(
|
||||||
|
"QListWidget { border: 1px solid #d1d1d6; border-radius: 8px; padding: 4px; font-size: 13px; }"
|
||||||
|
"QListWidget::item { padding: 7px 8px; }"
|
||||||
|
"QListWidget::item:selected { background: #0071e3; color: #ffffff; border-radius: 6px; }"
|
||||||
|
);
|
||||||
|
connect(m_list, &QListWidget::itemDoubleClicked, this, [this](QListWidgetItem *) { accept(); });
|
||||||
|
|
||||||
|
for (const Folder &folder : folders) {
|
||||||
|
if (folder.id() == excludeFolderId) continue;
|
||||||
|
QListWidgetItem *item = new QListWidgetItem(folder.name(), m_list);
|
||||||
|
item->setData(Qt::UserRole, folder.id());
|
||||||
|
m_list->addItem(item);
|
||||||
|
}
|
||||||
|
if (m_list->count() == 0) {
|
||||||
|
new QListWidgetItem(tr("(sin carpetas)"), m_list);
|
||||||
|
}
|
||||||
|
lay->addWidget(m_list, 1);
|
||||||
|
|
||||||
|
QHBoxLayout *btnLay = new QHBoxLayout();
|
||||||
|
QPushButton *cancel = new QPushButton(tr("Cancelar"), this);
|
||||||
|
connect(cancel, &QPushButton::clicked, this, &QDialog::reject);
|
||||||
|
btnLay->addWidget(cancel);
|
||||||
|
|
||||||
|
QPushButton *ok = new QPushButton(tr("Mover"), this);
|
||||||
|
ok->setDefault(true);
|
||||||
|
connect(ok, &QPushButton::clicked, this, &MoveToFolderDialog::accept);
|
||||||
|
btnLay->addWidget(ok);
|
||||||
|
lay->addLayout(btnLay);
|
||||||
|
|
||||||
|
if (m_list->count() > 0) m_list->setCurrentRow(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MoveToFolderDialog::accept()
|
||||||
|
{
|
||||||
|
QListWidgetItem *item = m_list->currentItem();
|
||||||
|
if (item && !item->data(Qt::UserRole).isNull()) {
|
||||||
|
m_selectedId = item->data(Qt::UserRole).toInt();
|
||||||
|
}
|
||||||
|
QDialog::accept();
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QListWidget>
|
||||||
|
#include "core/models/folder.h"
|
||||||
|
|
||||||
|
/// Modal dialog to pick a target folder for moving one or more mails.
|
||||||
|
class MoveToFolderDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit MoveToFolderDialog(const QVector<Folder> &folders, int excludeFolderId = -1,
|
||||||
|
QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
/// Target folder id, or -1 if cancelled.
|
||||||
|
int selectedFolderId() const { return m_selectedId; }
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void accept() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QListWidget *m_list;
|
||||||
|
int m_selectedId = -1;
|
||||||
|
};
|
||||||
+47
-3
@@ -1,4 +1,5 @@
|
|||||||
#include "ui/readerview.h"
|
#include "ui/readerview.h"
|
||||||
|
#include "services/mimestorage.h"
|
||||||
#include <QFont>
|
#include <QFont>
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
@@ -15,6 +16,9 @@
|
|||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QClipboard>
|
#include <QClipboard>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QPlainTextEdit>
|
||||||
|
#include <QDialogButtonBox>
|
||||||
#include <QScrollBar>
|
#include <QScrollBar>
|
||||||
#include <QShortcut>
|
#include <QShortcut>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
@@ -325,10 +329,50 @@ void ReaderView::setupUI() {
|
|||||||
if (item.has_value()) QApplication::clipboard()->setText(item->sender());
|
if (item.has_value()) QApplication::clipboard()->setText(item->sender());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
moreMenu->addAction("Ver código fuente", [this]() { /* TODO */ });
|
moreMenu->addAction("Ver código fuente", [this]() {
|
||||||
|
if (m_currentMailId < 0) return;
|
||||||
|
std::optional<MailItem> item = MailItemDao::findById(m_currentMailId);
|
||||||
|
if (!item.has_value()) return;
|
||||||
|
QByteArray raw;
|
||||||
|
if (!item->fileId().isEmpty()) {
|
||||||
|
MimeStorageService storage;
|
||||||
|
raw = storage.readEmlFile(item->fileId());
|
||||||
|
}
|
||||||
|
if (raw.isEmpty()) raw = item->rawMime();
|
||||||
|
if (raw.isEmpty()) {
|
||||||
|
QMessageBox::information(this, tr("Código fuente"),
|
||||||
|
tr("No hay código fuente disponible para este correo."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QDialog dlg(this);
|
||||||
|
dlg.setWindowTitle(tr("Código fuente"));
|
||||||
|
dlg.resize(760, 560);
|
||||||
|
QVBoxLayout *lay = new QVBoxLayout(&dlg);
|
||||||
|
QPlainTextEdit *view = new QPlainTextEdit(&dlg);
|
||||||
|
view->setReadOnly(true);
|
||||||
|
view->setPlainText(QString::fromUtf8(raw));
|
||||||
|
view->setStyleSheet("font-family: 'Consolas','Menlo',monospace; font-size: 12px;");
|
||||||
|
lay->addWidget(view);
|
||||||
|
QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Close, &dlg);
|
||||||
|
connect(buttons, &QDialogButtonBox::rejected, &dlg, &QDialog::reject);
|
||||||
|
lay->addWidget(buttons);
|
||||||
|
dlg.exec();
|
||||||
|
});
|
||||||
moreMenu->addSeparator();
|
moreMenu->addSeparator();
|
||||||
moreMenu->addAction("Marcar como no leído", [this]() { /* TODO */ });
|
moreMenu->addAction("Marcar como no leído", [this]() {
|
||||||
moreMenu->addAction("Marcar como spam", [this]() { /* TODO */ });
|
if (m_currentMailId < 0) return;
|
||||||
|
auto item = MailItemDao::findById(m_currentMailId);
|
||||||
|
if (item.has_value()) {
|
||||||
|
item->setRead(false);
|
||||||
|
MailItemDao::update(*item);
|
||||||
|
emit markUnreadRequested(m_currentMailId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
moreMenu->addAction("Marcar como spam", [this]() {
|
||||||
|
if (m_currentMailId < 0) return;
|
||||||
|
QMessageBox::information(this, tr("Marcar como spam"),
|
||||||
|
tr("Función: mover a carpeta «Spam». Disponible en una próxima versión."));
|
||||||
|
});
|
||||||
m_moreButton->setMenu(moreMenu);
|
m_moreButton->setMenu(moreMenu);
|
||||||
m_moreButton->setStyleSheet(
|
m_moreButton->setStyleSheet(
|
||||||
"QToolButton { background: transparent; border: 1px solid #d1d1d6; border-radius: 4px; font-size: 16px; }"
|
"QToolButton { background: transparent; border: 1px solid #d1d1d6; border-radius: 4px; font-size: 16px; }"
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ signals:
|
|||||||
void forwardRequested(int mailId);
|
void forwardRequested(int mailId);
|
||||||
void deleteRequested(int mailId);
|
void deleteRequested(int mailId);
|
||||||
void detachRequested();
|
void detachRequested();
|
||||||
|
void markUnreadRequested(int mailId);
|
||||||
void openAttachmentRequested(const QString &path);
|
void openAttachmentRequested(const QString &path);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
|||||||
Reference in New Issue
Block a user