Branch data Line data Source code
1 : : #include "SetupWizard.h"
2 : :
3 : : #include <QLabel>
4 : : #include <QLoggingCategory>
5 : : #include <QMessageBox>
6 : : #include <QProgressBar>
7 : : #include <QPushButton>
8 : : #include <QScrollArea>
9 : : #include <QTimer>
10 : : #include <QVBoxLayout>
11 : :
12 : : #include "data/AccountConfig.h"
13 : : #include "service/ImapService.h"
14 : : #include "ui/AccountFormWidget.h"
15 : : #include "ui/ThemeManager.h"
16 : : #include <QEvent>
17 : :
18 [ + - + - : 1 : Q_LOGGING_CATEGORY(lcWizard, "mailjd.wizard")
+ - - - ]
19 : :
20 : : // --- SetupWizard ---
21 : :
22 : 10 : SetupWizard::SetupWizard(QWidget *parent)
23 [ + - + - ]: 10 : : QWizard(parent), m_configDir(AccountConfigLoader::defaultConfigDir()) {
24 [ + - + - ]: 10 : setWindowTitle(tr("MailJD – Initial Setup"));
25 [ + - ]: 10 : setMinimumSize(650, 700);
26 [ + - ]: 10 : setWizardStyle(QWizard::ModernStyle);
27 : :
28 [ + - + - : 10 : addPage(new WelcomePage(this));
+ - - + -
- ]
29 [ + - + - : 10 : addPage(new AccountPage(this));
+ - - + -
- ]
30 [ + - + - : 10 : addPage(new TestPage(this));
+ - - + -
- ]
31 : :
32 : : // On finish: save the account
33 [ + - ]: 10 : connect(this, &QWizard::accepted, this, [this]() {
34 [ + - ]: 2 : auto acc = configuredAccount();
35 [ + + ]: 2 : if (!acc.name.isEmpty()) {
36 [ + - ]: 1 : AccountConfigLoader::save(acc, m_configDir);
37 [ + - + - : 2 : qCInfo(lcWizard) << "Wizard completed, saved account:" << acc.name;
+ - + - +
+ ]
38 : : }
39 : 2 : });
40 : 10 : }
41 : :
42 : 6 : void SetupWizard::setConfigDir(const QString &dir) { m_configDir = dir; }
43 : :
44 : 3 : AccountConfig SetupWizard::configuredAccount() const {
45 : 3 : auto *page = qobject_cast<AccountPage *>(this->page(1));
46 [ + - ]: 3 : if (page) {
47 : 3 : return page->form()->config();
48 : : }
49 : 0 : return {};
50 [ # # # # : 0 : }
# # # # #
# ]
51 : :
52 : : // --- WelcomePage ---
53 : :
54 : 10 : WelcomePage::WelcomePage(QWidget *parent) : QWizardPage(parent) {
55 [ + - + - ]: 10 : setTitle(tr("Welcome to MailJD"));
56 [ + - + - ]: 10 : setSubTitle(tr("Set up your first email account now."));
57 : :
58 [ + - + - : 10 : auto *layout = new QVBoxLayout(this);
- + - - ]
59 : :
60 : : auto *label = new QLabel(
61 : : "<p>MailJD ist ein schneller, tastaturgesteuerter Mail-Client für "
62 : : "Linux.</p>"
63 : : "<p>Im nächsten Schritt kannst du deinen IMAP/SMTP-Server konfigurieren. "
64 : : "Du brauchst folgende Informationen:</p>"
65 : : "<ul>"
66 : : "<li>IMAP-Server und Port (z.B. imap.example.com:993)</li>"
67 : : "<li>SMTP-Server und Port (z.B. smtp.example.com:587)</li>"
68 : : "<li>Dein Benutzername und Passwort</li>"
69 : : "</ul>",
70 [ + - + - : 10 : this);
+ - - + -
- ]
71 [ + - ]: 10 : label->setWordWrap(true);
72 [ + - ]: 10 : layout->addWidget(label);
73 [ + - ]: 10 : layout->addStretch();
74 : 10 : }
75 : :
76 : : // --- AccountPage ---
77 : :
78 : 10 : AccountPage::AccountPage(QWidget *parent) : QWizardPage(parent) {
79 [ + - + - ]: 10 : setTitle(tr("Account Setup"));
80 [ + - + - ]: 10 : setSubTitle(tr("Enter your email account server details."));
81 : :
82 [ + - + - : 10 : auto *layout = new QVBoxLayout(this);
- + - - ]
83 : :
84 [ + - + - : 10 : auto *scrollArea = new QScrollArea(this);
- + - - ]
85 [ + - ]: 10 : scrollArea->setWidgetResizable(true);
86 [ + - ]: 10 : scrollArea->setFrameShape(QFrame::NoFrame);
87 : :
88 [ + - + - : 10 : m_form = new AccountFormWidget(this);
- + - - ]
89 [ + - ]: 10 : scrollArea->setWidget(m_form);
90 : :
91 [ + - ]: 10 : layout->addWidget(scrollArea);
92 : 10 : }
93 : :
94 : 2 : bool AccountPage::validatePage() {
95 [ + - ]: 2 : auto errors = m_form->validate();
96 [ + + ]: 2 : if (!errors.isEmpty()) {
97 [ + - + - ]: 1 : QMessageBox::warning(this, tr("Missing Information"),
98 : 1 : "Bitte korrigiere folgende Fehler:\n\n• " +
99 [ + - + - : 2 : errors.join("\n• "));
+ - ]
100 : 1 : return false;
101 : : }
102 : 1 : return true;
103 : 2 : }
104 : :
105 : : // --- TestPage ---
106 : :
107 : 10 : TestPage::TestPage(QWidget *parent) : QWizardPage(parent) {
108 [ + - + - ]: 10 : setTitle(tr("Connection Test"));
109 [ + - + - ]: 10 : setSubTitle(tr("Your IMAP server connection is being tested."));
110 : :
111 [ + - + - : 10 : auto *layout = new QVBoxLayout(this);
- + - - ]
112 : :
113 [ + - + - : 10 : m_summaryLabel = new QLabel(this);
- + - - ]
114 [ + - ]: 10 : m_summaryLabel->setWordWrap(true);
115 [ + - ]: 10 : layout->addWidget(m_summaryLabel);
116 : :
117 [ + - + - : 10 : m_progressBar = new QProgressBar(this);
- + - - ]
118 [ + - ]: 10 : m_progressBar->setRange(0, 0); // Indeterminate
119 [ + - ]: 10 : layout->addWidget(m_progressBar);
120 : :
121 [ + - + - : 10 : m_statusLabel = new QLabel(tr("Testing connection..."), this);
+ - - + -
- ]
122 [ + - ]: 10 : m_statusLabel->setWordWrap(true);
123 [ + - ]: 10 : layout->addWidget(m_statusLabel);
124 : :
125 [ + - + - : 10 : m_retryButton = new QPushButton("Erneut testen", this);
+ - - + -
- ]
126 [ + - ]: 10 : m_retryButton->setVisible(false);
127 [ + - ]: 10 : connect(m_retryButton, &QPushButton::clicked, this, &TestPage::runTest);
128 [ + - ]: 10 : layout->addWidget(m_retryButton);
129 : :
130 [ + - ]: 10 : layout->addStretch();
131 : 10 : }
132 : :
133 : 2 : void TestPage::initializePage() {
134 : : // Get account info from the previous page
135 [ + - + - : 2 : auto *accountPage = qobject_cast<AccountPage *>(wizard()->page(1));
+ - ]
136 [ - + ]: 2 : if (!accountPage)
137 : 0 : return;
138 : :
139 [ + - ]: 2 : auto cfg = accountPage->form()->config();
140 [ + - ]: 2 : m_summaryLabel->setText(QString("<b>Account:</b> %1<br>"
141 : : "<b>E-Mail:</b> %2<br>"
142 [ + - ]: 2 : "<b>IMAP:</b> %3:%4 (%5)")
143 [ + - ]: 4 : .arg(cfg.name, cfg.email, cfg.imap.host,
144 [ + - ]: 4 : QString::number(cfg.imap.port),
145 [ + - ]: 4 : cfg.imap.security.toUpper()));
146 : :
147 : 2 : m_testComplete = false;
148 [ + - ]: 2 : emit completeChanged();
149 : :
150 [ + - ]: 2 : runTest();
151 : 2 : }
152 : :
153 : 9 : bool TestPage::isComplete() const {
154 : : // Always allow "Finish" – even if the test failed.
155 : : // The user may want to save the config and fix connection later.
156 : 9 : return m_testComplete;
157 : : }
158 : :
159 : 2 : void TestPage::runTest() {
160 [ + - ]: 2 : m_progressBar->setVisible(true);
161 [ + - ]: 2 : m_progressBar->setRange(0, 0);
162 [ + - + - ]: 2 : m_statusLabel->setText("Verbindung wird getestet...");
163 [ + - + - ]: 2 : m_statusLabel->setStyleSheet("");
164 [ + - ]: 2 : m_retryButton->setVisible(false);
165 : :
166 : : // Clean up previous
167 [ - + ]: 2 : if (m_testService) {
168 [ # # ]: 0 : m_testService->disconnect();
169 [ # # ]: 0 : m_testService->deleteLater();
170 : : }
171 : :
172 [ + - + - : 2 : m_testService = new ImapService(this);
- + - - ]
173 : :
174 [ + - + - : 2 : auto *accountPage = qobject_cast<AccountPage *>(wizard()->page(1));
+ - ]
175 [ - + ]: 2 : if (!accountPage)
176 : 0 : return;
177 : :
178 [ + - ]: 2 : auto cfg = accountPage->form()->config();
179 : :
180 : 2 : connect(m_testService, &ImapService::stateChanged, this,
181 [ + - ]: 2 : [this](ImapService::State state) {
182 [ - + ]: 10 : if (state == ImapService::State::Authenticated) {
183 : 0 : onTestSuccess();
184 : : }
185 : 10 : });
186 : :
187 : 2 : connect(m_testService, &ImapService::errorOccurred, this,
188 [ + - ]: 4 : [this](const QString &error) { onTestFailed(error); });
189 : :
190 : : // Timeout: 10s
191 [ + - ]: 2 : QTimer::singleShot(10000, this, [this]() {
192 [ # # # # ]: 0 : if (m_testService && !m_testComplete) {
193 [ # # # # ]: 0 : onTestFailed("Zeitüberschreitung (10s)");
194 : : }
195 : 0 : });
196 : :
197 [ + - ]: 2 : m_testService->connectToServer(cfg.imap);
198 : 2 : }
199 : :
200 : 0 : void TestPage::onTestSuccess() {
201 : 0 : m_progressBar->setVisible(false);
202 [ # # # # ]: 0 : m_statusLabel->setText(
203 : : "✅ Verbindung erfolgreich! Der Account kann gespeichert werden.");
204 : : // Sprint 69: @success token instead of CSS named color.
205 [ # # ]: 0 : m_statusLabel->setStyleSheet(
206 : 0 : QStringLiteral("color: %1; font-weight: bold;")
207 [ # # # # : 0 : .arg(ThemeManager::instance().color(QLatin1String("@success"))));
# # # # ]
208 : 0 : m_retryButton->setVisible(false);
209 : :
210 : 0 : m_testComplete = true;
211 : 0 : emit completeChanged();
212 : :
213 [ # # ]: 0 : if (m_testService) {
214 : 0 : m_testService->disconnect();
215 : 0 : m_testService->deleteLater();
216 : 0 : m_testService = nullptr;
217 : : }
218 : 0 : }
219 : :
220 : 2 : void TestPage::onTestFailed(const QString &error) {
221 : 2 : m_progressBar->setVisible(false);
222 [ + - + - ]: 2 : m_statusLabel->setText("❌ Verbindungstest fehlgeschlagen: " + error);
223 : : // Sprint 69: @danger token instead of CSS named color.
224 [ + - ]: 4 : m_statusLabel->setStyleSheet(
225 : 4 : QStringLiteral("color: %1;")
226 [ + - + - : 4 : .arg(ThemeManager::instance().color(QLatin1String("@danger"))));
+ - + - ]
227 : 2 : m_retryButton->setVisible(true);
228 : :
229 : : // Allow finish even on failure
230 : 2 : m_testComplete = true;
231 : 2 : emit completeChanged();
232 : :
233 [ + - ]: 2 : if (m_testService) {
234 : 2 : m_testService->disconnect();
235 : 2 : m_testService->deleteLater();
236 : 2 : m_testService = nullptr;
237 : : }
238 : 2 : }
239 : :
240 : : // T-304: Runtime language switching
241 : 19 : void SetupWizard::changeEvent(QEvent *event) {
242 [ + + ]: 19 : if (event->type() == QEvent::LanguageChange)
243 : 1 : retranslateUi();
244 : 19 : QWizard::changeEvent(event);
245 : 19 : }
246 : :
247 : 1 : void SetupWizard::retranslateUi() {
248 [ + - + - ]: 1 : setWindowTitle(tr("MailJD \u2013 Initial Setup"));
249 : 1 : }
|