Use consistent fields for JIDs and passwords and improve login page

* Validate entered JID and password
* Add show/hide button for password
* Disable connect button if JID or password are invalid
* Focus JID field after loading login page
* Focus field with invalid input if connect button is clicked or enter key pressed
parent 0d187ae1
Pipeline #13511 passed with stages
in 23 minutes and 32 seconds
......@@ -37,6 +37,7 @@ set(KAIDAN_SOURCES
src/CameraImageCapture.cpp
src/MediaUtils.cpp
src/MediaRecorder.cpp
src/CredentialsValidator.cpp
# needed to trigger moc generation / to be displayed in IDEs
src/Enums.h
......
/*
* Kaidan - A user-friendly XMPP client for every device!
*
* Copyright (C) 2016-2020 Kaidan developers and contributors
* (see the LICENSE file for a full list of copyright authors)
*
* Kaidan is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, as a special exception, the author of Kaidan gives
* permission to link the code of its release with the OpenSSL
* project's "OpenSSL" library (or with modified versions of it that
* use the same license as the "OpenSSL" library), and distribute the
* linked executables. You must obey the GNU General Public License in
* all respects for all of the code used other than "OpenSSL". If you
* modify this file, you may extend this exception to your version of
* the file, but you are not obligated to do so. If you do not wish to
* do so, delete this exception statement from your version.
*
* Kaidan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Kaidan. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CredentialsValidator.h"
#include <QXmppUtils.h>
CredentialsValidator::CredentialsValidator(QObject *parent)
: QObject(parent)
{
}
bool CredentialsValidator::isJidValid(const QString &jid)
{
if (jid.split("@").size() == 2) {
QString username = QXmppUtils::jidToUser(jid);
QString server = QXmppUtils::jidToDomain(jid);
if (isUsernameValid(username) && isServerValid(server))
return true;
}
return false;
}
bool CredentialsValidator::isUsernameValid(const QString &username)
{
return !(username.isEmpty() || username.contains(" "));
}
bool CredentialsValidator::isServerValid(const QString &server)
{
return !(server.isEmpty() || server.contains(" "));
}
bool CredentialsValidator::isPasswordValid(const QString &password)
{
return !password.isEmpty();
}
/*
* Kaidan - A user-friendly XMPP client for every device!
*
* Copyright (C) 2016-2020 Kaidan developers and contributors
* (see the LICENSE file for a full list of copyright authors)
*
* Kaidan is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, as a special exception, the author of Kaidan gives
* permission to link the code of its release with the OpenSSL
* project's "OpenSSL" library (or with modified versions of it that
* use the same license as the "OpenSSL" library), and distribute the
* linked executables. You must obey the GNU General Public License in
* all respects for all of the code used other than "OpenSSL". If you
* modify this file, you may extend this exception to your version of
* the file, but you are not obligated to do so. If you do not wish to
* do so, delete this exception statement from your version.
*
* Kaidan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Kaidan. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CREDENTIALSVALIDATOR_H
#define CREDENTIALSVALIDATOR_H
#include <QObject>
/**
* This is a validator for XMPP account credentials.
*/
class CredentialsValidator : public QObject
{
Q_OBJECT
public:
CredentialsValidator(QObject *parent = nullptr);
/**
* Returns true if the given string is a valid JID.
*
* @param jid JID to be validated
*/
Q_INVOKABLE static bool isJidValid(const QString &jid);
/**
* Returns true if the given string is a valid username.
*
* @param username username to be validated
*/
Q_INVOKABLE static bool isUsernameValid(const QString &username);
/**
* Returns true if the given string is a valid server.
*
* @param username server to be validated
*/
Q_INVOKABLE static bool isServerValid(const QString &server);
/**
* Returns true if the given string is a valid password.
*
* @param password password to be validated
*/
Q_INVOKABLE static bool isPasswordValid(const QString &password);
};
#endif // CREDENTIALSVALIDATOR_H
......@@ -68,6 +68,7 @@
#include "MediaSettingModel.h"
#include "MediaUtils.h"
#include "MediaRecorder.h"
#include "CredentialsValidator.h"
#ifdef STATIC_BUILD
#include "static_plugins.h"
......@@ -229,6 +230,7 @@ Q_DECL_EXPORT int main(int argc, char *argv[])
qRegisterMetaType<ImageEncoderSettings>();
qRegisterMetaType<AudioEncoderSettings>();
qRegisterMetaType<VideoEncoderSettings>();
qRegisterMetaType<CredentialsValidator*>("CredentialsValidator*");
// Enums for c++ member calls using enums
qRegisterMetaType<Enums::ConnectionState>();
......@@ -375,6 +377,10 @@ Q_DECL_EXPORT int main(int argc, char *argv[])
return static_cast<QObject*>(QmlUtils::instance());
});
qmlRegisterSingletonType<CredentialsValidator>(APPLICATION_ID, 1, 0, "CredentialsValidator", [] (QQmlEngine *, QJSEngine *) {
return static_cast<QObject*>(new CredentialsValidator(qApp));
});
engine.rootContext()->setContextProperty("kaidan", &kaidan);
engine.load(QUrl("qrc:/qml/main.qml"));
if (engine.rootObjects().isEmpty())
......
......@@ -36,6 +36,7 @@ import org.kde.kirigami 2.8 as Kirigami
import im.kaidan.kaidan 1.0
import "elements"
import "elements/fields"
Kirigami.Page {
title: qsTr("Log in")
......@@ -66,31 +67,24 @@ Kirigami.Page {
Layout.alignment: Qt.AlignCenter
Layout.maximumWidth: Kirigami.Units.gridUnit * 25
// JID field
Controls.Label {
id: jidLabel
text: qsTr("Your Jabber-ID:")
}
Controls.TextField {
JidField {
id: jidField
text: kaidan.jid
placeholderText: qsTr("user@example.org")
Layout.fillWidth: true
selectByMouse: true
inputMethodHints: Qt.ImhEmailCharactersOnly
}
// Password field
Controls.Label {
text: qsTr("Your Password:")
// Simulate the pressing of the connect button.
inputField {
onAccepted: connectButton.clicked()
}
}
Controls.TextField {
id: passField
text: kaidan.password
echoMode: TextInput.Password
selectByMouse: true
Layout.fillWidth: true
// password field
PasswordField {
id: passwordField
// Simulate the pressing of the connect button.
inputField {
onAccepted: connectButton.clicked()
}
}
// Connect button
......@@ -112,21 +106,20 @@ Kirigami.Page {
}
]
// Connect to the server and authenticate by the entered credentials if the JID is valid and a password entered.
onClicked: {
// connect to given account data
kaidan.jid = jidField.text
kaidan.password = passField.text
kaidan.mainConnect()
}
}
// connect when return was pressed
Keys.onPressed: {
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
if (jidField.activeFocus)
passField.forceActiveFocus()
else
connectButton.clicked()
// If the JID is invalid, focus its field.
if (!jidField.valid) {
jidField.forceFocus()
// If the password is invalid, focus its field.
// This also implies that if the JID field is focused and the password invalid, the password field will be focused instead of immediately trying to connect.
} else if (!passwordField.valid) {
passwordField.forceFocus()
} else {
kaidan.jid = jidField.text
kaidan.password = passwordField.text
kaidan.mainConnect()
}
}
}
}
......@@ -160,6 +153,7 @@ Kirigami.Page {
Component.onCompleted: {
kaidan.connectionErrorChanged.connect(handleConnectionError)
jidField.forceFocus()
}
Component.onDestruction: {
......
/*
* Kaidan - A user-friendly XMPP client for every device!
*
* Copyright (C) 2016-2020 Kaidan developers and contributors
* (see the LICENSE file for a full list of copyright authors)
*
* Kaidan is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, as a special exception, the author of Kaidan gives
* permission to link the code of its release with the OpenSSL
* project's "OpenSSL" library (or with modified versions of it that
* use the same license as the "OpenSSL" library), and distribute the
* linked executables. You must obey the GNU General Public License in
* all respects for all of the code used other than "OpenSSL". If you
* modify this file, you may extend this exception to your version of
* the file, but you are not obligated to do so. If you do not wish to
* do so, delete this exception statement from your version.
*
* Kaidan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Kaidan. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.10
import QtQuick.Controls 2.4 as Controls
import QtQuick.Layouts 1.10
import org.kde.kirigami 2.8 as Kirigami
/**
* This is a text field which can be focused and show a hint for invalid input.
*/
ColumnLayout {
// text of the label for the input field
property alias labelText: label.text
// input field
property alias inputField: inputField
// placeholder text for the input field
property alias placeholderText: inputField.placeholderText
// input method hints for the input field
property alias inputMethodHints: inputField.inputMethodHints
// entered text
property alias text: inputField.text
// text to be shown as a hint if the entered text is not valid
property alias invalidHintText: invalidHint.text
// validity of the entered text
property bool valid: true
// requirement for showing the hint for invalid input
property bool invalidHintMayBeShown: false
// label for the input field
Controls.Label {
id: label
}
RowLayout {
// input field
Kirigami.ActionTextField {
id: inputField
Layout.fillWidth: true
selectByMouse: true
// Show a hint for the first time if the entered text is not valid as soon as the input field loses the focus.
onFocusChanged: {
if (!focus && !invalidHintMayBeShown) {
invalidHintMayBeShown = true
toggleHintForInvalidText()
}
}
}
// icon for an invalid input
Kirigami.Icon {
id: invalidIcon
visible: invalidHint.visible
source: "error"
width: Kirigami.Units.iconSizes.smallMedium
height: width
}
}
// hint for entering a valid input
Controls.Label {
id: invalidHint
visible: false
Layout.fillWidth: true
leftPadding: 5
rightPadding: 5
wrapMode: Text.Wrap
color: Kirigami.Theme.neutralTextColor
}
/**
* Shows a hint if the entered text is not valid or hides it otherwise.
* If invalidHintMayBeShown was initially set to false, that is only done if the input field has lost the focus at least one time because of its onFocusChanged().
*/
function toggleHintForInvalidText() {
if (valid)
invalidHint.visible = false
else if (invalidHintMayBeShown && invalidHintText.length > 0)
invalidHint.visible = true
}
/**
* Focuses the input field.
* If the input field is already focused, the focusing is executed again to trigger its onFocusChanged().
*/
function forceFocus() {
if (inputField.focus)
inputField.focus = false
inputField.forceActiveFocus()
}
}
/*
* Kaidan - A user-friendly XMPP client for every device!
*
* Copyright (C) 2016-2020 Kaidan developers and contributors
* (see the LICENSE file for a full list of copyright authors)
*
* Kaidan is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, as a special exception, the author of Kaidan gives
* permission to link the code of its release with the OpenSSL
* project's "OpenSSL" library (or with modified versions of it that
* use the same license as the "OpenSSL" library), and distribute the
* linked executables. You must obey the GNU General Public License in
* all respects for all of the code used other than "OpenSSL". If you
* modify this file, you may extend this exception to your version of
* the file, but you are not obligated to do so. If you do not wish to
* do so, delete this exception statement from your version.
*
* Kaidan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Kaidan. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.10
import im.kaidan.kaidan 1.0
/**
* This is a JID field with a hint for invalid JIDs.
*/
Field {
labelText: qsTr("Chat address")
placeholderText: qsTr("user@example.org")
inputMethodHints: Qt.ImhEmailCharactersOnly
invalidHintText: qsTr("The chat address must have the form <b>username@server</b>")
valid: false
// Validate the entered JID and show a hint if it is not valid.
onTextChanged: {
if (CredentialsValidator.isJidValid(text))
valid = true
else
valid = false
toggleHintForInvalidText()
}
}
/*
* Kaidan - A user-friendly XMPP client for every device!
*
* Copyright (C) 2016-2020 Kaidan developers and contributors
* (see the LICENSE file for a full list of copyright authors)
*
* Kaidan is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, as a special exception, the author of Kaidan gives
* permission to link the code of its release with the OpenSSL
* project's "OpenSSL" library (or with modified versions of it that
* use the same license as the "OpenSSL" library), and distribute the
* linked executables. You must obey the GNU General Public License in
* all respects for all of the code used other than "OpenSSL". If you
* modify this file, you may extend this exception to your version of
* the file, but you are not obligated to do so. If you do not wish to
* do so, delete this exception statement from your version.
*
* Kaidan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Kaidan. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.10
import org.kde.kirigami 2.8 as Kirigami
import im.kaidan.kaidan 1.0
/**
* This is a password field with a hint for empty passwords and an option for showing the password.
*/
Field {
labelText: qsTr("Password")
invalidHintText: qsTr("Please enter a valid password")
valid: false
// indicator for showing the hidden password
property bool showPassword: false
// Add a button for showing and hiding the entered password.
inputField {
echoMode: showPassword ? TextInput.Normal : TextInput.Password
rightActions: [
Kirigami.Action {
iconName: showPassword ? "password-show-on" : "password-show-off"
onTriggered: showPassword = !showPassword
}
]
}
// Validate the entered password and show a hint if it is not valid.
onTextChanged: {
if (CredentialsValidator.isPasswordValid(text))
valid = true
else
valid = false
toggleHintForInvalidText()
}
}
......@@ -46,6 +46,10 @@
<file>elements/TextAvatar.qml</file>
<file>elements/Avatar.qml</file>
<file>elements/fields/Field.qml</file>
<file>elements/fields/JidField.qml</file>
<file>elements/fields/PasswordField.qml</file>
<file>settings/SettingsItem.qml</file>
<file>settings/SettingsPage.qml</file>
<file>settings/SettingsSheet.qml</file>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment