Files
wino-mail-dtkqt/src/ui/accountsetupdialog.cpp
T

621 lines
22 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "ui/accountsetupdialog.h"
#include <QMessageBox>
#include <QScrollArea>
#include <QFrame>
#include <QDebug>
#include <QRegularExpression>
#include <QRegularExpressionValidator>
#include <QIntValidator>
#include <QTimer>
// ─────────────────────── Constructor ───────────────────────
AccountSetupDialog::AccountSetupDialog(AccountService *accountService, QWidget *parent)
: QDialog(parent)
, m_accountService(accountService)
, m_selectedProvider(0)
, m_accountCreatedOk(false)
, m_editingAccountId(-1)
, m_isEditing(false)
{
m_authTimeoutTimer = new QTimer(this);
m_authTimeoutTimer->setSingleShot(true);
connect(m_authTimeoutTimer, &QTimer::timeout, this, &AccountSetupDialog::onAuthTimeout);
if (m_accountService) {
connect(m_accountService, &AccountService::accountAdded,
this, &AccountSetupDialog::onAccountAdded);
}
setupUI();
}
// ─────────────────────── Public API ───────────────────────
void AccountSetupDialog::loadAccountForEditing(const Account &account)
{
// Only support editing IMAP accounts for simplicity
if (account.type() != AccountType::IMAP) {
QMessageBox::information(this, tr("Edit Account"),
tr("Editing of OAuth (Gmail/Outlook) accounts is not supported in this version.\n"
"You can add a new account instead."));
m_isEditing = false;
return;
}
m_isEditing = true;
m_editingAccountId = account.id();
// Set window title
setWindowTitle(tr("Edit Email Account Wino Mail"));
// Fill IMAP fields
m_imapEmailEdit->setText(account.email());
m_imapNameEdit->setText(account.displayName());
// Password field left blank for security; user must re-enter
m_imapPasswordEdit->clear();
const Account::ConnectionSettings &settings = account.connectionSettings();
m_imapHostEdit->setText(settings.incomingHost);
m_imapPortEdit->setText(QString::number(settings.incomingPort));
m_smtpHostEdit->setText(settings.outgoingHost);
m_smtpPortEdit->setText(QString::number(settings.outgoingPort));
m_sslCheckbox->setChecked(settings.incomingSsl); // assuming same for SMTP
// Switch to IMAP page
m_selectedProvider = 2; // IMAP
goToPage(PageImap);
}
// ─────────────────────── UI Setup ───────────────────────
void AccountSetupDialog::setupUI()
{
setWindowTitle("Añadir Cuenta de Correo — Wino Mail");
setMinimumSize(600, 500);
resize(660, 520);
// ── Global stylesheet ──
setStyleSheet(
"AccountSetupDialog { background: #f5f5f7; }"
"QLabel { color: #1d1d1f; }"
"QLineEdit {"
" background: #fff; border: 1px solid #d1d1d6; border-radius: 6px;"
" padding: 8px 12px; font-size: 13px; color: #1d1d1f;"
"}"
"QLineEdit:focus { border: 2px solid #0071e3; }"
"QRadioButton { font-size: 14px; padding: 6px 0; }"
"QCheckBox { font-size: 13px; }"
"QPushButton {"
" font-weight: 600; font-size: 13px; padding: 8px 20px; border-radius: 6px;"
"}"
);
QVBoxLayout *root = new QVBoxLayout(this);
root->setContentsMargins(24, 24, 24, 18);
root->setSpacing(16);
// ── Stacked pages ──
m_stack = new QStackedWidget(this);
m_stack->addWidget(createProviderPage()); // 0
m_stack->addWidget(createOAuthPage()); // 1
m_stack->addWidget(createImapPage()); // 2
m_stack->addWidget(createProgressPage()); // 3
root->addWidget(m_stack, 1);
// ── Separator ──
QFrame *sep = new QFrame();
sep->setFrameShape(QFrame::HLine);
sep->setStyleSheet("color: #d1d1d6;");
root->addWidget(sep);
// ── Navigation buttons ──
QHBoxLayout *nav = new QHBoxLayout();
m_btnBack = new QPushButton("← Atrás");
m_btnBack->setStyleSheet(
"QPushButton { background: #fff; color: #0071e3; border: 1px solid #0071e3; }"
"QPushButton:hover { background: #e8f0fe; }"
);
m_btnCancel = new QPushButton("Cancelar");
m_btnCancel->setStyleSheet(
"QPushButton { background: #e5e5ea; color: #1d1d1f; border: none; }"
"QPushButton:hover { background: #d1d1d6; }"
);
m_btnNext = new QPushButton("Siguiente →");
m_btnNext->setStyleSheet(
"QPushButton { background: #0071e3; color: white; border: none; }"
"QPushButton:hover { background: #005bb5; }"
"QPushButton:disabled { background: #a0c4ff; color: #e0e0e0; }"
);
nav->addWidget(m_btnBack);
nav->addStretch();
nav->addWidget(m_btnCancel);
nav->addSpacing(8);
nav->addWidget(m_btnNext);
root->addLayout(nav);
connect(m_btnBack, &QPushButton::clicked, this, &AccountSetupDialog::onBackClicked);
connect(m_btnNext, &QPushButton::clicked, this, &AccountSetupDialog::onNextClicked);
connect(m_btnCancel, &QPushButton::clicked, this, &AccountSetupDialog::onCancelClicked);
goToPage(PageProvider);
}
// ─────────────────── Page 0: Provider ───────────────────
QWidget* AccountSetupDialog::createProviderPage()
{
QWidget *page = new QWidget();
QVBoxLayout *lay = new QVBoxLayout(page);
lay->setContentsMargins(10, 10, 10, 10);
lay->setSpacing(20);
// Title
QLabel *title = new QLabel("Añadir nueva cuenta");
title->setStyleSheet("font-size: 22px; font-weight: 700; color: #1d1d1f;");
title->setAlignment(Qt::AlignCenter);
QLabel *subtitle = new QLabel("Selecciona el tipo de cuenta que deseas configurar:");
subtitle->setStyleSheet("font-size: 14px; color: #8e8e93;");
subtitle->setWordWrap(true);
subtitle->setAlignment(Qt::AlignCenter);
lay->addWidget(title);
lay->addWidget(subtitle);
lay->addSpacing(10);
// Provider cards
m_providerGroup = new QButtonGroup(this);
auto makeRadio = [&](const QString &text, const QString &desc, int id) -> QWidget* {
QWidget *card = new QWidget();
card->setStyleSheet(
"QWidget { background: #fff; border: 1px solid #e0e0e5; border-radius: 10px; }"
"QWidget:hover { border-color: #0071e3; }"
);
QHBoxLayout *h = new QHBoxLayout(card);
h->setContentsMargins(16, 12, 16, 12);
QRadioButton *radio = new QRadioButton(text);
radio->setStyleSheet("font-size: 15px; font-weight: 600;");
m_providerGroup->addButton(radio, id);
QLabel *descLabel = new QLabel(desc);
descLabel->setStyleSheet("font-size: 12px; color: #8e8e93;");
descLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
h->addWidget(radio, 1);
h->addWidget(descLabel);
return card;
};
lay->addWidget(makeRadio("🔴 Google / Gmail", "OAuth2 seguro", 0));
lay->addWidget(makeRadio("🔵 Microsoft / Outlook", "OAuth2 seguro", 1));
lay->addWidget(makeRadio("⚙️ IMAP / SMTP", "Servidor personalizado", 2));
m_providerGroup->button(0)->setChecked(true);
connect(m_providerGroup, QOverload<int>::of(&QButtonGroup::idClicked),
this, &AccountSetupDialog::onProviderSelected);
lay->addStretch();
return page;
}
// ─────────────────── Page 1: OAuth ───────────────────
QWidget* AccountSetupDialog::createOAuthPage()
{
QWidget *page = new QWidget();
QVBoxLayout *lay = new QVBoxLayout(page);
lay->setContentsMargins(10, 10, 10, 10);
lay->setSpacing(14);
QLabel *title = new QLabel("Autenticación OAuth2");
title->setStyleSheet("font-size: 18px; font-weight: 700;");
lay->addWidget(title);
QLabel *info = new QLabel(
"Se abrirá tu navegador web para que inicies sesión de forma segura.\\n"
"Wino Mail no almacena tu contraseña, solo el token de acceso autorizado.\\n\\n"
"Pasos:\\n"
" 1. Introduce tu dirección de correo.\\n"
" 2. Pulsa «Autenticar en Navegador».\\n"
" 3. Completa el inicio de sesión en la ventana del navegador.\\n"
" 4. Al terminar, esta ventana se actualizará automáticamente."
);
info->setWordWrap(true);
info->setStyleSheet("font-size: 13px; color: #555; line-height: 1.5;");
lay->addWidget(info);
lay->addSpacing(6);
QLabel *emailLabel = new QLabel("Correo electrónico:");
emailLabel->setStyleSheet("font-weight: 600; font-size: 13px;");
lay->addWidget(emailLabel);
m_oauthEmailEdit = new QLineEdit();
m_oauthEmailEdit->setPlaceholderText("tu-correo@gmail.com");
QRegularExpression rx(R"([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})");
m_oauthEmailEdit->setValidator(new QRegularExpressionValidator(rx, this));
lay->addWidget(m_oauthEmailEdit);
lay->addSpacing(8);
m_btnOAuthStart = new QPushButton("🔑 Autenticar en Navegador");
m_btnOAuthStart->setMinimumHeight(40);
m_btnOAuthStart->setStyleSheet(
"QPushButton { background: #34a853; color: white; border: none; font-size: 14px; font-weight: 700; border-radius: 8px; }"
"QPushButton:hover { background: #2d9249; }"
"QPushButton:disabled { background: #a8d5b8; color: #e0e0e0; }"
);
connect(m_btnOAuthStart, &QPushButton::clicked, this, &AccountSetupDialog::startOAuthAuthentication);
lay->addWidget(m_btnOAuthStart);
m_oauthStatusLabel = new QLabel("Esperando…");
m_oauthStatusLabel->setStyleSheet("font-size: 12px; color: #8e8e93; font-style: italic;");
m_oauthStatusLabel->setAlignment(Qt::AlignCenter);
lay->addWidget(m_oauthStatusLabel);
lay->addStretch();
return page;
}
// ─────────────────── Page 2: IMAP ───────────────────
QWidget* AccountSetupDialog::createImapPage()
{
QWidget *page = new QWidget();
QVBoxLayout *outerLay = new QVBoxLayout(page);
outerLay->setContentsMargins(10, 10, 10, 10);
QLabel *title = new QLabel("Configuración IMAP / SMTP");
title->setStyleSheet("font-size: 18px; font-weight: 700;");
outerLay->addWidget(title);
QLabel *info = new QLabel("Introduce los datos de tu servidor de correo. "
"Consulta a tu proveedor si no conoces los datos del servidor.");
info->setWordWrap(true);
info->setStyleSheet("font-size: 12px; color: #8e8e93; margin-bottom: 8px;");
outerLay->addWidget(info);
// Scrollable form
QScrollArea *scroll = new QScrollArea();
scroll->setWidgetResizable(true);
scroll->setFrameShape(QFrame::NoFrame);
QWidget *formWidget = new QWidget();
QFormLayout *form = new QFormLayout(formWidget);
form->setSpacing(10);
form->setContentsMargins(0, 8, 12, 8);
form->setLabelAlignment(Qt::AlignRight);
m_imapEmailEdit = new QLineEdit();
m_imapEmailEdit->setPlaceholderText("usuario@empresa.com");
m_imapNameEdit = new QLineEdit();
m_imapNameEdit->setPlaceholderText("Nombre a mostrar (ej. Javier)");
m_imapPasswordEdit = new QLineEdit();
m_imapPasswordEdit->setEchoMode(QLineEdit::Password);
m_imapPasswordEdit->setPlaceholderText("Contraseña");
m_imapHostEdit = new QLineEdit();
m_imapHostEdit->setPlaceholderText("imap.empresa.com");
m_imapPortEdit = new QLineEdit();
m_imapPortEdit->setPlaceholderText("993");
m_imapPortEdit->setValidator(new QIntValidator(1, 65535, this));
m_imapPortEdit->setMaximumWidth(100);
m_smtpHostEdit = new QLineEdit();
m_smtpHostEdit->setPlaceholderText("smtp.empresa.com");
m_smtpPortEdit = new QLineEdit();
m_smtpPortEdit->setPlaceholderText("587");
m_smtpPortEdit->setValidator(new QIntValidator(1, 65535, this));
m_smtpPortEdit->setMaximumWidth(100);
m_sslCheckbox = new QCheckBox("Usar conexión segura (SSL/TLS)");
m_sslCheckbox->setChecked(true);
form->addRow("Correo:", m_imapEmailEdit);
form->addRow("Nombre:", m_imapNameEdit);
form->addRow("Contraseña:", m_imapPasswordEdit);
// Visual separator
QFrame *line = new QFrame();
line->setFrameShape(QFrame::HLine);
line->setStyleSheet("color: #e0e0e5;");
form->addRow(line);
QLabel *serverHeader = new QLabel("Servidores");
serverHeader->setStyleSheet("font-weight: 700; font-size: 13px; color: #0071e3;");
form->addRow(serverHeader);
form->addRow("Servidor IMAP:", m_imapHostEdit);
form->addRow("Puerto IMAP:", m_imapPortEdit);
form->addRow("Servidor SMTP:", m_smtpHostEdit);
form->addRow("Puerto SMTP:", m_smtpPortEdit);
form->addRow("", m_sslCheckbox);
scroll->setWidget(formWidget);
outerLay->addWidget(scroll, 1);
return page;
}
// ─────────────────── Page 3: Progress ───────────────────
QWidget* AccountSetupDialog::createProgressPage()
{
QWidget *page = new QWidget();
QVBoxLayout *lay = new QVBoxLayout(page);
lay->setContentsMargins(20, 40, 20, 20);
lay->setAlignment(Qt::AlignCenter);
m_progressIcon = new QLabel("⏳");
m_progressIcon->setStyleSheet("font-size: 56px;");
m_progressIcon->setAlignment(Qt::AlignCenter);
m_progressText = new QLabel("Conectando al servidor…");
m_progressText->setStyleSheet("font-size: 16px; font-weight: 600; color: #1d1d1f;");
m_progressText->setAlignment(Qt::AlignCenter);
m_progressDetail = new QLabel("Validando credenciales y configuración del servidor de correo.");
m_progressDetail->setWordWrap(true);
m_progressDetail->setStyleSheet("font-size: 13px; color: #8e8e93;");
m_progressDetail->setAlignment(Qt::AlignCenter);
lay->addWidget(m_progressIcon);
lay->addSpacing(16);
lay->addWidget(m_progressText);
lay->addSpacing(8);
lay->addWidget(m_progressDetail);
lay->addStretch();
return page;
}
// ─────────────────── Navigation ───────────────────
void AccountSetupDialog::goToPage(int page)
{
m_stack->setCurrentIndex(page);
updateNavButtons();
}
void AccountSetupDialog::updateNavButtons()
{
int page = m_stack->currentIndex();
m_btnBack->setVisible(page > 0 && page != PageProgress);
m_btnCancel->setVisible(page != PageProgress || !m_accountCreatedOk);
switch (page) {
case PageProvider:
m_btnNext->setText("Siguiente →");
m_btnNext->setEnabled(true);
m_btnNext->setVisible(true);
break;
case PageOAuth:
m_btnNext->setVisible(false); // OAuth flow is driven by the authenticate button
break;
case PageImap:
m_btnNext->setText(m_isEditing ? "Guardar cambios" : "Conectar");
m_btnNext->setEnabled(true);
m_btnNext->setVisible(true);
break;
case PageProgress:
if (m_accountCreatedOk) {
m_btnNext->setText("Finalizar ✓");
m_btnNext->setEnabled(true);
m_btnNext->setVisible(true);
m_btnBack->setVisible(false);
m_btnCancel->setVisible(false);
} else {
m_btnNext->setVisible(false);
}
break;
}
}
void AccountSetupDialog::onProviderSelected(int id)
{
m_selectedProvider = id;
}
void AccountSetupDialog::onNextClicked()
{
int page = m_stack->currentIndex();
if (page == PageProvider) {
if (m_selectedProvider == 2) {
goToPage(PageImap);
} else {
// Update OAuth page subtitle based on provider
goToPage(PageOAuth);
}
}
else if (page == PageImap) {
submitImapAccount();
}
else if (page == PageProgress) {
accept(); // Finalizar
}
}
void AccountSetupDialog::onBackClicked()
{
int page = m_stack->currentIndex();
if (page == PageOAuth || page == PageImap) {
m_authTimeoutTimer->stop();
goToPage(PageProvider);
}
}
void AccountSetupDialog::onCancelClicked()
{
m_authTimeoutTimer->stop();
reject();
}
// ─────────────────── OAuth Flow ───────────────────
void AccountSetupDialog::startOAuthAuthentication()
{
QString email = m_oauthEmailEdit->text().trimmed();
if (email.isEmpty()) {
QMessageBox::warning(this, "Validación",
"Introduce tu dirección de correo electrónico.");
return;
}
m_btnOAuthStart->setEnabled(false);
m_oauthStatusLabel->setText("Abriendo navegador… Completa el inicio de sesión allí.");
m_oauthStatusLabel->setStyleSheet("font-size: 12px; color: #0071e3; font-weight: 600;");
QString provider = (m_selectedProvider == 0) ? "gmail" : "outlook";
// Start authentication via AccountService (opens browser)
m_accountService->startAuthentication(email, provider);
// Start timeout timer (120 seconds to complete OAuth)
m_authTimeoutTimer->start(120000);
qDebug() << "[AccountSetupDialog] OAuth started for" << email << "provider:" << provider;
}
void AccountSetupDialog::onAuthTimeout()
{
if (m_stack->currentIndex() == PageOAuth) {
m_btnOAuthStart->setEnabled(true);
m_oauthStatusLabel->setText("⚠️ Tiempo de espera agotado. Inténtalo de nuevo.");
m_oauthStatusLabel->setStyleSheet("font-size: 12px; color: #ff3b30; font-weight: 600;");
}
}
// ─────────────────── IMAP Flow ───────────────────
void AccountSetupDialog::submitImapAccount()
{
QString email = m_imapEmailEdit->text().trimmed();
QString name = m_imapNameEdit->text().trimmed();
QString password = m_imapPasswordEdit->text().trimmed();
QString imapHost = m_imapHostEdit->text().trimmed();
QString imapPort = m_imapPortEdit->text().trimmed();
QString smtpHost = m_smtpHostEdit->text().trimmed();
QString smtpPort = m_smtpPortEdit->text().trimmed();
// Validation
if (email.isEmpty()) {
QMessageBox::warning(this, "Campo requerido", "Introduce tu dirección de correo.");
return;
}
if (password.isEmpty()) {
QMessageBox::warning(this, "Campo requerido", "Introduce tu contraseña.");
return;
}
if (imapHost.isEmpty()) {
QMessageBox::warning(this, "Campo requerido", "Introduce el servidor IMAP.");
return;
}
// Use defaults if ports are empty
if (imapPort.isEmpty()) imapPort = m_sslCheckbox->isChecked() ? "993" : "143";
if (smtpPort.isEmpty()) smtpPort = m_sslCheckbox->isChecked() ? "465" : "587";
if (smtpHost.isEmpty()) smtpHost = imapHost.replace("imap.", "smtp.");
if (name.isEmpty()) name = email.split("@").first();
// Create account with connection settings
Account account;
account.setEmail(email);
account.setDisplayName(name);
account.setType(AccountType::IMAP);
Account::ConnectionSettings settings;
settings.type = "imap";
settings.incomingHost = imapHost;
settings.incomingPort = imapPort.toInt();
settings.incomingSsl = m_sslCheckbox->isChecked();
settings.outgoingHost = smtpHost;
settings.outgoingPort = smtpPort.toInt();
settings.outgoingSsl = m_sslCheckbox->isChecked();
settings.username = email;
settings.password = password;
settings.authMethod = "plain";
account.setConnectionSettings(settings);
// Show progress
m_accountCreatedOk = false;
goToPage(PageProgress);
m_progressIcon->setText("⏳");
m_progressText->setText("Conectando al servidor...");
m_progressDetail->setText(
QString("Servidor IMAP: %1:%2\\nServidor SMTP: %3:%4\\nSSL: %5")
.arg(imapHost, imapPort, smtpHost, smtpPort,
m_sslCheckbox->isChecked() ? "Sí" : "No")
);
// Disable UI during connection attempt
m_btnBack->setEnabled(false);
m_btnNext->setEnabled(false);
m_btnCancel->setEnabled(false);
QString errorMsg;
bool success = m_accountService->testConnection(account.connectionSettings(), errorMsg);
if (success) {
if (m_isEditing) {
account.setId(m_editingAccountId);
m_accountService->updateAccount(account);
} else {
m_accountService->addAccount(account);
}
m_btnCancel->setEnabled(true);
} else {
QMessageBox::warning(this, tr("Connection failed"), tr("Could not connect to IMAP server: %1").arg(errorMsg));
// Re-enable UI
m_btnBack->setEnabled(true);
m_btnNext->setEnabled(true);
m_btnCancel->setEnabled(true);
}
}
// ─────────────────── Account Result ───────────────────
void AccountSetupDialog::onAccountAdded(const Account &account)
{
m_authTimeoutTimer->stop();
m_accountCreatedOk = true;
qDebug() << "[AccountSetupDialog] Account" << (m_isEditing ? "updated" : "created") << "successfully:" << account.email();
// If we were on the OAuth page, move to progress first
if (m_stack->currentIndex() == PageOAuth) {
goToPage(PageProgress);
}
showSuccess(QString(m_isEditing ? "¡Cuenta «%1» actualizada con éxito!\\n\\n"
"Los cambios se aplicarán inmediatamente."
: "¡Cuenta «%1» configurada con éxito!\\n\\n"
"La sincronización de correos comenzará automáticamente en segundo plano.")
.arg(account.email()));
emit accountCreated(account);
}
void AccountSetupDialog::showSuccess(const QString &message)
{
m_progressIcon->setText("✅");
m_progressText->setText("¡Cuenta añadida!");
m_progressDetail->setText(message);
updateNavButtons();
}
void AccountSetupDialog::showError(const QString &message)
{
m_accountCreatedOk = false;
m_progressIcon->setText("❌");
m_progressText->setText("Error de conexión");
m_progressDetail->setText(message);
// Allow going back
m_btnBack->setVisible(true);
m_btnCancel->setVisible(true);
m_btnNext->setVisible(false);
}
#include "accountsetupdialog.moc"