From 52b7a86fa191b1f0170f80b980e1e95de1c8da34 Mon Sep 17 00:00:00 2001 From: Javier Date: Sun, 30 Aug 2026 02:37:46 +0200 Subject: [PATCH] feat: mark-spam, rule priority persistence, run-rules-on-selected, icons fix, scheduled send + reader polish - Mark as spam: moves mail to Spam/Junk folder of account - Rule priority: persistPriorityOrder persists 1..N via RuleDao::update - Run rules on selected: uses MailListView::selectedMailIds() + onRunRulesOnSelected - Icons: switch code to qrc short aliases (close/send/bold/etc) + new detach.svg -> resolves all 'Cannot open file' SVG warnings at startup - Categories: CompactMailDelegate paints category chips from CategoryDao - Scheduled send: MailService::scheduleSend with QTimer; wire both composer paths - Reader: show real 'Para:' recipient; delete attachment removes stored file --- icons.qrc | 1 + src/services/mailservice.cpp | 33 ++++++++++ src/services/mailservice.h | 8 +++ src/ui/composeview.cpp | 12 ++-- src/ui/delegates/CompactMailDelegate.cpp | 35 ++++++++++- src/ui/maillistview.h | 2 + src/ui/mainmainwindow.cpp | 79 +++++++++++++++++++----- src/ui/mainmainwindow.h | 1 + src/ui/readerview.cpp | 29 +++++++-- src/ui/readerview.h | 1 + src/ui/richtexteditor.cpp | 6 +- src/ui/rulesmanagerdialog.cpp | 22 ++++++- src/ui/rulesmanagerdialog.h | 1 + 13 files changed, 195 insertions(+), 35 deletions(-) diff --git a/icons.qrc b/icons.qrc index aff78ee..3b053b1 100644 --- a/icons.qrc +++ b/icons.qrc @@ -12,6 +12,7 @@ resources/icons/SVG/Linear/Arrows Action/Reply.svg resources/icons/SVG/Linear/Arrows Action/Forward.svg resources/icons/SVG/Linear/Essentional, UI/Trash Bin Minimalistic.svg + resources/icons/SVG/Linear/Arrows Action/Square Top Down.svg resources/icons/SVG/Linear/Design, Tools/Filters.svg resources/icons/SVG/Linear/Time/Stopwatch Play.svg resources/icons/SVG/Linear/Files/File Text.svg diff --git a/src/services/mailservice.cpp b/src/services/mailservice.cpp index e508cb7..80a04e7 100644 --- a/src/services/mailservice.cpp +++ b/src/services/mailservice.cpp @@ -353,6 +353,39 @@ void MailService::sendMail(const MailItem &mail, const QString &accountId, watcher->setFuture(future); } +void MailService::scheduleSend(const MailItem &mail, const QString &accountId, + const QStringList &attachmentPaths, const QDateTime &when) +{ + QDateTime now = QDateTime::currentDateTime(); + if (!when.isValid() || when <= now) { + // Send immediately + sendMail(mail, accountId, attachmentPaths); + return; + } + + m_pendingMail = mail; + m_pendingAccountId = accountId; + m_pendingAttachments = attachmentPaths; + + int ms = qMax(1000, int(now.msecsTo(when))); + m_sendTimer.stop(); + // Re-arm the single-shot timer with the delay until `when`. + m_sendTimer.disconnect(this); + connect(&m_sendTimer, &QTimer::timeout, this, [this]() { + MailItem mail = m_pendingMail; + QString accountId = m_pendingAccountId; + QStringList attachments = m_pendingAttachments; + m_pendingMail = MailItem(); + m_pendingAccountId.clear(); + m_pendingAttachments.clear(); + sendMail(mail, accountId, attachments); + }); + m_sendTimer.start(ms); + + emit statusMessage( + tr("Envío programado para %1").arg(when.toString("dd/MM/yyyy HH:mm"))); +} + void MailService::fetchMails(const QString &accountId, const QString &folderId) { // Determine provider type from account diff --git a/src/services/mailservice.h b/src/services/mailservice.h index c2a1b3d..0bb39a7 100644 --- a/src/services/mailservice.h +++ b/src/services/mailservice.h @@ -5,6 +5,7 @@ #include #include #include +#include #include "core/mailitem.h" #include "core/emailcomposerbridge.h" #include "core/synchronizerprovider.h" @@ -24,6 +25,9 @@ public: void sendMail(const MailItem &mail, const QString &accountId); void sendMail(const MailItem &mail, const QString &accountId, const QStringList &attachmentPaths); + /// Schedule a mail to be sent at `when`. Emits mailSent(mailItemId) when sent. + void scheduleSend(const MailItem &mail, const QString &accountId, + const QStringList &attachmentPaths, const QDateTime &when); void fetchMails(const QString &accountId, const QString &folderId); void moveMail(const QString &mailItemId, const QString &targetFolderId); void deleteMail(const QString &mailItemId); @@ -50,6 +54,10 @@ private: const QString &folderId); EmailComposerBridge *m_composer; AccountService *m_accountService; + QTimer m_sendTimer{this}; // one-shot timer driving scheduled sends + MailItem m_pendingMail; + QString m_pendingAccountId; + QStringList m_pendingAttachments; }; #endif // MAILSERVICE_H diff --git a/src/ui/composeview.cpp b/src/ui/composeview.cpp index 84253da..41a5c40 100644 --- a/src/ui/composeview.cpp +++ b/src/ui/composeview.cpp @@ -292,7 +292,7 @@ void ComposeView::setupUI() { ccLayout->addWidget(m_ccField, 1); m_hideCcButton = new QPushButton(""); - m_hideCcButton->setIcon(QIcon(QStringLiteral(":/icons/resources/icons/SVG/Linear/Essentional, UI/Close Square.svg"))); + m_hideCcButton->setIcon(QIcon(QStringLiteral(":/icons/close.svg"))); m_hideCcButton->setFixedSize(20, 20); m_hideCcButton->setToolTip(tr("Hide Cc")); m_hideCcButton->setStyleSheet( @@ -319,7 +319,7 @@ void ComposeView::setupUI() { m_hideBccButton = new QPushButton(""); m_hideBccButton->setFixedSize(20, 20); - m_hideBccButton->setIcon(QIcon(QStringLiteral(":/icons/resources/icons/SVG/Linear/Essentional, UI/Close Square.svg"))); + m_hideBccButton->setIcon(QIcon(QStringLiteral(":/icons/close.svg"))); m_hideBccButton->setToolTip(tr("Hide Bcc")); m_hideBccButton->setStyleSheet( "QPushButton { background: transparent; border: none; color: blue; font-weight: bold; }" @@ -348,7 +348,7 @@ void ComposeView::setupUI() { // Detach button placed to the right of the subject line m_detachButton = new QPushButton("");//("↥ Detach"); - m_detachButton->setIcon(QIcon(QStringLiteral(":/icons/resources/icons/SVG/Linear/Arrows Action/Square Top Down.svg"))); + m_detachButton->setIcon(QIcon(QStringLiteral(":/icons/detach.svg"))); m_detachButton->setToolTip(tr("Open compose window in a separate window")); m_detachButton->setStyleSheet( "QPushButton { background: transparent; border: 1px solid #d1d1d6; border-radius: 4px; padding: 6px 12px; color: #555; font-size: 12px; }" @@ -421,7 +421,7 @@ void ComposeView::setupUI() { // Attachments and Templates buttons (right-aligned) QHBoxLayout *buttonRow = new QHBoxLayout(); m_attachButton = new QToolButton(); - m_attachButton->setIcon(QIcon(QStringLiteral(":/icons/resources/icons/SVG/Outline/Messages, Conversation/Paperclip.svg"))); + m_attachButton->setIcon(QIcon(QStringLiteral(":/icons/attachment.svg"))); m_attachButton->setToolTip(tr("Add attachment")); m_attachButton->setIconSize(QSize(20,20)); m_attachButton->setStyleSheet("QToolButton { border: none; padding: 5px; } QToolButton:hover { background: #e0e0e0; border-radius: 3px; }"); @@ -429,7 +429,7 @@ void ComposeView::setupUI() { buttonRow->addWidget(m_attachButton); m_templateButton = new QToolButton(); - m_templateButton->setIcon(QIcon(QStringLiteral(":/icons/resources/icons/SVG/Outline/Files/File Text.svg"))); // Assuming you have a template icon + m_templateButton->setIcon(QIcon(QStringLiteral(":/icons/file-text.svg"))); // Assuming you have a template icon m_templateButton->setToolTip(tr("Email templates")); m_templateButton->setIconSize(QSize(20,20)); m_templateButton->setStyleSheet("QToolButton { border: none; padding: 5px; } QToolButton:hover { background: #e0e0e0; border-radius: 3px; }"); @@ -459,7 +459,7 @@ void ComposeView::setupUI() { m_sendSplit = new QToolButton(); m_sendSplit->setText(tr("Send")); - m_sendSplit->setIcon(QIcon(QStringLiteral(":/icons/resources/icons/SVG/Bold Duotone/Messages, Conversation/Plain.svg"))); + m_sendSplit->setIcon(QIcon(QStringLiteral(":/icons/send.svg"))); m_templateButton->setIconSize(QSize(20,20)); //m_sendSplit->setToolButtonStyle(Qt::ToolButtonTextOnly); m_sendSplit->setPopupMode(QToolButton::MenuButtonPopup); diff --git a/src/ui/delegates/CompactMailDelegate.cpp b/src/ui/delegates/CompactMailDelegate.cpp index a532d40..a5c5c99 100644 --- a/src/ui/delegates/CompactMailDelegate.cpp +++ b/src/ui/delegates/CompactMailDelegate.cpp @@ -1,4 +1,5 @@ #include "CompactMailDelegate.h" +#include "db/dao/categorydao.h" #include #include #include @@ -269,8 +270,38 @@ void CompactMailDelegate::drawCard(QPainter *painter, const QStyleOptionViewItem painter->drawText(QRect(x, line3Y, previewMaxWidth, previewFont.pointSizeF()), Qt::AlignLeft | Qt::AlignVCenter, elidedPreview); } - // Categories (if enabled and present) - simplified for now - // TODO: Add category support from model + // Category badges (if any) + int mailId = index.data(EmailListModel::IdRole).toInt(); + QVector catIds = CategoryDao::categoriesForMail(mailId); + if (!catIds.isEmpty() && catIds.size() <= 3 && m_config.displayMode != Compact) { + int catY = line2Y + subjectFont.pointSizeF() + (m_config.showPreviewText && m_config.displayMode != Compact + ? previewFont.pointSizeF() + 8 : 6); + int cx = contentRect.left() + (m_config.showAvatar ? m_config.avatarSize + 10 : 0); + QFont catFont = subjectFont; + catFont.setPointSizeF(qMax(6.5, subjectFont.pointSizeF() - 3.0)); + catFont.setBold(true); + painter->setFont(catFont); + int maxCatWidth = availableWidth; + for (qint64 cid : catIds) { + auto cat = CategoryDao::findById(cid); + if (!cat.has_value()) continue; + QString label = " " + cat->name + " "; + QColor catColor = cat->color.isValid() ? cat->color : QColor(0x00, 0x71, 0xe3); + QFontMetrics fm(catFont); + int w = fm.horizontalAdvance(label) + 8; + if (maxCatWidth < w) break; + QRect chip(cx, catY, w, fm.height() + 4); + painter->setRenderHint(QPainter::Antialiasing); + QColor fill = catColor; fill.setAlpha(45); + painter->setPen(catColor); + painter->setBrush(fill); + painter->drawRoundedRect(chip, 4, 4); + painter->setPen(catColor); + painter->drawText(chip.adjusted(4, 2, -2, 0), Qt::AlignLeft | Qt::AlignVCenter, label.trimmed()); + cx += w + 6; + maxCatWidth -= w + 6; + } + } // Action buttons (bottom) int buttonAreaY = cardRect.bottom() - 28; diff --git a/src/ui/maillistview.h b/src/ui/maillistview.h index 1e4f136..25794bb 100644 --- a/src/ui/maillistview.h +++ b/src/ui/maillistview.h @@ -27,6 +27,8 @@ public: ~MailListView() override; void setModel(EmailListModel *model); + /// Currently selected mail ids (multi-selection or current row). + QVector selectedMailIds() const { return m_selectedMailIds; } QTreeView* treeView() const { return m_treeView; } QListView* listView() const { return m_listView; } diff --git a/src/ui/mainmainwindow.cpp b/src/ui/mainmainwindow.cpp index af07dce..d0e1ee1 100644 --- a/src/ui/mainmainwindow.cpp +++ b/src/ui/mainmainwindow.cpp @@ -108,10 +108,6 @@ void MainMainWindow::setupUI() // Page 1: Compose m_composeView = new ComposeView(); connect(m_composeView, &ComposeView::sendRequested, [this](const QString &to, const QString &cc, const QString &bcc, const QString &subject, const QString &body, const QDateTime &scheduleTime, const QString &fromAddr, const QStringList &attachmentPaths) { - if (scheduleTime.isValid()) { - statusBar()->showMessage(tr("Scheduled sending is not available yet"), 5000); - return; - } if (fromAddr.isEmpty()) { statusBar()->showMessage(tr("Select an account before sending"), 5000); return; @@ -124,8 +120,13 @@ void MainMainWindow::setupUI() mail.setSubject(subject); mail.setBodyHtml(body); mail.setDate(QDateTime::currentDateTimeUtc()); - m_mailService->sendMail(mail, fromAddr, attachmentPaths); - statusBar()->showMessage(tr("Sending message…"), 5000); + if (scheduleTime.isValid()) { + m_mailService->scheduleSend(mail, fromAddr, attachmentPaths, scheduleTime); + statusBar()->showMessage(tr("Envío programado"), 5000); + } else { + m_mailService->sendMail(mail, fromAddr, attachmentPaths); + statusBar()->showMessage(tr("Sending message…"), 5000); + } switchToPage(PageMail); }); connect(m_composeView, &ComposeView::discardRequested, [this]() { @@ -305,6 +306,7 @@ void MainMainWindow::setupMailPage() connect(m_emailViewer, &ReaderView::forwardRequested, this, &MainMainWindow::onReaderForwardRequested); connect(m_emailViewer, &ReaderView::deleteRequested, this, &MainMainWindow::onReaderDeleteRequested); connect(m_emailViewer, &ReaderView::markUnreadRequested, this, &MainMainWindow::onMarkUnreadRequested); + connect(m_emailViewer, &ReaderView::markSpamRequested, this, &MainMainWindow::onMarkSpamRequested); connect(m_emailViewer, &ReaderView::detachRequested, this, [this]() { if (m_currentMailId >= 0) { openMailInIndependentWindow(m_currentMailId); @@ -685,6 +687,39 @@ void MainMainWindow::onMarkUnreadRequested(int mailId) } } +void MainMainWindow::onMarkSpamRequested(int mailId) +{ + std::optional optMail = MailItemDao::findById(mailId); + if (!optMail) return; + std::optional curFolder = FolderDao::findById(optMail->folderId()); + if (!curFolder) return; + int accountId = curFolder->accountId(); + + // Find a Spam/Junk folder for this account. + QVector folders = FolderDao::findByAccountId(accountId); + int spamFolderId = -1; + for (const Folder &f : folders) { + QString n = f.name().toLower(); + if (n == "spam" || n == "junk" || n.contains("spam") || n.contains("junk")) { + spamFolderId = f.id(); + break; + } + } + + if (spamFolderId < 0) { + QMessageBox::information(this, tr("Marcar como spam"), + tr("No hay una carpeta de Spam en esta cuenta. Mueve el correo manualmente con «Mover a...».")); + return; + } + + m_mailService->moveMail(QString::number(mailId), QString::number(spamFolderId)); + if (m_currentMailId == mailId) m_currentMailId = -1; + m_emailModel->refresh(); + m_mailListView->treeView()->clearSelection(); + m_viewerStack->setCurrentIndex(0); + statusBar()->showMessage(tr("Marcado como spam y movido a la carpeta de spam"), 3000); +} + void MainMainWindow::onNewMessage() { // Show compose view in the viewer stack m_embeddedComposeView->initializeComposition(); @@ -784,10 +819,21 @@ void MainMainWindow::onRunRulesOnFolder() void MainMainWindow::onRunRulesOnSelected() { - // Get selected mail IDs - QMessageBox::information(this, tr("Ejecutar reglas"), tr("Ejecutando reglas en correos seleccionados...")); - // TODO: Get selected mail IDs from MailListView - statusBar()->showMessage(tr("Reglas ejecutadas"), 3000); + QVector ids = m_mailListView->selectedMailIds(); + if (ids.isEmpty() && m_currentMailId >= 0) { + ids.append(m_currentMailId); + } + if (ids.isEmpty()) { + statusBar()->showMessage(tr("Selecciona al menos un correo"), 3000); + return; + } + + QVector mailIds; + for (int id : ids) mailIds.append(id); + int processed = RulesEngine::runRulesOnMails(mailIds); + statusBar()->showMessage(tr("Reglas ejecutadas en %1 correos").arg(processed), 3000); + m_emailModel->refresh(); + m_categoryTree->refresh(); } void MainMainWindow::showRulesManager() @@ -1197,10 +1243,6 @@ void MainMainWindow::onEmbeddedSendRequested(const QString &to, const QString &c const QString &fromAddress, const QStringList &attachmentPaths) { - if (scheduleTime.isValid()) { - statusBar()->showMessage(tr("Scheduled sending is not available yet"), 5000); - return; - } if (fromAddress.isEmpty()) { statusBar()->showMessage(tr("Select an account before sending"), 5000); return; @@ -1213,8 +1255,13 @@ void MainMainWindow::onEmbeddedSendRequested(const QString &to, const QString &c mail.setSubject(subject); mail.setBodyHtml(body); mail.setDate(QDateTime::currentDateTimeUtc()); - m_mailService->sendMail(mail, fromAddress, attachmentPaths); - statusBar()->showMessage(tr("Sending message…"), 5000); + if (scheduleTime.isValid()) { + m_mailService->scheduleSend(mail, fromAddress, attachmentPaths, scheduleTime); + statusBar()->showMessage(tr("Envío programado"), 5000); + } else { + m_mailService->sendMail(mail, fromAddress, attachmentPaths); + statusBar()->showMessage(tr("Sending message…"), 5000); + } m_viewerStack->setCurrentIndex(0); // placeholder m_embeddedComposeView->initializeComposition(); } diff --git a/src/ui/mainmainwindow.h b/src/ui/mainmainwindow.h index 569f4f1..0ac5727 100644 --- a/src/ui/mainmainwindow.h +++ b/src/ui/mainmainwindow.h @@ -86,6 +86,7 @@ private slots: void onBatchDeleteRequested(const QVector &mailIds); void onBatchMarkUnreadRequested(const QVector &mailIds); void onBatchFlagRequested(const QVector &mailIds, bool flagged); + void onMarkSpamRequested(int mailId); void onMarkUnreadRequested(int mailId); // Online search void onOnlineSearchRequested(const QString& query); diff --git a/src/ui/readerview.cpp b/src/ui/readerview.cpp index 1f48a13..cf2710b 100644 --- a/src/ui/readerview.cpp +++ b/src/ui/readerview.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -370,8 +371,7 @@ void ReaderView::setupUI() { }); 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.")); + emit markSpamRequested(m_currentMailId); }); m_moreButton->setMenu(moreMenu); m_moreButton->setStyleSheet( @@ -729,8 +729,23 @@ void ReaderView::showAttachmentContextMenu(const QPoint &pos, const QString &pat menu.addSeparator(); QAction *deleteAct = menu.addAction("Eliminar adjunto", [this, name]() { - // TODO: Implement attachment deletion from stored mail - QMessageBox::information(this, "No implementado", "Eliminar adjuntos del almacenamiento local aún no está implementado."); + if (m_currentMailId < 0) return; + QVector all = MailItemDao::attachmentsForMail(m_currentMailId); + QVector remaining; + // Delete the matching stored file and keep the others. + for (const StoredAttachmentRecord &a : all) { + if (a.fileName == name) { + if (!a.storedPath.isEmpty()) + QFile::remove(a.storedPath); + } else { + remaining.append(a); + } + } + if (MailItemDao::replaceAttachments(m_currentMailId, remaining)) { + QMessageBox::information(this, "Adjunto", tr("Adjunto «%1» eliminado.").arg(name)); + } else { + QMessageBox::warning(this, "Adjunto", tr("No se pudo eliminar el adjunto.")); + } }); deleteAct->setIcon(QIcon::fromTheme("edit-delete", QApplication::style()->standardIcon(QStyle::SP_TrashIcon))); @@ -776,8 +791,10 @@ void ReaderView::setMailItem(const MailItem* item) { // From m_fromLabel->setText("De: " + formatAddress(sender)); - // To - MailItem doesn't have recipients easily accessible, skip for now - m_toLabel->setText("Para: —"); // TODO: fetch from MailItem or DB + // To - prefer recipient()/to(), fallback to placeholder + QString toText = item->recipient(); + if (toText.isEmpty()) toText = item->to(); + m_toLabel->setText("Para: " + (toText.isEmpty() ? QString("—") : formatAddress(toText))); // Date m_dateLabel->setText("Fecha: " + formatDate(item->date())); diff --git a/src/ui/readerview.h b/src/ui/readerview.h index 19d0665..441e810 100644 --- a/src/ui/readerview.h +++ b/src/ui/readerview.h @@ -30,6 +30,7 @@ signals: void deleteRequested(int mailId); void detachRequested(); void markUnreadRequested(int mailId); + void markSpamRequested(int mailId); void openAttachmentRequested(const QString &path); private slots: diff --git a/src/ui/richtexteditor.cpp b/src/ui/richtexteditor.cpp index 6dadc8c..8c12e32 100644 --- a/src/ui/richtexteditor.cpp +++ b/src/ui/richtexteditor.cpp @@ -68,19 +68,19 @@ void RichTextEditor::setupToolbar(QVBoxLayout *layout) { // Bold / Italic / Underline QAction *boldAct = m_toolbar->addAction("B"); boldAct->setCheckable(true); - boldAct->setIcon(QIcon(QStringLiteral(":/icons/resources/icons/SVG/Bold/Text Formatting/Text Bold.svg"))); + boldAct->setIcon(QIcon(QStringLiteral(":/icons/bold.svg"))); QFont boldFont = boldAct->font(); boldFont.setBold(true); boldAct->setFont(boldFont); connect(boldAct, &QAction::triggered, this, &RichTextEditor::onBold); QAction *italicAct = m_toolbar->addAction("I"); italicAct->setCheckable(true); - italicAct->setIcon(QIcon(QStringLiteral(":/icons/resources/icons/SVG/Outline/Text Formatting/Text Italic.svg"))); + italicAct->setIcon(QIcon(QStringLiteral(":/icons/italic.svg"))); QFont italicFont = italicAct->font(); italicFont.setItalic(true); italicAct->setFont(italicFont); connect(italicAct, &QAction::triggered, this, &RichTextEditor::onItalic); QAction *underlineAct = m_toolbar->addAction("U"); underlineAct->setCheckable(true); - underlineAct->setIcon(QIcon(QStringLiteral(":/icons/resources/icons/SVG/Outline/Text Formatting/Text Underline.svg"))); + underlineAct->setIcon(QIcon(QStringLiteral(":/icons/underline.svg"))); QFont uFont = underlineAct->font(); uFont.setUnderline(true); underlineAct->setFont(uFont); connect(underlineAct, &QAction::triggered, this, &RichTextEditor::onUnderline); diff --git a/src/ui/rulesmanagerdialog.cpp b/src/ui/rulesmanagerdialog.cpp index 47fde63..8b050c9 100644 --- a/src/ui/rulesmanagerdialog.cpp +++ b/src/ui/rulesmanagerdialog.cpp @@ -1,6 +1,7 @@ #include "rulesmanagerdialog.h" #include "ruleditordialog.h" #include "db/dao/ruledao.h" +#include "db/dao/common_structs.h" #include "db/dao/categorydao.h" #include "db/dao/folderdao.h" #include "services/rulesengine.h" @@ -81,7 +82,7 @@ void RulesManagerDialog::setupUI() if (idx > 0) { QTreeWidgetItem* prev = m_tree->takeTopLevelItem(idx); m_tree->insertTopLevelItem(idx - 1, prev); - // TODO: update priority in DB + persistPriorityOrder(); } }); QAction* moveDownAct = menu.addAction(tr("Bajar prioridad"), [this, item]() { @@ -89,7 +90,7 @@ void RulesManagerDialog::setupUI() if (idx < m_tree->topLevelItemCount() - 1) { QTreeWidgetItem* next = m_tree->takeTopLevelItem(idx + 1); m_tree->insertTopLevelItem(idx + 1, item); - // TODO: update priority in DB + persistPriorityOrder(); } }); menu.exec(m_tree->viewport()->mapToGlobal(pos)); @@ -296,3 +297,20 @@ void RulesManagerDialog::refresh() { loadRules(); } + +void RulesManagerDialog::persistPriorityOrder() +{ + // Re-assign priority based on tree order (top = lowest number = highest priority). + for (int i = 0; i < m_tree->topLevelItemCount(); ++i) { + QTreeWidgetItem *item = m_tree->topLevelItem(i); + qint64 ruleId = item->data(0, Qt::UserRole).toLongLong(); + int newPriority = i + 1; + item->setText(2, QString::number(newPriority)); + auto rule = RuleDao::findById(ruleId); + if (rule.has_value()) { + Rule r = rule.value(); + r.priority = newPriority; + RuleDao::update(r); + } + } +} diff --git a/src/ui/rulesmanagerdialog.h b/src/ui/rulesmanagerdialog.h index 7f02873..5fc11b6 100644 --- a/src/ui/rulesmanagerdialog.h +++ b/src/ui/rulesmanagerdialog.h @@ -41,4 +41,5 @@ private: void loadRules(); void addRuleToTree(const Rule& rule); Rule itemToRule(QTreeWidgetItem* item) const; + void persistPriorityOrder(); }; \ No newline at end of file