Flow2: Refactor UI into Flow2AuthWidget only and improve Flow2Auth
authorMichael Schuster <michael@schuster.ms>
Mon, 23 Dec 2019 05:51:46 +0000 (06:51 +0100)
committerMichael Schuster <48932272+misch7@users.noreply.github.com>
Tue, 24 Dec 2019 06:46:57 +0000 (07:46 +0100)
- Flow2AuthCredsPage:
  - Remove .ui file and embed Flow2AuthWidget into layout

- Flow2AuthWidget:
  - Make use generic for Flow2AuthCredsPage and WebFlowCredentialsDialog
  - Fix _errorLabel to render HTML tags instead of dumping them as plain text

- Flow2Auth:
  - Explicitly start auth with startAuth(account) instead of using constructor
  - Take control of copying the auth link to clipboard
  - Request a new auth link on copying, to avoid expiry invalidation
  - Use signals statusChanged() and result() to be more verbose (status, errors)
  - Change timer invocation and add safety bool's to avoid weird behaviour when
    the user triggers multiple link-copy calls (fetchNewToken)

Signed-off-by: Michael Schuster <michael@schuster.ms>
src/gui/CMakeLists.txt
src/gui/creds/flow2auth.cpp
src/gui/creds/flow2auth.h
src/gui/creds/webflowcredentialsdialog.cpp
src/gui/creds/webflowcredentialsdialog.h
src/gui/wizard/flow2authcredspage.cpp
src/gui/wizard/flow2authcredspage.h
src/gui/wizard/flow2authcredspage.ui [deleted file]
src/gui/wizard/flow2authwidget.cpp
src/gui/wizard/flow2authwidget.h

