Port the reCAPTCHA dialog to QML

This commit is contained in:
Loren Burkholder 2023-06-28 17:00:34 -04:00
parent ff4c16c34f
commit fedc178827
9 changed files with 153 additions and 124 deletions

View file

@ -353,8 +353,6 @@ set(SRC_FILES
# Dialogs # Dialogs
src/dialogs/FallbackAuth.cpp src/dialogs/FallbackAuth.cpp
src/dialogs/FallbackAuth.h src/dialogs/FallbackAuth.h
src/dialogs/ReCaptcha.cpp
src/dialogs/ReCaptcha.h
# Emoji # Emoji
src/emoji/Provider.cpp src/emoji/Provider.cpp
@ -485,6 +483,8 @@ set(SRC_FILES
src/PowerlevelsEditModels.h src/PowerlevelsEditModels.h
src/ReadReceiptsModel.cpp src/ReadReceiptsModel.cpp
src/ReadReceiptsModel.h src/ReadReceiptsModel.h
src/ReCaptcha.cpp
src/ReCaptcha.h
src/RegisterPage.cpp src/RegisterPage.cpp
src/RegisterPage.h src/RegisterPage.h
src/RoomDirectoryModel.cpp src/RoomDirectoryModel.cpp
@ -776,6 +776,7 @@ set(QML_SOURCES
resources/qml/dialogs/PowerLevelSpacesApplyDialog.qml resources/qml/dialogs/PowerLevelSpacesApplyDialog.qml
resources/qml/dialogs/RawMessageDialog.qml resources/qml/dialogs/RawMessageDialog.qml
resources/qml/dialogs/ReadReceipts.qml resources/qml/dialogs/ReadReceipts.qml
resources/qml/dialogs/ReCaptchaDialog.qml
resources/qml/dialogs/RoomDirectory.qml resources/qml/dialogs/RoomDirectory.qml
resources/qml/dialogs/RoomMembers.qml resources/qml/dialogs/RoomMembers.qml
resources/qml/dialogs/AllowedRoomsSettingsDialog.qml resources/qml/dialogs/AllowedRoomsSettingsDialog.qml

View file

@ -360,6 +360,7 @@ Pane {
onAccepted: UIA.continue3pidReceived() onAccepted: UIA.continue3pidReceived()
} }
Connections { Connections {
function onConfirm3pidToken() { function onConfirm3pidToken() {
uiaConfirmationLinkDialog.open(); uiaConfirmationLinkDialog.open();
@ -381,6 +382,18 @@ Pane {
function onPrompt3pidToken() { function onPrompt3pidToken() {
uiaTokenPrompt.show(); uiaTokenPrompt.show();
} }
function onReCaptcha(recaptcha) {
var component = Qt.createComponent("qrc:/resources/qml/dialogs/ReCaptchaDialog.qml");
if (component.status == Component.Ready) {
var dialog = component.createObject(timelineRoot, {
"recaptcha": recaptcha
});
dialog.show();
destroyOnClose(dialog);
} else {
console.error("Failed to create component: " + component.errorString());
}
}
target: UIA target: UIA
} }

View file

@ -0,0 +1,63 @@
// SPDX-FileCopyrightText: Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick
import QtQuick.Controls
import im.nheko
ApplicationWindow {
id: recaptchaRoot
required property ReCaptcha recaptcha
function accept() {
recaptcha.confirm();
recaptchaRoot.close();
}
function reject() {
recaptcha.cancel();
recaptchaRoot.close();
}
color: palette.window
title: recaptcha.context
flags: Qt.Tool | Qt.WindowStaysOnTopHint | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
height: msg.implicitHeight + footer.implicitHeight
width: Math.max(msg.implicitWidth, footer.implicitWidth)
Shortcut {
sequence: StandardKey.Cancel
onActivated: recaptchaRoot.reject()
}
Label {
id: msg
anchors.fill: parent
padding: 8
text: qsTr("Solve the reCAPTCHA and press the confirm button")
}
footer: DialogButtonBox {
onAccepted: recaptchaRoot.accept()
onRejected: recaptchaRoot.reject()
Button {
text: qsTr("Open reCAPTCHA")
onClicked: recaptcha.openReCaptcha()
}
Button {
text: qsTr("Cancel")
DialogButtonBox.buttonRole: DialogButtonBox.RejectRole
}
Button {
text: qsTr("Confirm")
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
}
}
}

28
src/ReCaptcha.cpp Normal file
View file

@ -0,0 +1,28 @@
// SPDX-FileCopyrightText: Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
#include "ReCaptcha.h"
#include <QDesktopServices>
#include <QUrl>
#include "MatrixClient.h"
ReCaptcha::ReCaptcha(const QString &session, const QString &context, QObject *parent)
: QObject{parent},
m_session{session},
m_context{context}
{
}
void ReCaptcha::openReCaptcha()
{
const auto url = QString("https://%1:%2/_matrix/client/r0/auth/m.login.recaptcha/"
"fallback/web?session=%3")
.arg(QString::fromStdString(http::client()->server()))
.arg(http::client()->port())
.arg(m_session);
QDesktopServices::openUrl(url);
}

32
src/ReCaptcha.h Normal file
View file

@ -0,0 +1,32 @@
// SPDX-FileCopyrightText: Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <QQmlEngine>
class ReCaptcha : public QObject
{
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("")
Q_PROPERTY(QString context MEMBER m_context CONSTANT)
Q_PROPERTY(QString session MEMBER m_session CONSTANT)
public:
ReCaptcha(const QString &session, const QString &context, QObject *parent = nullptr);
Q_INVOKABLE void openReCaptcha();
Q_INVOKABLE void confirm() { emit confirmation(); }
Q_INVOKABLE void cancel() { emit cancelled(); }
signals:
void confirmation();
void cancelled();
private:
QString m_session;
QString m_context;
};

View file

@ -1,74 +0,0 @@
// SPDX-FileCopyrightText: Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
#include <QDesktopServices>
#include <QLabel>
#include <QPushButton>
#include <QUrl>
#include <QVBoxLayout>
#include "dialogs/ReCaptcha.h"
#include "Config.h"
#include "MatrixClient.h"
using namespace dialogs;
ReCaptcha::ReCaptcha(const QString &session, QWidget *parent)
: QWidget(parent)
{
setAutoFillBackground(true);
setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
setWindowModality(Qt::WindowModal);
setAttribute(Qt::WA_DeleteOnClose, true);
auto layout = new QVBoxLayout(this);
layout->setSpacing(conf::modals::WIDGET_SPACING);
layout->setContentsMargins(conf::modals::WIDGET_MARGIN,
conf::modals::WIDGET_MARGIN,
conf::modals::WIDGET_MARGIN,
conf::modals::WIDGET_MARGIN);
auto buttonLayout = new QHBoxLayout();
buttonLayout->setContentsMargins(0, 0, 0, 0);
buttonLayout->setSpacing(8);
openCaptchaBtn_ = new QPushButton(tr("Open reCAPTCHA"), this);
cancelBtn_ = new QPushButton(tr("Cancel"), this);
confirmBtn_ = new QPushButton(tr("Confirm"), this);
confirmBtn_->setDefault(true);
buttonLayout->addStretch(1);
buttonLayout->addWidget(openCaptchaBtn_);
buttonLayout->addWidget(cancelBtn_);
buttonLayout->addWidget(confirmBtn_);
QFont font;
font.setPointSizeF(font.pointSizeF() * conf::modals::LABEL_MEDIUM_SIZE_RATIO);
auto label = new QLabel(tr("Solve the reCAPTCHA and press the confirm button"), this);
label->setFont(font);
layout->addWidget(label);
layout->addLayout(buttonLayout);
connect(openCaptchaBtn_, &QPushButton::clicked, [session]() {
const auto url = QString("https://%1:%2/_matrix/client/r0/auth/m.login.recaptcha/"
"fallback/web?session=%3")
.arg(QString::fromStdString(http::client()->server()))
.arg(http::client()->port())
.arg(session);
QDesktopServices::openUrl(url);
});
connect(confirmBtn_, &QPushButton::clicked, this, [this]() {
emit confirmation();
close();
});
connect(cancelBtn_, &QPushButton::clicked, this, [this]() {
emit cancel();
close();
});
}

View file

@ -1,29 +0,0 @@
// SPDX-FileCopyrightText: Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <QWidget>
class QPushButton;
namespace dialogs {
class ReCaptcha final : public QWidget
{
Q_OBJECT
public:
ReCaptcha(const QString &session, QWidget *parent = nullptr);
signals:
void confirmation();
void cancel();
private:
QPushButton *openCaptchaBtn_;
QPushButton *confirmBtn_;
QPushButton *cancelBtn_;
};
} // dialogs

View file

@ -14,12 +14,12 @@
#include "Logging.h" #include "Logging.h"
#include "MatrixClient.h" #include "MatrixClient.h"
#include "dialogs/FallbackAuth.h" #include "dialogs/FallbackAuth.h"
#include "dialogs/ReCaptcha.h" #include "ReCaptcha.h"
UIA * UIA *
UIA::instance() UIA::instance()
{ {
static UIA uia; static UIA uia{nullptr};
return &uia; return &uia;
} }
@ -99,24 +99,16 @@ UIA::genericHandler(QString context)
} else if (current_stage == mtx::user_interactive::auth_types::msisdn) { } else if (current_stage == mtx::user_interactive::auth_types::msisdn) {
emit phoneNumber(); emit phoneNumber();
} else if (current_stage == mtx::user_interactive::auth_types::recaptcha) { } else if (current_stage == mtx::user_interactive::auth_types::recaptcha) {
auto captchaDialog = auto captcha = new ReCaptcha(QString::fromStdString(u.session), context, nullptr);
new dialogs::ReCaptcha(QString::fromStdString(u.session), nullptr); QQmlEngine::setObjectOwnership(captcha, QQmlEngine::JavaScriptOwnership);
captchaDialog->setWindowTitle(context); connect(captcha, &ReCaptcha::confirmation, this, [h, u]() {
h.next(mtx::user_interactive::Auth{u.session,
connect( mtx::user_interactive::auth::Fallback{}});
captchaDialog, &dialogs::ReCaptcha::confirmation, this, [captchaDialog, h, u]() { });
captchaDialog->close(); connect(captcha, &ReCaptcha::cancelled, this, [this]() {
captchaDialog->deleteLater();
h.next(mtx::user_interactive::Auth{u.session,
mtx::user_interactive::auth::Fallback{}});
});
connect(captchaDialog, &dialogs::ReCaptcha::cancel, this, [this]() {
emit error(tr("Registration aborted")); emit error(tr("Registration aborted"));
}); });
emit reCaptcha(captcha);
QTimer::singleShot(0, this, [captchaDialog]() { captchaDialog->show(); });
} else if (current_stage == mtx::user_interactive::auth_types::dummy) { } else if (current_stage == mtx::user_interactive::auth_types::dummy) {
h.next( h.next(
mtx::user_interactive::Auth{u.session, mtx::user_interactive::auth::Dummy{}}); mtx::user_interactive::Auth{u.session, mtx::user_interactive::auth::Dummy{}});

View file

@ -9,6 +9,8 @@
#include <mtxclient/http/client.hpp> #include <mtxclient/http/client.hpp>
#include "ReCaptcha.h"
class UIA final : public QObject class UIA final : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -39,7 +41,7 @@ public:
return instance(); return instance();
} }
UIA(QObject *parent = nullptr) UIA(QObject *parent)
: QObject(parent) : QObject(parent)
{ {
} }
@ -59,6 +61,7 @@ signals:
void password(); void password();
void email(); void email();
void phoneNumber(); void phoneNumber();
void reCaptcha(ReCaptcha *recaptcha);
void confirm3pidToken(); void confirm3pidToken();
void prompt3pidToken(); void prompt3pidToken();