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