index 381a5d2d178d1ec0e121cac75aca9ad7f0cd462d..849d2cf28f57f770bb25644d6a7a220c7c98b303 100644 (file)
@@ -35,12 +35,11 @@ set(client_UI_SRCS
     addcertificatedialog.ui
     proxyauthdialog.ui
     mnemonicdialog.ui
+    wizard/flow2authwidget.ui
     wizard/owncloudadvancedsetuppage.ui
     wizard/owncloudconnectionmethoddialog.ui
     wizard/owncloudhttpcredspage.ui
     wizard/owncloudoauthcredspage.ui
-    wizard/flow2authcredspage.ui
-    wizard/flow2authwidget.ui
     wizard/owncloudsetupnocredspage.ui
     wizard/owncloudwizardresultpage.ui
     wizard/webview.ui
index f5e6423c5e6eec2d8cc8cf6950825f30499417dc..e75a8c9bf5bb182a13bd83c2c1dfad6a478ea579 100644 (file)
  */
 
 #include <QDesktopServices>
+#include <QApplication>
+#include <QClipboard>
 #include <QTimer>
 #include <QBuffer>
 #include "account.h"
-#include "creds/flow2auth.h"
+#include "flow2auth.h"
 #include <QJsonObject>
 #include <QJsonDocument>
 #include "theme.h"
@@ -28,6 +30,17 @@ namespace OCC {
 
 Q_LOGGING_CATEGORY(lcFlow2auth, "nextcloud.sync.credentials.flow2auth", QtInfoMsg)
 
+
+Flow2Auth::Flow2Auth(Account *account, QObject *parent)
+    : QObject(parent)
+    , _account(account)
+    , _isBusy(false)
+    , _hasToken(false)
+{
+    _pollTimer.setInterval(1000);
+    QObject::connect(&_pollTimer, &QTimer::timeout, this, &Flow2Auth::slotPollTimerTimeout);
+}
+
 Flow2Auth::~Flow2Auth()
 {
 }
@@ -47,7 +60,23 @@ QUrl Flow2Auth::authorisationLink() const
 
 void Flow2Auth::openBrowser()
 {
-    _pollTimer.stop();
+    fetchNewToken(TokenAction::actionOpenBrowser);
+}
+
+void Flow2Auth::copyLinkToClipboard()
+{
+    fetchNewToken(TokenAction::actionCopyLinkToClipboard);
+}
+
+void Flow2Auth::fetchNewToken(const TokenAction action)
+{
+    if(_isBusy)
+        return;
+
+    _isBusy = true;
+    _hasToken = false;
+
+    emit statusChanged(PollStatus::statusFetchToken, 0);
 
     // Step 1: Initiate a login, do an anonymous POST request
     QUrl url = Utility::concatUrlPath(_account->url().toString(), QLatin1String("/index.php/login/v2"));
@@ -59,14 +88,18 @@ void Flow2Auth::openBrowser()
     auto job = _account->sendRequest("POST", url, req);
     job->setTimeout(qMin(30 * 1000ll, job->timeoutMsec()));
 
-    QObject::connect(job, &SimpleNetworkJob::finishedSignal, this, [this](QNetworkReply *reply) {
+    QObject::connect(job, &SimpleNetworkJob::finishedSignal, this, [this, action](QNetworkReply *reply) {
         auto jsonData = reply->readAll();
         QJsonParseError jsonParseError;
         QJsonObject json = QJsonDocument::fromJson(jsonData, &jsonParseError).object();
+        QString pollToken, pollEndpoint, loginUrl;
 
-        QString pollToken = json.value("poll").toObject().value("token").toString();
-        QString pollEndpoint = json.value("poll").toObject().value("endpoint").toString();
-        QUrl loginUrl = json["login"].toString();
+        if (reply->error() == QNetworkReply::NoError && jsonParseError.error == QJsonParseError::NoError
+            && !json.isEmpty()) {
+            pollToken = json.value("poll").toObject().value("token").toString();
+            pollEndpoint = json.value("poll").toObject().value("endpoint").toString();
+            loginUrl = json["login"].toString();
+        }
 
         if (reply->error() != QNetworkReply::NoError || jsonParseError.error != QJsonParseError::NoError
             || json.isEmpty() || pollToken.isEmpty() || pollEndpoint.isEmpty() || loginUrl.isEmpty()) {
@@ -85,7 +118,9 @@ void Flow2Auth::openBrowser()
                 errorReason = tr("The reply from the server did not contain all expected fields");
             }
             qCWarning(lcFlow2auth) << "Error when getting the loginUrl" << json << errorReason;
-            emit result(Error);
+            emit result(Error, errorReason);
+            _pollTimer.stop();
+            _isBusy = false;
             return;
         }
 
@@ -99,34 +134,50 @@ void Flow2Auth::openBrowser()
         ConfigFile cfg;
         std::chrono::milliseconds polltime = cfg.remotePollInterval();
         qCInfo(lcFlow2auth) << "setting remote poll timer interval to" << polltime.count() << "msec";
-        _pollTimer.setInterval(1000);
-        QObject::connect(&_pollTimer, &QTimer::timeout, this, &Flow2Auth::slotPollTimerTimeout);
         _secondsInterval = (polltime.count() / 1000);
         _secondsLeft = _secondsInterval;
-        emit statusChanged(_secondsLeft);
-        _pollTimer.start();
+        emit statusChanged(PollStatus::statusPollCountdown, _secondsLeft);
 
+        if(!_pollTimer.isActive()) {
+            _pollTimer.start();
+        }
 
-        // Try to open Browser
-        if (!QDesktopServices::openUrl(authorisationLink())) {
-            // We cannot open the browser, then we claim we don't support Flow2Auth.
-            // Our UI callee should ask the user to copy and open the link.
-            emit result(NotSupported, QString());
+
+        switch(action)
+        {
+        case actionOpenBrowser:
+            // Try to open Browser
+            if (!QDesktopServices::openUrl(authorisationLink())) {
+                // We cannot open the browser, then we claim we don't support Flow2Auth.
+                // Our UI callee will ask the user to copy and open the link.
+                emit result(NotSupported);
+            }
+            break;
+        case actionCopyLinkToClipboard:
+            QApplication::clipboard()->setText(authorisationLink().toString(QUrl::FullyEncoded));
+            emit statusChanged(PollStatus::statusCopyLinkToClipboard, 0);
+            break;
         }
+
+        _isBusy = false;
+        _hasToken = true;
     });
 }
 
 void Flow2Auth::slotPollTimerTimeout()
 {
-    _pollTimer.stop();
+    if(_isBusy || !_hasToken)
+        return;
 
-    _secondsLeft--;
-    emit statusChanged(_secondsLeft);
+    _isBusy = true;
 
+    _secondsLeft--;
     if(_secondsLeft > 0) {
-        _pollTimer.start();
+        emit statusChanged(PollStatus::statusPollCountdown, _secondsLeft);
+        _isBusy = false;
         return;
     }
+    emit statusChanged(PollStatus::statusPollNow, 0);
 
     // Step 2: Poll
     QNetworkRequest req;
@@ -143,10 +194,15 @@ void Flow2Auth::slotPollTimerTimeout()
         auto jsonData = reply->readAll();
         QJsonParseError jsonParseError;
         QJsonObject json = QJsonDocument::fromJson(jsonData, &jsonParseError).object();
-
-        QUrl serverUrl = json["server"].toString();
-        QString loginName = json["loginName"].toString();
-        QString appPassword = json["appPassword"].toString();
+        QUrl serverUrl;
+        QString loginName, appPassword;
+
+        if (reply->error() == QNetworkReply::NoError && jsonParseError.error == QJsonParseError::NoError
+            && !json.isEmpty()) {
+            serverUrl = json["server"].toString();
+            loginName = json["loginName"].toString();
+            appPassword = json["appPassword"].toString();
+        }
 
         if (reply->error() != QNetworkReply::NoError || jsonParseError.error != QJsonParseError::NoError
             || json.isEmpty() || serverUrl.isEmpty() || loginName.isEmpty() || appPassword.isEmpty()) {
@@ -166,36 +222,50 @@ void Flow2Auth::slotPollTimerTimeout()
             }
             qCDebug(lcFlow2auth) << "Error when polling for the appPassword" << json << errorReason;
 
+            // We get a 404 until authentication is done, so don't show this error in the GUI.
+            if(reply->error() != QNetworkReply::ContentNotFoundError)
+                emit result(Error, errorReason);
+
             // Forget sensitive data
             appPassword.clear();
             loginName.clear();
 
             // Failed: poll again
             _secondsLeft = _secondsInterval;
-            _pollTimer.start();
+            _isBusy = false;
             return;
         }
 
+        _pollTimer.stop();
+
         // Success
         qCInfo(lcFlow2auth) << "Success getting the appPassword for user: " << loginName << ", server: " << serverUrl.toString();
 
         _account->setUrl(serverUrl);
 
-        emit result(LoggedIn, loginName, appPassword);
+        emit result(LoggedIn, QString(), loginName, appPassword);
 
         // Forget sensitive data
         appPassword.clear();
         loginName.clear();
+
+        _loginUrl.clear();
+        _pollToken.clear();
+        _pollEndpoint.clear();
+
+        _isBusy = false;
+        _hasToken = false;
     });
 }
 
 void Flow2Auth::slotPollNow()
 {
     // poll now if we're not already doing so
-    if(_pollTimer.isActive()) {
-        _secondsLeft = 1;
-        slotPollTimerTimeout();
-    }
+    if(_isBusy || !_hasToken)
+        return;
+
+    _secondsLeft = 1;
+    slotPollTimerTimeout();
 }
 
 } // namespace OCC
