From 2a3a1a0470c4a8a8f5107902db1df735031a9b34 Mon Sep 17 00:00:00 2001 From: Javier Date: Thu, 18 Jun 2026 18:58:27 +0200 Subject: [PATCH] Baseline for wino-mail-dtkqt UI migration --- build/wino-mail.sqlite | Bin 0 -> 28672 bytes src/core/authenticator.cpp | 40 +++++++++++++++++++----------- src/core/gmailauthenticator.cpp | 37 +++++---------------------- src/core/outlookauthenticator.cpp | 35 ++++---------------------- src/ui/maillistview.cpp | 6 +++++ src/ui/maillistview.h | 1 + src/ui/mainmainwindow.cpp | 27 ++++++++++++++++++++ src/ui/mainmainwindow.h | 1 + src/ui/readerview.cpp | 8 +++++- src/ui/readerview.h | 2 ++ 10 files changed, 80 insertions(+), 77 deletions(-) create mode 100644 build/wino-mail.sqlite diff --git a/build/wino-mail.sqlite b/build/wino-mail.sqlite new file mode 100644 index 0000000000000000000000000000000000000000..37758a3a812366a74e47efa4b72b21cec1f83345 GIT binary patch literal 28672 zcmeI(O>f#T7zgmAr7(43>t#Y!h2-9f%HFigj!UVjqCsdONb9L`!GO0wAP!pTt)2P} zciR`($C&d*po^wnc0vD00ly@1p5J3fiV&TPhVVHVxZargALob5KYr^YA>ig z*@AkoC!3xW{_&mD-rUZLMWN+)e`_yazxY+0J^xw!s4CbX009U<00Izz00bcL9|i72 zzEC4p2*p%4DNrRHKog9fiF)f>((Kb16n@yvANj}p{ zVs!16X{ocC)UvB&%!E^OrwcPZ+G^Rv>NXlxG7t`*urVh#{Sq82#@t>^ld2TAJQ<=& zdhWzmcc!ir9b9ju6VpBrb5LU*ZWMp zi=3(?*k)6m>2cO4C#_b48WuUFb)(y`$vcJVu;Gwrc8!=^$y~;{>(oJ7GM$q!*OcpF z?4j}7oqVZQ)-|D`xsfwRy+mEHx$lPcs|5AxeNxJ+x&17d-}l4qLTP_rf2f4Xi7pv^ z=GGDuwLLX0tk-kr;hafkYC89p^Nyr#Q|2l5XpAL*kmlsty~!dMvJ<2!160UUj}3g8 z-Di7DjO2bks_M+j-ML;U9USQQ-=l{}G*~?5*4j1V#;2x_kJSPW7r{!7 z`a(_}wy@I2h95jK{!Xy6P)pBtzwya(VGTSkN2^VK&`ocqLUk-nE5%?=?gby%AOHaf zKmY;|fB*y_009U<00I!$Q~|vIZ)#={7X%;x0SG_<0uX=z1Rwwb2tXhe!25q{ff58D z009U<00Izz00bZa0SG`~Qw8w;zp0r;To8Z&1Rwwb2tWV=5P$##AOL|>U^gh@|Nkin zN)Uhm1Rwwb2tWV=5P$##AOL|46u|HQ8<post(request, params.toString(QUrl::FullyEncoded).toUtf8()); - connect(reply, &QNetworkReply::finished, this, [this, reply]() { - onTokenReply(reply); - }); + // Exchange the authorization code for tokens using base class implementation + exchangeAuthorizationCode(code, m_redirectUri); } void GmailAuthenticator::refreshToken(const Account &account) @@ -94,23 +83,9 @@ void GmailAuthenticator::refreshToken(const Account &account) QNetworkRequest request = createTokenRequest(QUrl(m_tokenEndpoint)); QNetworkReply *reply = m_networkManager->post(request, params.toString(QUrl::FullyEncoded).toUtf8()); connect(reply, &QNetworkReply::finished, this, [this, reply]() { - onTokenReply(reply); + handleTokenResponse(reply->readAll(), m_email, "gmail"); + reply->deleteLater(); }); } -void GmailAuthenticator::onTokenReply(QNetworkReply *reply) -{ - if (reply->error() == QNetworkReply::NoError) { - QByteArray data = reply->readAll(); - handleTokenResponse(data, m_email, "gmail"); - } else { - QByteArray responseData = reply->readAll(); - qWarning() << "[GmailAuthenticator] Token request failed:" - << reply->errorString() - << "Response:" << QString::fromUtf8(responseData); - emit authenticationFailed("Gmail token request failed: " + reply->errorString()); - } - reply->deleteLater(); -} - -#include "gmailauthenticator.moc" \ No newline at end of file +#include "gmailauthenticator.moc" diff --git a/src/core/outlookauthenticator.cpp b/src/core/outlookauthenticator.cpp index 56c9349..5394872 100644 --- a/src/core/outlookauthenticator.cpp +++ b/src/core/outlookauthenticator.cpp @@ -24,7 +24,7 @@ OutlookAuthenticator::OutlookAuthenticator(QObject *parent) connect(m_callbackServer, &OAuthCallbackServer::codeReceived, this, &OutlookAuthenticator::onAuthCodeReceived); connect(m_callbackServer, &OAuthCallbackServer::errorOccurred, - this, [this](const QString &error) { + [this](const QString &error) { emit authenticationFailed("OAuth callback error: " + error); }); } @@ -58,18 +58,7 @@ void OutlookAuthenticator::onAuthCodeReceived(const QString &code, const QString m_callbackServer->stop(); - QUrlQuery params; - params.addQueryItem("code", code); - params.addQueryItem("client_id", m_clientId); - params.addQueryItem("client_secret", m_clientSecret); - params.addQueryItem("redirect_uri", m_redirectUri); - params.addQueryItem("grant_type", "authorization_code"); - - QNetworkRequest request = createTokenRequest(QUrl(m_tokenEndpoint)); - QNetworkReply *reply = m_networkManager->post(request, params.toString(QUrl::FullyEncoded).toUtf8()); - connect(reply, &QNetworkReply::finished, this, [this, reply]() { - onTokenReply(reply); - }); + exchangeAuthorizationCode(code, m_redirectUri); } void OutlookAuthenticator::refreshToken(const Account &account) @@ -86,23 +75,9 @@ void OutlookAuthenticator::refreshToken(const Account &account) QNetworkRequest request = createTokenRequest(QUrl(m_tokenEndpoint)); QNetworkReply *reply = m_networkManager->post(request, params.toString(QUrl::FullyEncoded).toUtf8()); connect(reply, &QNetworkReply::finished, this, [this, reply]() { - onTokenReply(reply); + handleTokenResponse(reply->readAll(), m_email, "outlook"); + reply->deleteLater(); }); } -void OutlookAuthenticator::onTokenReply(QNetworkReply *reply) -{ - if (reply->error() == QNetworkReply::NoError) { - QByteArray data = reply->readAll(); - handleTokenResponse(data, m_email, "outlook"); - } else { - QByteArray responseData = reply->readAll(); - qWarning() << "[OutlookAuthenticator] Token request failed:" - << reply->errorString() - << "Response:" << QString::fromUtf8(responseData); - emit authenticationFailed("Outlook token request failed: " + reply->errorString()); - } - reply->deleteLater(); -} - -#include "outlookauthenticator.moc" \ No newline at end of file +#include "outlookauthenticator.moc" diff --git a/src/ui/maillistview.cpp b/src/ui/maillistview.cpp index b57b101..c9dbc4b 100644 --- a/src/ui/maillistview.cpp +++ b/src/ui/maillistview.cpp @@ -59,6 +59,12 @@ void MailListView::setupUI() { m_tableView->setModel(m_proxyModel); connect(m_tableView, &QTableView::clicked, this, &MailListView::onRowSelected); + connect(m_tableView, &QTableView::doubleClicked, this, [this](const QModelIndex &index) { + if (!index.isValid()) return; + QModelIndex sourceIndex = m_proxyModel->mapToSource(index); + int mailId = sourceIndex.data(EmailListModel::IdRole).toInt(); + emit emailOpenRequested(mailId); + }); layout->addWidget(m_tableView); } diff --git a/src/ui/maillistview.h b/src/ui/maillistview.h index 815038f..9bf7b1f 100644 --- a/src/ui/maillistview.h +++ b/src/ui/maillistview.h @@ -21,6 +21,7 @@ public: signals: void emailSelected(int mailId); + void emailOpenRequested(int mailId); void composeRequested(); private slots: diff --git a/src/ui/mainmainwindow.cpp b/src/ui/mainmainwindow.cpp index 23810f3..fa089c1 100644 --- a/src/ui/mainmainwindow.cpp +++ b/src/ui/mainmainwindow.cpp @@ -164,12 +164,21 @@ void MainMainWindow::setupMailPage() { m_mailListView = new MailListView(); connect(m_mailListView, &MailListView::emailSelected, this, &MainMainWindow::onEmailSelected); connect(m_mailListView, &MailListView::composeRequested, this, &MainMainWindow::onComposeRequested); + connect(m_mailListView, &MailListView::emailOpenRequested, this, [this](int mailId) { + openMailInIndependentWindow(mailId); + }); m_folderSplitter->addWidget(m_mailListView); // Reader m_emailViewer = new ReaderView(); m_emailViewer->setMinimumWidth(350); connect(m_emailViewer, &ReaderView::replyRequested, this, &MainMainWindow::onReaderReplyRequested); + connect(m_emailViewer, &ReaderView::detachRequested, this, [this]() { + std::optional currentItem = MailItemDao::findById(m_emailModel->getCurrentMailId()); + if (currentItem) { + openMailInIndependentWindow(currentItem->id()); + } + }); m_folderSplitter->addWidget(m_emailViewer); // Default sizes: folder 240, list 380, reader flex @@ -248,6 +257,24 @@ void MainMainWindow::onNewMessage() { switchToPage(PageCompose); } +void MainMainWindow::openMailInIndependentWindow(int mailId) { + std::optional item = MailItemDao::findById(mailId); + if (!item.has_value()) return; + + QMainWindow *detachedWin = new QMainWindow(); + detachedWin->setWindowTitle(QString("Mail - %1").arg(item->subject())); + + ReaderView *detachedReader = new ReaderView(); + detachedReader->setMailItem(&item.value()); + + detachedWin->setCentralWidget(detachedReader); + detachedWin->resize(800, 600); + detachedWin->setAttribute(Qt::WA_DeleteOnClose); + detachedWin->show(); + + statusBar()->showMessage("Opened mail in independent window", 3000); +} + void MainMainWindow::createToolBar() { m_toolBar = addToolBar("Main Toolbar"); m_toolBar->setMovable(false); diff --git a/src/ui/mainmainwindow.h b/src/ui/mainmainwindow.h index 72bdcf8..89ced9c 100644 --- a/src/ui/mainmainwindow.h +++ b/src/ui/mainmainwindow.h @@ -34,6 +34,7 @@ private slots: void onComposeRequested(); void onReaderReplyRequested(const MailItem *item); void onNewMessage(); + void openMailInIndependentWindow(int mailId); private: void setupUI(); diff --git a/src/ui/readerview.cpp b/src/ui/readerview.cpp index 99a1636..656e922 100644 --- a/src/ui/readerview.cpp +++ b/src/ui/readerview.cpp @@ -40,9 +40,13 @@ void ReaderView::setupUI() { m_forwardButton = new QPushButton("Forward"); m_deleteButton = new QPushButton("Delete"); m_deleteButton->setStyleSheet("color: red;"); - + + QPushButton *m_detachButton = new QPushButton("独立 (Independent)"); + m_detachButton->setToolTip("Open in separate window"); + actionsLayout->addWidget(m_replyButton); actionsLayout->addWidget(m_forwardButton); + actionsLayout->addWidget(m_detachButton); actionsLayout->addStretch(); actionsLayout->addWidget(m_deleteButton); @@ -59,6 +63,8 @@ void ReaderView::setupUI() { connect(m_replyButton, &QPushButton::clicked, [this]() { if (m_bodyViewer->toPlainText().isEmpty()) return; }); + + connect(m_detachButton, &QPushButton::clicked, this, &ReaderView::detachRequested); } void ReaderView::setMailItem(const MailItem* item) { diff --git a/src/ui/readerview.h b/src/ui/readerview.h index 19eb03f..48749b9 100644 --- a/src/ui/readerview.h +++ b/src/ui/readerview.h @@ -21,6 +21,8 @@ signals: void replyRequested(const MailItem* item); void forwardRequested(const MailItem* item); void deleteRequested(const MailItem* item); + void detachRequested(); + private: void setupUI();