@@ -77,7 +77,7 @@ ecm_setup_version(${PROJECT_VERSION}
|
|||||||
VERSION_HEADER ${CMAKE_CURRENT_BINARY_DIR}/neochat-version.h
|
VERSION_HEADER ${CMAKE_CURRENT_BINARY_DIR}/neochat-version.h
|
||||||
)
|
)
|
||||||
|
|
||||||
find_package(Qt${QT_MAJOR_VERSION} ${QT_MIN_VERSION} NO_MODULE COMPONENTS Core Quick Gui QuickControls2 Multimedia Svg)
|
find_package(Qt${QT_MAJOR_VERSION} ${QT_MIN_VERSION} NO_MODULE COMPONENTS Core Quick Gui QuickControls2 Multimedia Svg WebView)
|
||||||
set_package_properties(Qt${QT_MAJOR_VERSION} PROPERTIES
|
set_package_properties(Qt${QT_MAJOR_VERSION} PROPERTIES
|
||||||
TYPE REQUIRED
|
TYPE REQUIRED
|
||||||
PURPOSE "Basic application components"
|
PURPOSE "Basic application components"
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
android:versionName="${versionName}"
|
android:versionName="${versionName}"
|
||||||
android:versionCode="${versionCode}"
|
android:versionCode="${versionCode}"
|
||||||
android:installLocation="auto">
|
android:installLocation="auto">
|
||||||
<application android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="NeoChat" android:icon="@drawable/neochat">
|
<application android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="NeoChat" android:icon="@drawable/neochat" android:usesCleartextTraffic="true">
|
||||||
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation"
|
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation"
|
||||||
android:name="org.qtproject.qt5.android.bindings.QtActivity"
|
android:name="org.qtproject.qt5.android.bindings.QtActivity"
|
||||||
android:label="NeoChat"
|
android:label="NeoChat"
|
||||||
|
|||||||
@@ -125,6 +125,7 @@ add_library(neochat STATIC
|
|||||||
events/pollevent.cpp
|
events/pollevent.cpp
|
||||||
pollhandler.cpp
|
pollhandler.cpp
|
||||||
utils.h
|
utils.h
|
||||||
|
registration.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
ecm_qt_declare_logging_category(neochat
|
ecm_qt_declare_logging_category(neochat
|
||||||
@@ -141,6 +142,11 @@ add_executable(neochat-app
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/res.generated.qrc
|
${CMAKE_CURRENT_SOURCE_DIR}/res.generated.qrc
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(TARGET Qt::WebView)
|
||||||
|
target_link_libraries(neochat-app PUBLIC Qt::WebView)
|
||||||
|
target_compile_definitions(neochat-app PUBLIC -DHAVE_WEBVIEW)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_include_directories(neochat-app PRIVATE ${CMAKE_BINARY_DIR})
|
target_include_directories(neochat-app PRIVATE ${CMAKE_BINARY_DIR})
|
||||||
|
|
||||||
target_link_libraries(neochat-app PRIVATE
|
target_link_libraries(neochat-app PRIVATE
|
||||||
@@ -177,6 +183,7 @@ endif()
|
|||||||
|
|
||||||
target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR})
|
target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR})
|
||||||
target_link_libraries(neochat PUBLIC Qt::Core Qt::Quick Qt::Qml Qt::Gui Qt::Multimedia Qt::Network Qt::QuickControls2 KF${QT_MAJOR_VERSION}::I18n KF${QT_MAJOR_VERSION}::Kirigami2 KF${QT_MAJOR_VERSION}::Notifications KF${QT_MAJOR_VERSION}::ConfigCore KF${QT_MAJOR_VERSION}::ConfigGui KF${QT_MAJOR_VERSION}::CoreAddons KF${QT_MAJOR_VERSION}::SonnetCore KF${QT_MAJOR_VERSION}::ItemModels Quotient${QUOTIENT_SUFFIX} cmark::cmark QCoro::Core)
|
target_link_libraries(neochat PUBLIC Qt::Core Qt::Quick Qt::Qml Qt::Gui Qt::Multimedia Qt::Network Qt::QuickControls2 KF${QT_MAJOR_VERSION}::I18n KF${QT_MAJOR_VERSION}::Kirigami2 KF${QT_MAJOR_VERSION}::Notifications KF${QT_MAJOR_VERSION}::ConfigCore KF${QT_MAJOR_VERSION}::ConfigGui KF${QT_MAJOR_VERSION}::CoreAddons KF${QT_MAJOR_VERSION}::SonnetCore KF${QT_MAJOR_VERSION}::ItemModels Quotient${QUOTIENT_SUFFIX} cmark::cmark QCoro::Core)
|
||||||
|
|
||||||
kconfig_add_kcfg_files(neochat GENERATE_MOC neochatconfig.kcfgc)
|
kconfig_add_kcfg_files(neochat GENERATE_MOC neochatconfig.kcfgc)
|
||||||
|
|
||||||
if(NEOCHAT_FLATPAK)
|
if(NEOCHAT_FLATPAK)
|
||||||
|
|||||||
10
src/main.cpp
10
src/main.cpp
@@ -18,6 +18,10 @@
|
|||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_WEBVIEW
|
||||||
|
#include <QtWebView>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <KAboutData>
|
#include <KAboutData>
|
||||||
#ifdef HAVE_KDBUSADDONS
|
#ifdef HAVE_KDBUSADDONS
|
||||||
#include <KDBusService>
|
#include <KDBusService>
|
||||||
@@ -95,6 +99,7 @@
|
|||||||
#include "runner.h"
|
#include "runner.h"
|
||||||
#include <QDBusConnection>
|
#include <QDBusConnection>
|
||||||
#endif
|
#endif
|
||||||
|
#include "registration.h"
|
||||||
|
|
||||||
#ifdef Q_OS_WINDOWS
|
#ifdef Q_OS_WINDOWS
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
@@ -139,6 +144,10 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
QNetworkProxyFactory::setUseSystemConfiguration(true);
|
QNetworkProxyFactory::setUseSystemConfiguration(true);
|
||||||
|
|
||||||
|
#ifdef HAVE_WEBVIEW
|
||||||
|
QtWebView::initialize();
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef Q_OS_ANDROID
|
#ifdef Q_OS_ANDROID
|
||||||
QGuiApplication app(argc, argv);
|
QGuiApplication app(argc, argv);
|
||||||
QQuickStyle::setStyle(QStringLiteral("org.kde.breeze"));
|
QQuickStyle::setStyle(QStringLiteral("org.kde.breeze"));
|
||||||
@@ -233,6 +242,7 @@ int main(int argc, char *argv[])
|
|||||||
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "AccountRegistry", &Controller::instance().accounts());
|
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "AccountRegistry", &Controller::instance().accounts());
|
||||||
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "SpaceHierarchyCache", &SpaceHierarchyCache::instance());
|
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "SpaceHierarchyCache", &SpaceHierarchyCache::instance());
|
||||||
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "CustomEmojiModel", &CustomEmojiModel::instance());
|
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "CustomEmojiModel", &CustomEmojiModel::instance());
|
||||||
|
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "Registration", &Registration::instance());
|
||||||
qmlRegisterType<ActionsHandler>("org.kde.neochat", 1, 0, "ActionsHandler");
|
qmlRegisterType<ActionsHandler>("org.kde.neochat", 1, 0, "ActionsHandler");
|
||||||
qmlRegisterType<ChatDocumentHandler>("org.kde.neochat", 1, 0, "ChatDocumentHandler");
|
qmlRegisterType<ChatDocumentHandler>("org.kde.neochat", 1, 0, "ChatDocumentHandler");
|
||||||
qmlRegisterType<RoomListModel>("org.kde.neochat", 1, 0, "RoomListModel");
|
qmlRegisterType<RoomListModel>("org.kde.neochat", 1, 0, "RoomListModel");
|
||||||
|
|||||||
49
src/qml/Component/Login/Captcha.qml
Normal file
49
src/qml/Component/Login/Captcha.qml
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
import QtQuick 2.14
|
||||||
|
import QtQuick.Controls 2.14 as Controls
|
||||||
|
import QtQuick.Layouts 1.14
|
||||||
|
import QtWebView 1.15
|
||||||
|
|
||||||
|
import org.kde.kirigami 2.12 as Kirigami
|
||||||
|
import org.kde.kirigamiaddons.formcard 1.0 as FormCard
|
||||||
|
|
||||||
|
import org.kde.neochat 1.0
|
||||||
|
|
||||||
|
LoginStep {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
FormCard.AbstractFormDelegate {
|
||||||
|
background: null
|
||||||
|
contentItem: WebView {
|
||||||
|
id: webview
|
||||||
|
url: "http://localhost:20847"
|
||||||
|
implicitHeight: 500
|
||||||
|
onLoadingChanged: {
|
||||||
|
webview.runJavaScript("document.body.style.background = '" + Kirigami.Theme.backgroundColor + "'")
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: timer
|
||||||
|
repeat: true
|
||||||
|
running: true
|
||||||
|
interval: 300
|
||||||
|
onTriggered: {
|
||||||
|
if(!webview.visible) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
webview.runJavaScript("!!grecaptcha ? grecaptcha.getResponse() : \"\"", function(response){
|
||||||
|
if(!webview.visible || !response)
|
||||||
|
return
|
||||||
|
timer.running = false;
|
||||||
|
Registration.recaptchaResponse = response;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
previousAction: Kirigami.Action {
|
||||||
|
onTriggered: root.processed("qrc:/Username.qml")
|
||||||
|
}
|
||||||
|
}
|
||||||
59
src/qml/Component/Login/Email.qml
Normal file
59
src/qml/Component/Login/Email.qml
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.12 as QQC2
|
||||||
|
import QtQuick.Layouts 1.12
|
||||||
|
|
||||||
|
import org.kde.kirigami 2.12 as Kirigami
|
||||||
|
import org.kde.kirigamiaddons.formcard 1.0 as FormCard
|
||||||
|
|
||||||
|
import org.kde.neochat 1.0
|
||||||
|
|
||||||
|
LoginStep {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
onActiveFocusChanged: if (activeFocus) emailField.forceActiveFocus()
|
||||||
|
|
||||||
|
FormCard.FormTextFieldDelegate {
|
||||||
|
id: emailField
|
||||||
|
label: i18n("Add an e-mail address:")
|
||||||
|
placeholderText: "user@example.com"
|
||||||
|
onTextChanged: Registration.email = text
|
||||||
|
Keys.onReturnPressed: {
|
||||||
|
if (root.nextAction.enabled) {
|
||||||
|
root.nextAction.trigger()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FormCard.FormTextDelegate {
|
||||||
|
id: confirmMessage
|
||||||
|
text: i18n("Confirm e-mail address")
|
||||||
|
visible: false
|
||||||
|
description: i18n("A confirmation e-mail has been sent to your address. Please continue here <b>after</b> clicking on the confirmation link in the e-mail")
|
||||||
|
}
|
||||||
|
|
||||||
|
FormCard.FormButtonDelegate {
|
||||||
|
id: resendButton
|
||||||
|
text: i18nc("@button", "Re-send confirmation e-mail")
|
||||||
|
onClicked: Registration.registerEmail()
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
|
||||||
|
nextAction: Kirigami.Action {
|
||||||
|
enabled: emailField.text.length > 0
|
||||||
|
onTriggered: {
|
||||||
|
if (confirmMessage.visible) {
|
||||||
|
Registration.registerAccount()
|
||||||
|
} else {
|
||||||
|
Registration.registerEmail()
|
||||||
|
confirmMessage.visible = true
|
||||||
|
resendButton.visible = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
previousAction: Kirigami.Action {
|
||||||
|
onTriggered: root.processed("qrc:/Username.qml")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: 2020 Tobias Fella <tobias.fella@kde.org>
|
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
@@ -6,56 +6,42 @@ import QtQuick.Controls 2.15 as QQC2
|
|||||||
import QtQuick.Layouts 1.15
|
import QtQuick.Layouts 1.15
|
||||||
|
|
||||||
import org.kde.kirigami 2.15 as Kirigami
|
import org.kde.kirigami 2.15 as Kirigami
|
||||||
|
import org.kde.kirigamiaddons.formcard 1.0 as FormCard
|
||||||
|
|
||||||
import org.kde.neochat 1.0
|
import org.kde.neochat 1.0
|
||||||
|
|
||||||
LoginStep {
|
LoginStep {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
readonly property var homeserver: customHomeserver.visible ? customHomeserver.text : serverCombo.currentText
|
onActiveFocusChanged: if (activeFocus) urlField.forceActiveFocus()
|
||||||
property bool loading: false
|
|
||||||
|
|
||||||
title: i18nc("@title", "Select a Homeserver")
|
FormCard.FormTextFieldDelegate {
|
||||||
|
id: urlField
|
||||||
action: Kirigami.Action {
|
label: i18n("Server Url:")
|
||||||
enabled: LoginHelper.homeserverReachable && !customHomeserver.visible || customHomeserver.acceptableInput
|
validator: RegularExpressionValidator {
|
||||||
onTriggered: {
|
regularExpression: /([a-zA-Z0-9\-]+\.)*[a-zA-Z0-9]+(:[0-9]+)?/
|
||||||
// TODO
|
}
|
||||||
console.log("register todo")
|
onTextChanged: timer.restart()
|
||||||
|
statusMessage: Registration.status === Registration.ServerNoRegistration ? i18n("Registration is disabled on this server.") : ""
|
||||||
|
Keys.onReturnPressed: {
|
||||||
|
if (root.nextAction.enabled) {
|
||||||
|
root.nextAction.trigger()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onHomeserverChanged: {
|
Timer {
|
||||||
LoginHelper.testHomeserver("@user:" + homeserver)
|
id: timer
|
||||||
|
interval: 500
|
||||||
|
onTriggered: Registration.homeserver = urlField.text
|
||||||
}
|
}
|
||||||
|
|
||||||
Kirigami.FormLayout {
|
nextAction: Kirigami.Action {
|
||||||
Component.onCompleted: Controller.testHomeserver(homeserver)
|
text: Registration.testing ? i18n("Loading") : null
|
||||||
|
enabled: Registration.status > Registration.ServerNoRegistration
|
||||||
QQC2.ComboBox {
|
onTriggered: root.processed("qrc:/Username.qml");
|
||||||
id: serverCombo
|
}
|
||||||
|
previousAction: Kirigami.Action {
|
||||||
Kirigami.FormData.label: i18n("Homeserver:")
|
onTriggered: root.processed("qrc:/LoginRegister.qml")
|
||||||
model: ["matrix.org", "kde.org", "tchncs.de", i18n("Other...")]
|
|
||||||
}
|
|
||||||
|
|
||||||
QQC2.TextField {
|
|
||||||
id: customHomeserver
|
|
||||||
|
|
||||||
Kirigami.FormData.label: i18n("Url:")
|
|
||||||
visible: serverCombo.currentIndex === 3
|
|
||||||
onTextChanged: {
|
|
||||||
Controller.testHomeserver(text)
|
|
||||||
}
|
|
||||||
validator: RegularExpressionValidator {
|
|
||||||
regularExpression: /([a-zA-Z0-9\-]+\.)*[a-zA-Z0-9]+(:[0-9]+)?/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QQC2.Button {
|
|
||||||
id: continueButton
|
|
||||||
text: i18nc("@action:button", "Continue")
|
|
||||||
action: root.action
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,10 +47,9 @@ LoginStep {
|
|||||||
}
|
}
|
||||||
enabled: LoginHelper.homeserverReachable
|
enabled: LoginHelper.homeserverReachable
|
||||||
}
|
}
|
||||||
// TODO: enable once we have registration
|
previousAction: Kirigami.Action {
|
||||||
// previousAction: Kirigami.Action {
|
onTriggered: {
|
||||||
// onTriggered: {
|
root.processed("qrc:/LoginRegister.qml")
|
||||||
// root.processed("qrc:/Login.qml")
|
}
|
||||||
// }
|
}
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,9 +13,12 @@ import org.kde.neochat 1.0
|
|||||||
LoginStep {
|
LoginStep {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
onActiveFocusChanged: if (activeFocus) loginButton.forceActiveFocus()
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
FormCard.FormButtonDelegate {
|
FormCard.FormButtonDelegate {
|
||||||
|
id: loginButton
|
||||||
text: i18nc("@action:button", "Login")
|
text: i18nc("@action:button", "Login")
|
||||||
onClicked: root.processed("qrc:/Login.qml")
|
onClicked: root.processed("qrc:/Login.qml")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,6 @@ ColumnLayout {
|
|||||||
/// Show a message in a banner at the top of the page.
|
/// Show a message in a banner at the top of the page.
|
||||||
signal showMessage(string message)
|
signal showMessage(string message)
|
||||||
|
|
||||||
|
/// Clears any error messages currently being shown
|
||||||
signal clearError()
|
signal clearError()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
51
src/qml/Component/Login/RegisterPassword.qml
Normal file
51
src/qml/Component/Login/RegisterPassword.qml
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.12 as QQC2
|
||||||
|
|
||||||
|
import org.kde.kirigami 2.12 as Kirigami
|
||||||
|
import org.kde.kirigamiaddons.formcard 1.0 as FormCard
|
||||||
|
|
||||||
|
import org.kde.neochat 1.0
|
||||||
|
|
||||||
|
LoginStep {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
onActiveFocusChanged: if (activeFocus) passwordField.forceActiveFocus()
|
||||||
|
|
||||||
|
FormCard.FormTextFieldDelegate {
|
||||||
|
id: passwordField
|
||||||
|
label: i18n("Password:")
|
||||||
|
echoMode: TextInput.Password
|
||||||
|
onTextChanged: Registration.password = text
|
||||||
|
Keys.onReturnPressed: {
|
||||||
|
confirmPasswordField.forceActiveFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FormCard.FormTextFieldDelegate {
|
||||||
|
id: confirmPasswordField
|
||||||
|
label: i18n("Confirm Password:")
|
||||||
|
enabled: passwordField.enabled
|
||||||
|
echoMode: TextInput.Password
|
||||||
|
statusMessage: passwordField.text.length === confirmPasswordField.text.length && passwordField.text !== confirmPasswordField.text ? i18n("The passwords do not match.") : ""
|
||||||
|
Keys.onReturnPressed: {
|
||||||
|
if (root.nextAction.enabled) {
|
||||||
|
root.nextAction.trigger()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nextAction: Kirigami.Action {
|
||||||
|
onTriggered: {
|
||||||
|
passwordField.enabled = false
|
||||||
|
Registration.registerAccount()
|
||||||
|
}
|
||||||
|
enabled: passwordField.text === confirmPasswordField.text
|
||||||
|
}
|
||||||
|
|
||||||
|
previousAction: Kirigami.Action {
|
||||||
|
onTriggered: root.processed("qrc:/Username.qml")
|
||||||
|
}
|
||||||
|
}
|
||||||
39
src/qml/Component/Login/Terms.qml
Normal file
39
src/qml/Component/Login/Terms.qml
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.12 as QQC2
|
||||||
|
import QtQuick.Layouts 1.12
|
||||||
|
|
||||||
|
import org.kde.kirigami 2.12 as Kirigami
|
||||||
|
import org.kde.kirigamiaddons.formcard 1.0 as FormCard
|
||||||
|
|
||||||
|
import org.kde.neochat 1.0
|
||||||
|
|
||||||
|
LoginStep {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
noControls: true
|
||||||
|
|
||||||
|
FormCard.FormTextDelegate {
|
||||||
|
text: i18n("Terms & Conditions")
|
||||||
|
description: i18n("By continuing with the registration, you agree to the following terms and conditions:")
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: Registration.terms
|
||||||
|
delegate: FormCard.FormTextDelegate {
|
||||||
|
text: "<a href=\"" + modelData.url + "\">" + modelData.title + "</a>"
|
||||||
|
onLinkActivated: Qt.openUrlExternally(modelData.url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nextAction: Kirigami.Action {
|
||||||
|
onTriggered: {
|
||||||
|
Registration.registerAccount()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
previousAction: Kirigami.Action {
|
||||||
|
onTriggered: root.processed("qrc:/Username.qml")
|
||||||
|
}
|
||||||
|
}
|
||||||
46
src/qml/Component/Login/Username.qml
Normal file
46
src/qml/Component/Login/Username.qml
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.12 as QQC2
|
||||||
|
|
||||||
|
import org.kde.kirigami 2.12 as Kirigami
|
||||||
|
import org.kde.kirigamiaddons.formcard 1.0 as FormCard
|
||||||
|
|
||||||
|
import org.kde.neochat 1.0
|
||||||
|
|
||||||
|
LoginStep {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
onActiveFocusChanged: if (activeFocus) usernameField.forceActiveFocus()
|
||||||
|
|
||||||
|
FormCard.FormTextFieldDelegate {
|
||||||
|
id: usernameField
|
||||||
|
label: i18n("Username:")
|
||||||
|
placeholderText: "user"
|
||||||
|
onTextChanged: timer.restart()
|
||||||
|
statusMessage: Registration.status === Registration.UsernameTaken ? i18n("Username unavailable") : ""
|
||||||
|
Keys.onReturnPressed: {
|
||||||
|
if (root.nextAction.enabled) {
|
||||||
|
root.nextAction.trigger()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: timer
|
||||||
|
interval: 500
|
||||||
|
onTriggered: Registration.username = usernameField.text
|
||||||
|
}
|
||||||
|
|
||||||
|
nextAction: Kirigami.Action {
|
||||||
|
text: Registration.status === Registration.TestingUsername ? i18n("Loading") : null
|
||||||
|
|
||||||
|
onTriggered: root.processed("qrc:/RegisterPassword.qml")
|
||||||
|
enabled: Registration.status === Registration.Ready
|
||||||
|
}
|
||||||
|
|
||||||
|
previousAction: Kirigami.Action {
|
||||||
|
onTriggered: root.processed("qrc:/Homeserver.qml")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -41,7 +41,7 @@ FormCard.FormCardPage {
|
|||||||
|
|
||||||
FormCard.FormTextDelegate {
|
FormCard.FormTextDelegate {
|
||||||
id: welcomeMessage
|
id: welcomeMessage
|
||||||
text: AccountRegistry.accountCount > 0 ? i18n("Log in to a different account.") : i18n("Welcome to NeoChat! Continue by logging in.")
|
text: AccountRegistry.accountCount > 0 ? i18n("Log in to a different account or create a new account.") : i18n("Welcome to NeoChat! Continue by logging in or creating a new account.")
|
||||||
}
|
}
|
||||||
|
|
||||||
FormCard.FormDelegateSeparator {
|
FormCard.FormDelegateSeparator {
|
||||||
@@ -51,9 +51,10 @@ FormCard.FormCardPage {
|
|||||||
Loader {
|
Loader {
|
||||||
id: module
|
id: module
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
source: "qrc:/Login.qml"
|
source: "qrc:/LoginRegister.qml"
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
|
id: stepConnections
|
||||||
target: currentStep
|
target: currentStep
|
||||||
|
|
||||||
function onProcessed(nextUrl) {
|
function onProcessed(nextUrl) {
|
||||||
@@ -76,6 +77,23 @@ FormCard.FormCardPage {
|
|||||||
headerMessage.visible = false;
|
headerMessage.visible = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Connections {
|
||||||
|
target: Registration
|
||||||
|
function onNextStepChanged() {
|
||||||
|
if (Registration.nextStep === "m.login.recaptcha") {
|
||||||
|
stepConnections.onProcessed("qrc:/Captcha.qml")
|
||||||
|
}
|
||||||
|
if (Registration.nextStep === "m.login.terms") {
|
||||||
|
stepConnections.onProcessed("qrc:/Terms.qml")
|
||||||
|
}
|
||||||
|
if (Registration.nextStep === "m.login.email.identity") {
|
||||||
|
stepConnections.onProcessed("qrc:/Email.qml")
|
||||||
|
}
|
||||||
|
if (Registration.nextStep === "loading") {
|
||||||
|
stepConnections.onProcessed("qrc:/Loading.qml")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Connections {
|
Connections {
|
||||||
target: LoginHelper
|
target: LoginHelper
|
||||||
function onErrorOccured(message) {
|
function onErrorOccured(message) {
|
||||||
@@ -92,7 +110,7 @@ FormCard.FormCardPage {
|
|||||||
|
|
||||||
FormCard.FormButtonDelegate {
|
FormCard.FormButtonDelegate {
|
||||||
id: continueButton
|
id: continueButton
|
||||||
text: root.currentStep.nextAction ? root.currentStep.nextAction.text : i18nc("@action:button", "Continue")
|
text: root.currentStep.nextAction && root.currentStep.nextAction.text ? root.currentStep.nextAction.text : i18nc("@action:button", "Continue")
|
||||||
visible: root.currentStep.nextAction
|
visible: root.currentStep.nextAction
|
||||||
onClicked: root.currentStep.nextAction.trigger()
|
onClicked: root.currentStep.nextAction.trigger()
|
||||||
icon.name: "arrow-right"
|
icon.name: "arrow-right"
|
||||||
@@ -111,5 +129,8 @@ FormCard.FormCardPage {
|
|||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
LoginHelper.init()
|
LoginHelper.init()
|
||||||
module.item.forceActiveFocus()
|
module.item.forceActiveFocus()
|
||||||
|
Registration.username = ""
|
||||||
|
Registration.password = ""
|
||||||
|
Registration.email = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
338
src/registration.cpp
Normal file
338
src/registration.cpp
Normal file
@@ -0,0 +1,338 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "registration.h"
|
||||||
|
|
||||||
|
#include <QTcpServer>
|
||||||
|
#include <QTcpSocket>
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
|
#include <Quotient/csapi/registration.h>
|
||||||
|
#include <Quotient/qt_connection_util.h>
|
||||||
|
#include <Quotient/settings.h>
|
||||||
|
|
||||||
|
#include "controller.h"
|
||||||
|
|
||||||
|
#include <KLocalizedString>
|
||||||
|
|
||||||
|
using namespace Quotient;
|
||||||
|
|
||||||
|
Registration::Registration()
|
||||||
|
{
|
||||||
|
auto server = new QTcpServer(this);
|
||||||
|
server->listen(QHostAddress("127.0.0.1"_ls), 20847);
|
||||||
|
connect(server, &QTcpServer::newConnection, this, [=]() {
|
||||||
|
auto conn = server->nextPendingConnection();
|
||||||
|
connect(conn, &QIODevice::readyRead, this, [=]() {
|
||||||
|
auto code =
|
||||||
|
"HTTP/1.0 200\nContent-type: text/html\n\n<html><head><script src=\"https://www.google.com/recaptcha/api.js\" async defer></script></head><body style=\"background: #00000000\"><center><div class=\"g-recaptcha\" data-sitekey=\"%1\"></div></center></body></html>"_ls
|
||||||
|
.arg(m_recaptchaSiteKey);
|
||||||
|
conn->write(code.toLatin1().data(), code.length());
|
||||||
|
conn->close();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(this, &Registration::homeserverChanged, this, &Registration::testHomeserver);
|
||||||
|
connect(this, &Registration::usernameChanged, this, &Registration::testUsername);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Registration::setRecaptchaResponse(const QString &recaptchaResponse)
|
||||||
|
{
|
||||||
|
m_recaptchaResponse = recaptchaResponse;
|
||||||
|
Q_EMIT recaptchaResponseChanged();
|
||||||
|
registerAccount();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Registration::recaptchaResponse() const
|
||||||
|
{
|
||||||
|
return m_recaptchaResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Registration::setRecaptchaSiteKey(const QString &recaptchaSiteKey)
|
||||||
|
{
|
||||||
|
m_recaptchaSiteKey = recaptchaSiteKey;
|
||||||
|
Q_EMIT recaptchaSiteKeyChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Registration::recaptchaSiteKey() const
|
||||||
|
{
|
||||||
|
return m_recaptchaSiteKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Registration::registerAccount()
|
||||||
|
{
|
||||||
|
setStatus(Working);
|
||||||
|
Omittable<QJsonObject> authData = none;
|
||||||
|
if (nextStep() == "m.login.recaptcha"_ls) {
|
||||||
|
authData = QJsonObject{
|
||||||
|
{"type"_ls, "m.login.recaptcha"_ls},
|
||||||
|
{"response"_ls, m_recaptchaResponse},
|
||||||
|
{"session"_ls, m_session},
|
||||||
|
};
|
||||||
|
} else if (nextStep() == "m.login.terms"_ls) {
|
||||||
|
authData = QJsonObject{
|
||||||
|
{"type"_ls, "m.login.terms"_ls},
|
||||||
|
{"session"_ls, m_session},
|
||||||
|
};
|
||||||
|
} else if (nextStep() == "m.login.email.identity"_ls) {
|
||||||
|
authData = QJsonObject{
|
||||||
|
{"type"_ls, "m.login.email.identity"_ls},
|
||||||
|
{"threepid_creds"_ls,
|
||||||
|
QJsonObject{
|
||||||
|
{"sid"_ls, m_sid},
|
||||||
|
{"client_secret"_ls, m_emailSecret},
|
||||||
|
}},
|
||||||
|
{"session"_ls, m_session},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
auto job = m_connection->callApi<NeoChatRegisterJob>("user"_ls, authData, m_username, m_password, QString(), QString(), true);
|
||||||
|
connect(job, &BaseJob::result, this, [=]() {
|
||||||
|
if (job->status() == BaseJob::Success) {
|
||||||
|
setNextStep("loading"_ls);
|
||||||
|
auto connection = new Connection(this);
|
||||||
|
auto matrixId = "@%1:%2"_ls.arg(m_username, m_homeserver);
|
||||||
|
connection->resolveServer(matrixId);
|
||||||
|
|
||||||
|
auto displayName = "NeoChat %1 %2 %3 %4"_ls.arg(QSysInfo::machineHostName(),
|
||||||
|
QSysInfo::productType(),
|
||||||
|
QSysInfo::productVersion(),
|
||||||
|
QSysInfo::currentCpuArchitecture());
|
||||||
|
connection->loginWithPassword(matrixId, m_password, displayName);
|
||||||
|
|
||||||
|
connect(connection, &Connection::connected, this, [this, displayName, connection] {
|
||||||
|
AccountSettings account(connection->userId());
|
||||||
|
account.setKeepLoggedIn(true);
|
||||||
|
account.setHomeserver(connection->homeserver());
|
||||||
|
account.setDeviceId(connection->deviceId());
|
||||||
|
account.setDeviceName(displayName);
|
||||||
|
if (!Controller::instance().saveAccessTokenToKeyChain(account, connection->accessToken())) {
|
||||||
|
qWarning() << "Couldn't save access token";
|
||||||
|
}
|
||||||
|
account.sync();
|
||||||
|
Controller::instance().addConnection(connection);
|
||||||
|
Controller::instance().setActiveConnection(connection);
|
||||||
|
connectSingleShot(connection, &Connection::syncDone, this, []() {
|
||||||
|
Q_EMIT Controller::instance().initiated();
|
||||||
|
});
|
||||||
|
m_connection = nullptr;
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto &data = job->jsonData();
|
||||||
|
m_session = data["session"_ls].toString();
|
||||||
|
const auto ¶ms = data["params"_ls].toObject();
|
||||||
|
|
||||||
|
// I'm not motivated enough to figure out how we should handle the flow stuff, so:
|
||||||
|
// If there is a flow that requires e-mail, we use that, to make sure that the user can recover the account from a forgotten password.
|
||||||
|
// Otherwise, we're using the first flow.
|
||||||
|
auto selectedFlow = data["flows"_ls].toArray()[0].toObject()["stages"_ls].toArray();
|
||||||
|
for (const auto &flow : data["flows"_ls].toArray()) {
|
||||||
|
if (flow.toObject()["stages"_ls].toArray().contains("m.login.email.identity"_ls)) {
|
||||||
|
selectedFlow = flow.toObject()["stages"_ls].toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setNextStep(selectedFlow[data["completed"_ls].toArray().size()].toString());
|
||||||
|
m_recaptchaSiteKey = params["m.login.recaptcha"_ls]["public_key"_ls].toString();
|
||||||
|
Q_EMIT recaptchaSiteKeyChanged();
|
||||||
|
m_terms.clear();
|
||||||
|
for (const auto &term : params["m.login.terms"_ls]["policies"_ls].toObject().keys()) {
|
||||||
|
QVariantMap termData;
|
||||||
|
termData["title"_ls] = params["m.login.terms"_ls]["policies"_ls][term]["en"_ls]["name"_ls].toString();
|
||||||
|
termData["url"_ls] = params["m.login.terms"_ls]["policies"_ls][term]["en"_ls]["url"_ls].toString();
|
||||||
|
m_terms += termData;
|
||||||
|
Q_EMIT termsChanged();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Registration::homeserver() const
|
||||||
|
{
|
||||||
|
return m_homeserver;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Registration::setHomeserver(const QString &url)
|
||||||
|
{
|
||||||
|
m_homeserver = url;
|
||||||
|
Q_EMIT homeserverChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Registration::testHomeserver()
|
||||||
|
{
|
||||||
|
if (m_homeserver.isEmpty()) {
|
||||||
|
setStatus(NoServer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setStatus(TestingHomeserver);
|
||||||
|
if (m_connection) {
|
||||||
|
delete m_connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_connection = new Connection(this);
|
||||||
|
m_connection->resolveServer("@user:%1"_ls.arg(m_homeserver));
|
||||||
|
connectSingleShot(m_connection.data(), &Connection::loginFlowsChanged, this, [this]() {
|
||||||
|
if (m_testServerJob) {
|
||||||
|
delete m_testServerJob;
|
||||||
|
}
|
||||||
|
m_testServerJob = m_connection->callApi<NeoChatRegisterJob>("user"_ls, none, "user"_ls, QString(), QString(), QString(), false);
|
||||||
|
connect(m_testServerJob.data(), &BaseJob::finished, this, [this]() {
|
||||||
|
if (m_testServerJob->error() == BaseJob::StatusCode::ContentAccessError) {
|
||||||
|
setStatus(ServerNoRegistration);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (m_testServerJob->status().code != 106) {
|
||||||
|
setStatus(InvalidServer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!m_username.isEmpty()) {
|
||||||
|
setStatus(TestingUsername);
|
||||||
|
testUsername();
|
||||||
|
} else {
|
||||||
|
setStatus(NoUsername);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Registration::setUsername(const QString &username)
|
||||||
|
{
|
||||||
|
m_username = username;
|
||||||
|
Q_EMIT usernameChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Registration::username() const
|
||||||
|
{
|
||||||
|
return m_username;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Registration::testUsername()
|
||||||
|
{
|
||||||
|
if (status() <= ServerNoRegistration) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setStatus(TestingUsername);
|
||||||
|
if (m_usernameJob) {
|
||||||
|
m_usernameJob->abandon();
|
||||||
|
}
|
||||||
|
if (m_username.isEmpty()) {
|
||||||
|
setStatus(NoUsername);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_usernameJob = m_connection->callApi<CheckUsernameAvailabilityJob>(m_username);
|
||||||
|
connect(m_usernameJob, &BaseJob::result, this, [this]() {
|
||||||
|
setStatus(m_usernameJob->error() == BaseJob::StatusCode::Success && *m_usernameJob->available() ? Ready : UsernameTaken);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<QVariantMap> Registration::terms() const
|
||||||
|
{
|
||||||
|
return m_terms;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Registration::password() const
|
||||||
|
{
|
||||||
|
return m_password;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Registration::setPassword(const QString &password)
|
||||||
|
{
|
||||||
|
m_password = password;
|
||||||
|
Q_EMIT passwordChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
NeoChatRegisterJob::NeoChatRegisterJob(const QString &kind,
|
||||||
|
const Omittable<QJsonObject> &auth,
|
||||||
|
const QString &username,
|
||||||
|
const QString &password,
|
||||||
|
const QString &deviceId,
|
||||||
|
const QString &initialDeviceDisplayName,
|
||||||
|
Omittable<bool> inhibitLogin)
|
||||||
|
: BaseJob(HttpVerb::Post, "RegisterJob"_ls, QByteArrayLiteral("/_matrix/client/r0/register"), false)
|
||||||
|
{
|
||||||
|
QJsonObject _data;
|
||||||
|
if (auth) {
|
||||||
|
addParam<>(_data, "auth"_ls, auth);
|
||||||
|
}
|
||||||
|
addParam<>(_data, "username"_ls, username);
|
||||||
|
addParam<IfNotEmpty>(_data, "password"_ls, password);
|
||||||
|
addParam<IfNotEmpty>(_data, "device_id"_ls, deviceId);
|
||||||
|
addParam<IfNotEmpty>(_data, "initial_device_display_name"_ls, initialDeviceDisplayName);
|
||||||
|
addParam<IfNotEmpty>(_data, "inhibit_login"_ls, inhibitLogin);
|
||||||
|
addParam<IfNotEmpty>(_data, "kind"_ls, kind);
|
||||||
|
addParam<IfNotEmpty>(_data, "refresh_token"_ls, false);
|
||||||
|
setRequestData(_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Registration::email() const
|
||||||
|
{
|
||||||
|
return m_email;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Registration::setEmail(const QString &email)
|
||||||
|
{
|
||||||
|
m_email = email;
|
||||||
|
Q_EMIT emailChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Registration::nextStep() const
|
||||||
|
{
|
||||||
|
return m_nextStep;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Registration::setNextStep(const QString &nextStep)
|
||||||
|
{
|
||||||
|
m_nextStep = nextStep;
|
||||||
|
Q_EMIT nextStepChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
Registration::Status Registration::status() const
|
||||||
|
{
|
||||||
|
return m_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Registration::statusString() const
|
||||||
|
{
|
||||||
|
switch (m_status) {
|
||||||
|
case NoServer:
|
||||||
|
return i18n("No server.");
|
||||||
|
case TestingHomeserver:
|
||||||
|
return i18n("Checking Server availability.");
|
||||||
|
case InvalidServer:
|
||||||
|
return i18n("This is not a valid server.");
|
||||||
|
case ServerNoRegistration:
|
||||||
|
return i18n("Regisration for this server is disabled.");
|
||||||
|
case NoUsername:
|
||||||
|
return i18n("No username.");
|
||||||
|
case TestingUsername:
|
||||||
|
return i18n("Checking username availability.");
|
||||||
|
case UsernameTaken:
|
||||||
|
return i18n("This username is not available.");
|
||||||
|
case Ready:
|
||||||
|
return i18n("Continue");
|
||||||
|
case Working:
|
||||||
|
return i18n("Working");
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void Registration::setStatus(Status status)
|
||||||
|
{
|
||||||
|
m_status = status;
|
||||||
|
Q_EMIT statusChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Registration::registerEmail()
|
||||||
|
{
|
||||||
|
m_emailSecret = QString::fromLatin1(QUuid::createUuid().toString().toLatin1().toBase64());
|
||||||
|
EmailValidationData data;
|
||||||
|
data.email = m_email;
|
||||||
|
data.clientSecret = m_emailSecret;
|
||||||
|
data.sendAttempt = 0;
|
||||||
|
|
||||||
|
auto job = m_connection->callApi<RequestTokenToRegisterEmailJob>(data);
|
||||||
|
connect(job, &BaseJob::finished, this, [=]() {
|
||||||
|
m_sid = job->jsonData()["sid"_ls].toString();
|
||||||
|
});
|
||||||
|
}
|
||||||
163
src/registration.h
Normal file
163
src/registration.h
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QPointer>
|
||||||
|
#include <QString>
|
||||||
|
#include <QVariantMap>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
#include <Quotient/csapi/registration.h>
|
||||||
|
#include <Task>
|
||||||
|
|
||||||
|
#include <Quotient/connection.h>
|
||||||
|
|
||||||
|
#include <Quotient/jobs/basejob.h>
|
||||||
|
#include <Quotient/util.h>
|
||||||
|
|
||||||
|
namespace Quotient
|
||||||
|
{
|
||||||
|
class Connection;
|
||||||
|
class CheckUsernameAvailabilityJob;
|
||||||
|
}
|
||||||
|
|
||||||
|
class NeoChatRegisterJob : public Quotient::BaseJob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit NeoChatRegisterJob(const QString &kind = QStringLiteral("user"),
|
||||||
|
const Quotient::Omittable<QJsonObject> &auth = Quotient::none,
|
||||||
|
const QString &username = {},
|
||||||
|
const QString &password = {},
|
||||||
|
const QString &deviceId = {},
|
||||||
|
const QString &initialDeviceDisplayName = {},
|
||||||
|
Quotient::Omittable<bool> inhibitLogin = Quotient::none);
|
||||||
|
|
||||||
|
QString userId() const
|
||||||
|
{
|
||||||
|
return loadFromJson<QString>(QStringLiteral("user_id"));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString accessToken() const
|
||||||
|
{
|
||||||
|
return loadFromJson<QString>(QStringLiteral("access_token"));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString homeServer() const
|
||||||
|
{
|
||||||
|
return loadFromJson<QString>(QStringLiteral("home_server"));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString deviceId() const
|
||||||
|
{
|
||||||
|
return loadFromJson<QString>(QStringLiteral("device_id"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Registration : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(QString homeserver READ homeserver WRITE setHomeserver NOTIFY homeserverChanged)
|
||||||
|
Q_PROPERTY(QString username READ username WRITE setUsername NOTIFY usernameChanged)
|
||||||
|
Q_PROPERTY(QString recaptchaSiteKey READ recaptchaSiteKey WRITE setRecaptchaSiteKey NOTIFY recaptchaSiteKeyChanged)
|
||||||
|
Q_PROPERTY(QString recaptchaResponse READ recaptchaResponse WRITE setRecaptchaResponse NOTIFY recaptchaResponseChanged)
|
||||||
|
Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged)
|
||||||
|
Q_PROPERTY(QString email READ email WRITE setEmail NOTIFY emailChanged)
|
||||||
|
Q_PROPERTY(QString nextStep READ nextStep WRITE setNextStep NOTIFY nextStepChanged)
|
||||||
|
Q_PROPERTY(QVector<QVariantMap> terms READ terms NOTIFY termsChanged)
|
||||||
|
Q_PROPERTY(Status status READ status NOTIFY statusChanged)
|
||||||
|
Q_PROPERTY(QString statusString READ statusString NOTIFY statusChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum Status {
|
||||||
|
NoServer,
|
||||||
|
TestingHomeserver,
|
||||||
|
InvalidServer,
|
||||||
|
ServerNoRegistration,
|
||||||
|
NoUsername,
|
||||||
|
TestingUsername,
|
||||||
|
UsernameTaken,
|
||||||
|
Ready,
|
||||||
|
Working,
|
||||||
|
};
|
||||||
|
Q_ENUM(Status);
|
||||||
|
static Registration &instance()
|
||||||
|
{
|
||||||
|
static Registration _instance;
|
||||||
|
return _instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_INVOKABLE void registerAccount();
|
||||||
|
Q_INVOKABLE void registerEmail();
|
||||||
|
|
||||||
|
void setRecaptchaSiteKey(const QString &recaptchaSiteKey);
|
||||||
|
QString recaptchaSiteKey() const;
|
||||||
|
|
||||||
|
void setRecaptchaResponse(const QString &response);
|
||||||
|
QString recaptchaResponse() const;
|
||||||
|
|
||||||
|
void setHomeserver(const QString &url);
|
||||||
|
QString homeserver() const;
|
||||||
|
|
||||||
|
QString username() const;
|
||||||
|
void setUsername(const QString &username);
|
||||||
|
|
||||||
|
QString password() const;
|
||||||
|
void setPassword(const QString &password);
|
||||||
|
|
||||||
|
[[nodiscard]] QString email() const;
|
||||||
|
void setEmail(const QString &email);
|
||||||
|
|
||||||
|
QString nextStep() const;
|
||||||
|
void setNextStep(const QString &nextStep);
|
||||||
|
|
||||||
|
QVector<QVariantMap> terms() const;
|
||||||
|
|
||||||
|
Status status() const;
|
||||||
|
QString statusString() const;
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void recaptchaSiteKeyChanged();
|
||||||
|
void recaptchaResponseChanged();
|
||||||
|
void homeserverChanged();
|
||||||
|
void homeserverAvailableChanged();
|
||||||
|
void testingChanged();
|
||||||
|
void usernameChanged();
|
||||||
|
void usernameAvailableChanged();
|
||||||
|
void testingUsernameChanged();
|
||||||
|
void flowsChanged();
|
||||||
|
void termsChanged();
|
||||||
|
void passwordChanged();
|
||||||
|
void emailChanged();
|
||||||
|
void nextStepChanged();
|
||||||
|
void statusChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_recaptchaSiteKey;
|
||||||
|
QString m_recaptchaResponse;
|
||||||
|
QString m_homeserver;
|
||||||
|
QString m_username;
|
||||||
|
QString m_password;
|
||||||
|
QVector<QVariantMap> m_terms;
|
||||||
|
QString m_email;
|
||||||
|
Status m_status = NoServer;
|
||||||
|
QString m_nextStep;
|
||||||
|
QString m_session;
|
||||||
|
QString m_sid;
|
||||||
|
QString m_emailSecret;
|
||||||
|
|
||||||
|
QPointer<Quotient::CheckUsernameAvailabilityJob> m_usernameJob;
|
||||||
|
QPointer<NeoChatRegisterJob> m_testServerJob;
|
||||||
|
QVector<QVector<QString>> m_flows;
|
||||||
|
QPointer<Quotient::Connection> m_connection;
|
||||||
|
|
||||||
|
void testHomeserver();
|
||||||
|
void testUsername();
|
||||||
|
QCoro::Task<void> loadFlows();
|
||||||
|
void setStatus(Status status);
|
||||||
|
|
||||||
|
Registration();
|
||||||
|
};
|
||||||
@@ -65,10 +65,15 @@
|
|||||||
<file alias="AvatarFlow.qml">qml/Component/Timeline/AvatarFlow.qml</file>
|
<file alias="AvatarFlow.qml">qml/Component/Timeline/AvatarFlow.qml</file>
|
||||||
<file alias="LoginStep.qml">qml/Component/Login/LoginStep.qml</file>
|
<file alias="LoginStep.qml">qml/Component/Login/LoginStep.qml</file>
|
||||||
<file alias="Login.qml">qml/Component/Login/Login.qml</file>
|
<file alias="Login.qml">qml/Component/Login/Login.qml</file>
|
||||||
|
<file alias="Homeserver.qml">qml/Component/Login/Homeserver.qml</file>
|
||||||
|
<file alias="Username.qml">qml/Component/Login/Username.qml</file>
|
||||||
|
<file alias="RegisterPassword.qml">qml/Component/Login/RegisterPassword.qml</file>
|
||||||
|
<file alias="Captcha.qml">qml/Component/Login/Captcha.qml</file>
|
||||||
|
<file alias="Terms.qml">qml/Component/Login/Terms.qml</file>
|
||||||
|
<file alias="Email.qml">qml/Component/Login/Email.qml</file>
|
||||||
<file alias="Password.qml">qml/Component/Login/Password.qml</file>
|
<file alias="Password.qml">qml/Component/Login/Password.qml</file>
|
||||||
<file alias="LoginRegister.qml">qml/Component/Login/LoginRegister.qml</file>
|
<file alias="LoginRegister.qml">qml/Component/Login/LoginRegister.qml</file>
|
||||||
<file alias="Loading.qml">qml/Component/Login/Loading.qml</file>
|
<file alias="Loading.qml">qml/Component/Login/Loading.qml</file>
|
||||||
<file alias="Homeserver.qml">qml/Component/Login/Homeserver.qml</file>
|
|
||||||
<file alias="LoginMethod.qml">qml/Component/Login/LoginMethod.qml</file>
|
<file alias="LoginMethod.qml">qml/Component/Login/LoginMethod.qml</file>
|
||||||
<file alias="Sso.qml">qml/Component/Login/Sso.qml</file>
|
<file alias="Sso.qml">qml/Component/Login/Sso.qml</file>
|
||||||
<file alias="UserDetailDialog.qml">qml/Dialog/UserDetailDialog.qml</file>
|
<file alias="UserDetailDialog.qml">qml/Dialog/UserDetailDialog.qml</file>
|
||||||
|
|||||||
Reference in New Issue
Block a user