index b53c32e8a14d4a64077028ac21f5878074f40ae0..e4deb203ef2ecbfc1a7e41b11201e6d9c177456c 100644 (file)
@@ -30,11 +30,18 @@ class Flow2Auth : public QObject
 {
     Q_OBJECT
 public:
-    Flow2Auth(Account *account, QObject *parent)
-            : QObject(parent)
-            , _account(account)
-    {
-    }
+    enum TokenAction {
+        actionOpenBrowser = 1,
+        actionCopyLinkToClipboard
+    };
+    enum PollStatus {
+        statusPollCountdown = 1,
+        statusPollNow,
+        statusFetchToken,
+        statusCopyLinkToClipboard
+    };
+
+    Flow2Auth(Account *account, QObject *parent);
     ~Flow2Auth();
 
     enum Result { NotSupported,
@@ -43,6 +50,7 @@ public:
     Q_ENUM(Result);
     void start();
     void openBrowser();
+    void copyLinkToClipboard();
     QUrl authorisationLink() const;
 
 signals:
@@ -50,9 +58,10 @@ signals:
      * The state has changed.
      * when logged in, appPassword has the value of the app password.
      */
-    void result(Flow2Auth::Result result, const QString &user = QString(), const QString &appPassword = QString());
+    void result(Flow2Auth::Result result, const QString &errorString = QString(),
+                const QString &user = QString(), const QString &appPassword = QString());
 
-    void statusChanged(int secondsLeft);
+    void statusChanged(const PollStatus status, int secondsLeft);
 
 public slots:
     void slotPollNow();
@@ -61,6 +70,8 @@ private slots:
     void slotPollTimerTimeout();
 
 private:
+    void fetchNewToken(const TokenAction action);
+
     Account *_account;
     QUrl _loginUrl;
     QString _pollToken;
@@ -68,7 +79,8 @@ private:
     QTimer _pollTimer;
     int _secondsLeft;
     int _secondsInterval;
+    bool _isBusy;
+    bool _hasToken;
 };
 
-
 } // namespace OCC
