#include "ui/accountsetupdialog.h" #include #include #include #include #include #include #include #include // ─────────────────────── 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::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"