index 3e8d55a39fb57b3949f5824862312cab5b8867a6..ea8d29e7979db7b3ef2764ed2a0dff812f763a41 100644 (file)
@@ -43,17 +43,18 @@ WebFlowCredentialsDialog::WebFlowCredentialsDialog(Account *account, bool useFlo
     _containerLayout->addWidget(_infoLabel);
 
     if (_useFlow2) {
-        _flow2AuthWidget = new Flow2AuthWidget(account);
         _flow2AuthWidget = new Flow2AuthWidget();
         _containerLayout->addWidget(_flow2AuthWidget);
 
-        connect(_flow2AuthWidget, &Flow2AuthWidget::urlCatched, this, &WebFlowCredentialsDialog::urlCatched);
+        connect(_flow2AuthWidget, &Flow2AuthWidget::authResult, this, &WebFlowCredentialsDialog::slotFlow2AuthResult);
 
         // Connect styleChanged events to our widgets, so they can adapt (Dark-/Light-Mode switching)
         connect(this, &WebFlowCredentialsDialog::styleChanged, _flow2AuthWidget, &Flow2AuthWidget::slotStyleChanged);
 
         // allow Flow2 page to poll on window activation
         connect(this, &WebFlowCredentialsDialog::onActivate, _flow2AuthWidget, &Flow2AuthWidget::slotPollNow);
+
+        _flow2AuthWidget->startAuth(account);
     } else {
         _webView = new WebView();
         _containerLayout->addWidget(_webView);
@@ -87,6 +88,7 @@ void WebFlowCredentialsDialog::closeEvent(QCloseEvent* e) {
     }
 
     if (_flow2AuthWidget) {
+        _flow2AuthWidget->resetAuth();
         _flow2AuthWidget->deleteLater();
         _flow2AuthWidget = nullptr;
     }
@@ -155,4 +157,14 @@ void WebFlowCredentialsDialog::slotShowSettingsDialog()
     });
 }
 
+void WebFlowCredentialsDialog::slotFlow2AuthResult(Flow2Auth::Result r, const QString &errorString, const QString &user, const QString &appPassword)
+{
+    if(r == Flow2Auth::LoggedIn) {
+        emit urlCatched(user, appPassword, QString());
+    } else {
+        // bring window to top
+        slotShowSettingsDialog();
+    }
+}
+
 } // namespace OCC
index 3dfdf744d9d095332a37e2918278b4355cb4db68..50ed7088d3d29eccb36d4d92a8559755d5fc96e2 100644 (file)
@@ -5,6 +5,7 @@
 #include <QUrl>
 
 #include "accountfwd.h"
+#include "creds/flow2auth.h"
 
 class QLabel;
 class QVBoxLayout;
@@ -34,6 +35,7 @@ protected:
     void changeEvent(QEvent *) override;
 
 public slots:
+    void slotFlow2AuthResult(Flow2Auth::Result, const QString &errorString, const QString &user, const QString &appPassword);
     void slotShowSettingsDialog();
 
 signals:
index 97df052ec6cc1a8c336424487666e9ae13e5bf05..d04d9741e0ccfcb028c0afc62469b625c30a5203 100644 (file)
  */
 
 #include <QVariant>
-#include <QMenu>
-#include <QClipboard>
+#include <QVBoxLayout>
 
-#include "wizard/flow2authcredspage.h"
+#include "flow2authcredspage.h"
 #include "theme.h"
 #include "account.h"
 #include "cookiejar.h"
 #include "wizard/owncloudwizardcommon.h"
 #include "wizard/owncloudwizard.h"
+#include "wizard/flow2authwidget.h"
 #include "creds/credentialsfactory.h"
 #include "creds/webflowcredentials.h"
 
-#include "QProgressIndicator.h"
-
 namespace OCC {
 
 Flow2AuthCredsPage::Flow2AuthCredsPage()
-    : AbstractCredentialsWizardPage()
-    , _ui()
-    , _progressIndi(new QProgressIndicator(this))
+    : AbstractCredentialsWizardPage(),
+    _flow2AuthWidget(nullptr)
 {
-    _ui.setupUi(this);
-
-    Theme *theme = Theme::instance();
-    _ui.topLabel->hide();
-    _ui.bottomLabel->hide();
-    QVariant variant = theme->customMedia(Theme::oCSetupTop);
-    WizardCommon::setupCustomMedia(variant, _ui.topLabel);
-    variant = theme->customMedia(Theme::oCSetupBottom);
-    WizardCommon::setupCustomMedia(variant, _ui.bottomLabel);
-
-    WizardCommon::initErrorLabel(_ui.errorLabel);
+    _layout = new QVBoxLayout(this);
 
     setTitle(WizardCommon::titleTemplate().arg(tr("Connect to %1").arg(Theme::instance()->appNameGUI())));
     setSubTitle(WizardCommon::subTitleTemplate().arg(tr("Login in your browser (Login Flow v2)")));
 
-    connect(_ui.openLinkButton, &QCommandLinkButton::clicked, this, &Flow2AuthCredsPage::slotOpenBrowser);
-    connect(_ui.copyLinkButton, &QCommandLinkButton::clicked, this, &Flow2AuthCredsPage::slotCopyLinkToClipboard);
+    _flow2AuthWidget = new Flow2AuthWidget();
+    _layout->addWidget(_flow2AuthWidget);
+
+    connect(_flow2AuthWidget, &Flow2AuthWidget::authResult, this, &Flow2AuthCredsPage::slotFlow2AuthResult);
 
-    _ui.horizontalLayout->addWidget(_progressIndi);
-    stopSpinner(false);
+    // Connect styleChanged events to our widgets, so they can adapt (Dark-/Light-Mode switching)
+    connect(this, &Flow2AuthCredsPage::styleChanged, _flow2AuthWidget, &Flow2AuthWidget::slotStyleChanged);
 
-    customizeStyle();
+    // allow Flow2 page to poll on window activation
+    connect(this, &Flow2AuthCredsPage::pollNow, _flow2AuthWidget, &Flow2AuthWidget::slotPollNow);
 }
 
 void Flow2AuthCredsPage::initializePage()
@@ -64,11 +54,9 @@ void Flow2AuthCredsPage::initializePage()
     OwncloudWizard *ocWizard = qobject_cast<OwncloudWizard *>(wizard());
     Q_ASSERT(ocWizard);
     ocWizard->account()->setCredentials(CredentialsFactory::create("http"));
-    _asyncAuth.reset(new Flow2Auth(ocWizard->account().data(), this));
-    connect(_asyncAuth.data(), &Flow2Auth::result, this, &Flow2AuthCredsPage::asyncAuthResult, Qt::QueuedConnection);
-    connect(_asyncAuth.data(), &Flow2Auth::statusChanged, this, &Flow2AuthCredsPage::slotStatusChanged);
-    connect(this, &Flow2AuthCredsPage::pollNow, _asyncAuth.data(), &Flow2Auth::slotPollNow);
-    _asyncAuth->start();
+
+    if(_flow2AuthWidget)
+        _flow2AuthWidget->startAuth(ocWizard->account().data());
 
     // Don't hide the wizard (avoid user confusion)!
     //wizard()->hide();
@@ -78,23 +66,20 @@ void OCC::Flow2AuthCredsPage::cleanupPage()
 {
     // The next or back button was activated, show the wizard again
     wizard()->show();
-    _asyncAuth.reset();
+    if(_flow2AuthWidget)
+        _flow2AuthWidget->resetAuth();
 
     // Forget sensitive data
     _appPassword.clear();
     _user.clear();
 }
 
-void Flow2AuthCredsPage::asyncAuthResult(Flow2Auth::Result r, const QString &user,
-    const QString &appPassword)
+void Flow2AuthCredsPage::slotFlow2AuthResult(Flow2Auth::Result r, const QString &errorString, const QString &user, const QString &appPassword)
 {
-    stopSpinner(false);
-
     switch (r) {
     case Flow2Auth::NotSupported: {
         /* Flow2Auth not supported (can't open browser) */
-        _ui.errorLabel->setText(tr("Unable to open the Browser, please copy the link to your Browser."));
-        _ui.errorLabel->show();
+        wizard()->show();
 
         /* Don't fallback to HTTP credentials */
         /*OwncloudWizard *ocWizard = qobject_cast<OwncloudWizard *>(wizard());
@@ -104,7 +89,6 @@ void Flow2AuthCredsPage::asyncAuthResult(Flow2Auth::Result r, const QString &use
     }
     case Flow2Auth::Error:
         /* Error while getting the access token.  (Timeout, or the server did not accept our client credentials */
-        _ui.errorLabel->show();
         wizard()->show();
         break;
     case Flow2Auth::LoggedIn: {
@@ -151,69 +135,14 @@ bool Flow2AuthCredsPage::isComplete() const
     return false; /* We can never go forward manually */
 }
 
-void Flow2AuthCredsPage::slotOpenBrowser()
-{
-    if (_ui.errorLabel)
-        _ui.errorLabel->hide();
-
-    if (_asyncAuth)
-        _asyncAuth->openBrowser();
-}
-
-void Flow2AuthCredsPage::slotCopyLinkToClipboard()
-{
-    if (_asyncAuth)
-        QApplication::clipboard()->setText(_asyncAuth->authorisationLink().toString(QUrl::FullyEncoded));
-}
-
 void Flow2AuthCredsPage::slotPollNow()
 {
     emit pollNow();
 }
 
-void Flow2AuthCredsPage::slotStatusChanged(int secondsLeft)
-{
-    const bool pollingNow = (secondsLeft == 0);
-
-    _ui.statusLabel->setText(tr("Polling for authorization") + (pollingNow ? "" : QString(" " + tr("in %1 seconds").arg(secondsLeft))) + "...");
-
-    if(pollingNow)
-        startSpinner();
-    else
-        stopSpinner(true);
-}
-
-void Flow2AuthCredsPage::startSpinner()
-{
-    _ui.horizontalLayout->setEnabled(true);
-    _ui.statusLabel->setVisible(true);
-    _progressIndi->setVisible(true);
-    _progressIndi->startAnimation();
-
-    _ui.openLinkButton->setEnabled(false);
-    _ui.copyLinkButton->setEnabled(false);
-}
-
-void Flow2AuthCredsPage::stopSpinner(bool showStatusLabel)
-{
-    _ui.horizontalLayout->setEnabled(false);
-    _ui.statusLabel->setVisible(showStatusLabel);
-    _progressIndi->setVisible(false);
-    _progressIndi->stopAnimation();
-
-    _ui.openLinkButton->setEnabled(true);
-    _ui.copyLinkButton->setEnabled(true);
-}
-
 void Flow2AuthCredsPage::slotStyleChanged()
 {
-    customizeStyle();
-}
-
-void Flow2AuthCredsPage::customizeStyle()
-{
-    if(_progressIndi)
-        _progressIndi->setColor(QGuiApplication::palette().color(QPalette::Text));
+    emit styleChanged();
 }
 
 } // namespace OCC
index eac4a0c372dc8519031f07b7e5fdd52238f2c8be..8a59605e01cea34c8817bf6ca14df184f862f8f8 100644 (file)
 #include "accountfwd.h"
 #include "creds/flow2auth.h"
 
-#include "ui_flow2authcredspage.h"
-
+class QVBoxLayout;
 class QProgressIndicator;
 
 namespace OCC {
 
+class Flow2AuthWidget;
+
 class Flow2AuthCredsPage : public AbstractCredentialsWizardPage
 {
     Q_OBJECT
@@ -46,31 +47,22 @@ public:
     bool isComplete() const override;
 
 public Q_SLOTS:
-    void asyncAuthResult(Flow2Auth::Result, const QString &user, const QString &appPassword);
+    void slotFlow2AuthResult(Flow2Auth::Result, const QString &errorString, const QString &user, const QString &appPassword);
     void slotPollNow();
-    void slotStatusChanged(int secondsLeft);
     void slotStyleChanged();
 
 signals:
     void connectToOCUrl(const QString &);
     void pollNow();
+    void styleChanged();
 
 public:
     QString _user;
     QString _appPassword;
-    QScopedPointer<Flow2Auth> _asyncAuth;
-    Ui_Flow2AuthCredsPage _ui;
-
-protected slots:
-    void slotOpenBrowser();
-    void slotCopyLinkToClipboard();
 
 private:
-    void startSpinner();
-    void stopSpinner(bool showStatusLabel);
-    void customizeStyle();
-
-    QProgressIndicator *_progressIndi;
+    Flow2AuthWidget *_flow2AuthWidget;
+    QVBoxLayout *_layout;
 };
 
 } // namespace OCC
diff --git a/src/gui/wizard/flow2authcredspage.ui b/src/gui/wizard/flow2authcredspage.ui
deleted file mode 100644 (file)
index 7fe3587..0000000
+++ /dev/null
@@ -1,126 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>Flow2AuthCredsPage</class>
- <widget class="QWidget" name="Flow2AuthCredsPage">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>424</width>
-    <height>373</height>
-   </rect>
-  </property>
-  <property name="windowTitle">
-   <string>Browser Authentication</string>
-  </property>
-  <layout class="QVBoxLayout" name="verticalLayout">
-   <item>
-    <widget class="QLabel" name="topLabel">
-     <property name="text">
-      <string notr="true">TextLabel</string>
-     </property>
-     <property name="textFormat">
-      <enum>Qt::RichText</enum>
-     </property>
-     <property name="alignment">
-      <set>Qt::AlignCenter</set>
-     </property>
-     <property name="wordWrap">
-      <bool>true</bool>
-     </property>
-    </widget>
-   </item>
-   <item>
-    <widget class="QLabel" name="label">
-     <property name="text">
-      <string>Please switch to your browser to proceed.</string>
-     </property>
-     <property name="wordWrap">
-      <bool>true</bool>
-     </property>
-    </widget>
-   </item>
-   <item>
-    <widget class="QLabel" name="errorLabel">
-     <property name="text">
-      <string>An error occurred while connecting. Please try again.</string>
-     </property>
-     <property name="textFormat">
-      <enum>Qt::PlainText</enum>
-     </property>
-    </widget>
-   </item>
-   <item>
-    <widget class="QCommandLinkButton" name="openLinkButton">
-     <property name="text">
-      <string>Re-open Browser</string>
-     </property>
-    </widget>
-   </item>
-   <item>
-    <widget class="QCommandLinkButton" name="copyLinkButton">
-     <property name="font">
-      <font>
-       <weight>50</weight>
-       <bold>false</bold>
-      </font>
-     </property>
-     <property name="text">
-      <string>Copy link</string>
-     </property>
-    </widget>
-   </item>
-   <item>
-    <spacer name="verticalSpacer_2">
-     <property name="orientation">
-      <enum>Qt::Vertical</enum>
-     </property>
-     <property name="sizeHint" stdset="0">
-      <size>
-       <width>20</width>
-       <height>10</height>
-      </size>
-     </property>
-    </spacer>
-   </item>
-   <item>
-    <widget class="QLabel" name="statusLabel">
-     <property name="text">
-      <string notr="true">TextLabel</string>
-     </property>
-     <property name="alignment">
-      <set>Qt::AlignCenter</set>
-     </property>
-    </widget>
-   </item>
-   <item>
-    <layout class="QHBoxLayout" name="horizontalLayout"/>
-   </item>
-   <item>
-    <spacer name="verticalSpacer">
-     <property name="orientation">
-      <enum>Qt::Vertical</enum>
-     </property>
-     <property name="sizeHint" stdset="0">
-      <size>
-       <width>20</width>
-       <height>127</height>
-      </size>
-     </property>
-    </spacer>
-   </item>
-   <item>
-    <widget class="QLabel" name="bottomLabel">
-     <property name="text">
-      <string notr="true">TextLabel</string>
-     </property>
-     <property name="textFormat">
-      <enum>Qt::RichText</enum>
-     </property>
-    </widget>
-   </item>
-  </layout>
- </widget>
- <resources/>
- <connections/>
-</ui>
index ee8d6c74903b30ed936b8b50a838116a8f16f4d9..1ba91be3341e85bab1277dd7ad6cf9f2c467986e 100644 (file)
 
 #include "flow2authwidget.h"
 
-#include <QDesktopServices>
-#include <QProgressBar>
-#include <QLoggingCategory>
-#include <QLocale>
-#include <QMessageBox>
-
-#include <QMenu>
-#include <QClipboard>
-
 #include "common/utility.h"
 #include "account.h"
 #include "wizard/owncloudwizardcommon.h"
@@ -34,15 +25,16 @@ namespace OCC {
 Q_LOGGING_CATEGORY(lcFlow2AuthWidget, "gui.wizard.flow2authwidget", QtInfoMsg)
 
 
-Flow2AuthWidget::Flow2AuthWidget(Account *account, QWidget *parent)
+Flow2AuthWidget::Flow2AuthWidget(QWidget *parent)
     : QWidget(parent)
-    , _account(account)
+    , _account(nullptr)
     , _ui()
     , _progressIndi(new QProgressIndicator(this))
 {
     _ui.setupUi(this);
 
     WizardCommon::initErrorLabel(_ui.errorLabel);
+    _ui.errorLabel->setTextFormat(Qt::RichText);
 
     connect(_ui.openLinkButton, &QCommandLinkButton::clicked, this, &Flow2AuthWidget::slotOpenBrowser);
     connect(_ui.copyLinkButton, &QCommandLinkButton::clicked, this, &Flow2AuthWidget::slotCopyLinkToClipboard);
@@ -50,17 +42,32 @@ Flow2AuthWidget::Flow2AuthWidget(Account *account, QWidget *parent)
     _ui.horizontalLayout->addWidget(_progressIndi);
     stopSpinner(false);
 
+    customizeStyle();
+}
+
+void Flow2AuthWidget::startAuth(Account *account)
+{
+    Flow2Auth *oldAuth = _asyncAuth.take();
+    if(oldAuth)
+        oldAuth->deleteLater();
+
+    if(account) {
+        _account = account;
+
     _asyncAuth.reset(new Flow2Auth(_account, this));
-    connect(_asyncAuth.data(), &Flow2Auth::result, this, &Flow2AuthWidget::asyncAuthResult, Qt::QueuedConnection);
+        connect(_asyncAuth.data(), &Flow2Auth::result, this, &Flow2AuthWidget::slotAuthResult, Qt::QueuedConnection);
     connect(_asyncAuth.data(), &Flow2Auth::statusChanged, this, &Flow2AuthWidget::slotStatusChanged);
     connect(this, &Flow2AuthWidget::pollNow, _asyncAuth.data(), &Flow2Auth::slotPollNow);
     _asyncAuth->start();
+    }
+}
 
-    customizeStyle();
+void Flow2AuthWidget::resetAuth(Account *account)
+{
+    startAuth(account);
 }
 
-void Flow2AuthWidget::asyncAuthResult(Flow2Auth::Result r, const QString &user,
-    const QString &appPassword)
+void Flow2AuthWidget::slotAuthResult(Flow2Auth::Result r, const QString &errorString, const QString &user, const QString &appPassword)
 {
     stopSpinner(false);
 
@@ -72,15 +79,16 @@ void Flow2AuthWidget::asyncAuthResult(Flow2Auth::Result r, const QString &user,
         break;
     case Flow2Auth::Error:
         /* Error while getting the access token.  (Timeout, or the server did not accept our client credentials */
+        _ui.errorLabel->setText(errorString);
         _ui.errorLabel->show();
         break;
     case Flow2Auth::LoggedIn: {
-        _user = user;
-        _appPassword = appPassword;
-        emit urlCatched(_user, _appPassword, QString());
+        _ui.errorLabel->hide();
         break;
     }
     }
+
+    emit authResult(r, errorString, user, appPassword);
 }
 
 void Flow2AuthWidget::setError(const QString &error) {
@@ -93,11 +101,8 @@ void Flow2AuthWidget::setError(const QString &error) {
 }
 
 Flow2AuthWidget::~Flow2AuthWidget() {
-    _asyncAuth.reset();
-
     // Forget sensitive data
-    _appPassword.clear();
-    _user.clear();
+    _asyncAuth.reset();
 }
 
 void Flow2AuthWidget::slotOpenBrowser()
@@ -111,8 +116,11 @@ void Flow2AuthWidget::slotOpenBrowser()
 
 void Flow2AuthWidget::slotCopyLinkToClipboard()
 {
+    if (_ui.errorLabel)
+        _ui.errorLabel->hide();
+
     if (_asyncAuth)
-        QApplication::clipboard()->setText(_asyncAuth->authorisationLink().toString(QUrl::FullyEncoded));
+        _asyncAuth->copyLinkToClipboard();
 }
 
 void Flow2AuthWidget::slotPollNow()
@@ -120,16 +128,27 @@ void Flow2AuthWidget::slotPollNow()
     emit pollNow();
 }
 
-void Flow2AuthWidget::slotStatusChanged(int secondsLeft)
+void Flow2AuthWidget::slotStatusChanged(Flow2Auth::PollStatus status, int secondsLeft)
 {
-    const bool pollingNow = (secondsLeft == 0);
-
-    _ui.statusLabel->setText(tr("Polling for authorization") + (pollingNow ? "" : QString(" " + tr("in %1 seconds").arg(secondsLeft))) + "...");
-
-    if(pollingNow)
+    switch(status)
+{
+    case Flow2Auth::statusPollCountdown:
+        _ui.statusLabel->setText(tr("Waiting for authorization") + QString(" (%1)").arg(secondsLeft));
+        stopSpinner(true);
+        break;
+    case Flow2Auth::statusPollNow:
+        _ui.statusLabel->setText(tr("Polling for authorization") + "...");
+        startSpinner();
+        break;
+    case Flow2Auth::statusFetchToken:
+        _ui.statusLabel->setText(tr("Starting authorization") + "...");
         startSpinner();
-    else
+        break;
+    case Flow2Auth::statusCopyLinkToClipboard:
+        _ui.statusLabel->setText(tr("Link copied to clipboard."));
         stopSpinner(true);
+        break;
+    }
 }
 
 void Flow2AuthWidget::startSpinner()
index 0912657a79c99c898a19b1071b37154c8614c201..c10b847105f6e0a0258f5881fef9c5a4ab6e31c5 100644 (file)
@@ -30,25 +30,25 @@ class Flow2AuthWidget : public QWidget
 {
     Q_OBJECT
 public:
-    Flow2AuthWidget(Account *account, QWidget *parent = nullptr);
+    Flow2AuthWidget(QWidget *parent = nullptr);
     virtual ~Flow2AuthWidget();
 
+    void startAuth(Account *account);
+    void resetAuth(Account *account = nullptr);
     void setError(const QString &error);
 
 public Q_SLOTS:
-    void asyncAuthResult(Flow2Auth::Result, const QString &user, const QString &appPassword);
+    void slotAuthResult(Flow2Auth::Result, const QString &errorString, const QString &user, const QString &appPassword);
     void slotPollNow();
-    void slotStatusChanged(int secondsLeft);
+    void slotStatusChanged(Flow2Auth::PollStatus status, int secondsLeft);
     void slotStyleChanged();
 
 signals:
-    void urlCatched(const QString user, const QString pass, const QString host);
+    void authResult(Flow2Auth::Result, const QString &errorString, const QString &user, const QString &appPassword);
     void pollNow();
 
 private:
     Account *_account;
-    QString _user;
-    QString _appPassword;
     QScopedPointer<Flow2Auth> _asyncAuth;
     Ui_Flow2AuthWidget _ui;