Improve setup wizard
This commit is contained in:
parent
2ad82d80a6
commit
424547333f
@ -271,6 +271,7 @@ void JsonRpcClient::processCreateUser(const QVariantMap &data)
|
|||||||
{
|
{
|
||||||
qDebug() << "create user response:" << data;
|
qDebug() << "create user response:" << data;
|
||||||
if (data.value("status").toString() == "success" && data.value("params").toMap().value("error").toString() == "UserErrorNoError") {
|
if (data.value("status").toString() == "success" && data.value("params").toMap().value("error").toString() == "UserErrorNoError") {
|
||||||
|
emit createUserSucceeded();
|
||||||
m_initialSetupRequired = false;
|
m_initialSetupRequired = false;
|
||||||
emit initialSetupRequiredChanged();
|
emit initialSetupRequiredChanged();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -93,6 +93,7 @@ signals:
|
|||||||
void invalidProtocolVersion(const QString &actualVersion, const QString &minimumVersion);
|
void invalidProtocolVersion(const QString &actualVersion, const QString &minimumVersion);
|
||||||
void authenticationFailed();
|
void authenticationFailed();
|
||||||
void pushButtonAuthFailed();
|
void pushButtonAuthFailed();
|
||||||
|
void createUserSucceeded();
|
||||||
void createUserFailed(const QString &error);
|
void createUserFailed(const QString &error);
|
||||||
void cloudConnectionStateChanged();
|
void cloudConnectionStateChanged();
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
<qresource prefix="/">
|
<qresource prefix="/">
|
||||||
<file>ui/Nymea.qml</file>
|
<file>ui/Nymea.qml</file>
|
||||||
<file>ui/SettingsPage.qml</file>
|
<file>ui/SettingsPage.qml</file>
|
||||||
<file>ui/LoginPage.qml</file>
|
<file>ui/connection/LoginPage.qml</file>
|
||||||
<file>ui/MagicPage.qml</file>
|
<file>ui/MagicPage.qml</file>
|
||||||
<file>ui/PushButtonAuthPage.qml</file>
|
<file>ui/PushButtonAuthPage.qml</file>
|
||||||
<file>ui/KeyboardLoader.qml</file>
|
<file>ui/KeyboardLoader.qml</file>
|
||||||
@ -182,5 +182,6 @@
|
|||||||
<file>ui/magic/SelectStatePage.qml</file>
|
<file>ui/magic/SelectStatePage.qml</file>
|
||||||
<file>ui/system/SystemUpdatePage.qml</file>
|
<file>ui/system/SystemUpdatePage.qml</file>
|
||||||
<file>ui/components/UpdateRunningOverlay.qml</file>
|
<file>ui/components/UpdateRunningOverlay.qml</file>
|
||||||
|
<file>ui/connection/SetupWizard.qml</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|||||||
@ -1,24 +1,32 @@
|
|||||||
import QtQuick 2.0
|
import QtQuick 2.4
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
implicitHeight: childrenRect.height
|
implicitHeight: active ? childrenRect.height : 0
|
||||||
|
property bool active: d.kbd && d.kbd.active
|
||||||
|
|
||||||
|
Behavior on implicitHeight { NumberAnimation { duration: 130; easing.type: Easing.InOutQuad } }
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: d
|
||||||
|
property var kbd: null
|
||||||
|
property string virtualKeyboardString:
|
||||||
|
'
|
||||||
|
import QtQuick 2.8;
|
||||||
|
import QtQuick.VirtualKeyboard 2.1
|
||||||
|
InputPanel {
|
||||||
|
id: inputPanel
|
||||||
|
y: Qt.inputMethod.visible ? parent.height - inputPanel.height : parent.height
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
}
|
||||||
|
'
|
||||||
|
}
|
||||||
|
|
||||||
property string virtualKeyboardString:
|
|
||||||
'
|
|
||||||
import QtQuick 2.8;
|
|
||||||
import QtQuick.VirtualKeyboard 2.1
|
|
||||||
InputPanel {
|
|
||||||
id: inputPanel
|
|
||||||
y: Qt.inputMethod.visible ? parent.height - inputPanel.height : parent.height
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
}
|
|
||||||
'
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (useVirtualKeyboard) {
|
if (useVirtualKeyboard) {
|
||||||
var kbd = Qt.createQmlObject(virtualKeyboardString, root);
|
d.kbd = Qt.createQmlObject(d.virtualKeyboardString, root);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,141 +0,0 @@
|
|||||||
import QtQuick 2.5
|
|
||||||
import QtQuick.Controls 2.1
|
|
||||||
import QtQuick.Layouts 1.1
|
|
||||||
import Nymea 1.0
|
|
||||||
import "components"
|
|
||||||
|
|
||||||
Page {
|
|
||||||
id: root
|
|
||||||
signal backPressed();
|
|
||||||
|
|
||||||
header: GuhHeader {
|
|
||||||
text: qsTr("Welcome to %1!").arg(app.systemName)
|
|
||||||
backButtonVisible: true
|
|
||||||
onBackPressed: root.backPressed()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: engine.jsonRpcClient
|
|
||||||
onAuthenticationFailed: {
|
|
||||||
var popup = errorDialog.createObject(root)
|
|
||||||
popup.text = qsTr("Sorry, that wasn't right. Try again please.")
|
|
||||||
popup.open();
|
|
||||||
}
|
|
||||||
onCreateUserFailed: {
|
|
||||||
print("createUser failed")
|
|
||||||
var message;
|
|
||||||
switch (error) {
|
|
||||||
case "UserErrorInvalidUserId":
|
|
||||||
message = qsTr("The email you've entered isn't valid.")
|
|
||||||
break;
|
|
||||||
case "UserErrorDuplicateUserId":
|
|
||||||
message = qsTr("The email you've entered is already used.")
|
|
||||||
break;
|
|
||||||
case "UserErrorBadPassword":
|
|
||||||
message = qsTr("The password you've chose is too weak.")
|
|
||||||
break;
|
|
||||||
case "UserErrorBackendError":
|
|
||||||
message = qsTr("An error happened with the user storage. Please make sure your %1 box is installed correctly.")
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
var popup = errorDialog.createObject(root, {text: message});
|
|
||||||
popup.open();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: app.margins
|
|
||||||
spacing: app.margins
|
|
||||||
|
|
||||||
Label {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
text: engine.jsonRpcClient.initialSetupRequired ?
|
|
||||||
qsTr("In order to use your %1 system, please enter your email address and set a password for your nymea box.").arg(app.systemName)
|
|
||||||
: qsTr("In order to use your %1 system, please log in.").arg(app.systemName)
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
Label {
|
|
||||||
text: qsTr("Your e-mail address:")
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
TextField {
|
|
||||||
id: usernameTextField
|
|
||||||
Layout.fillWidth: true
|
|
||||||
inputMethodHints: Qt.ImhEmailCharactersOnly
|
|
||||||
placeholderText: "john.smith@cooldomain.com"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ColumnLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
Label {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
text: qsTr("Password:")
|
|
||||||
}
|
|
||||||
TextField {
|
|
||||||
id: passwordTextField
|
|
||||||
Layout.fillWidth: true
|
|
||||||
echoMode: TextInput.Password
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
visible: engine.jsonRpcClient.initialSetupRequired
|
|
||||||
|
|
||||||
Label {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
text: qsTr("Confirm password:")
|
|
||||||
}
|
|
||||||
TextField {
|
|
||||||
id: confirmPasswordTextField
|
|
||||||
Layout.fillWidth: true
|
|
||||||
echoMode: TextInput.Password
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
visible: engine.jsonRpcClient.initialSetupRequired
|
|
||||||
opacity: (passwordTextField.text.length > 0 && passwordTextField.text.length < 8) || passwordTextField.text != confirmPasswordTextField.text ? 1 : 0
|
|
||||||
text: passwordTextField.text.length < 8 ? qsTr("This password isn't long enought to be secure, add some more characters please.")
|
|
||||||
: qsTr("The passwords don't match.")
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
Layout.preferredHeight: confirmPasswordTextField.height * 2
|
|
||||||
color: app.accentColor
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
text: qsTr("OK")
|
|
||||||
enabled: usernameTextField.text.length >= 5 && passwordTextField.text.length >= 8
|
|
||||||
&& (!engine.jsonRpcClient.initialSetupRequired || confirmPasswordTextField.text == passwordTextField.text)
|
|
||||||
onClicked: {
|
|
||||||
if (engine.jsonRpcClient.initialSetupRequired) {
|
|
||||||
print("create user")
|
|
||||||
engine.jsonRpcClient.createUser(usernameTextField.text, passwordTextField.text);
|
|
||||||
} else {
|
|
||||||
print("authenticate", usernameTextField.text, passwordTextField.text, "nymea-app")
|
|
||||||
engine.jsonRpcClient.authenticate(usernameTextField.text, passwordTextField.text, "nymea-app");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Item {
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: errorDialog
|
|
||||||
ErrorDialog {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -54,6 +54,7 @@ ApplicationWindow {
|
|||||||
RootItem {
|
RootItem {
|
||||||
id: rootItem
|
id: rootItem
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
anchors.bottomMargin: keyboardRect.height
|
||||||
}
|
}
|
||||||
|
|
||||||
NymeaDiscovery {
|
NymeaDiscovery {
|
||||||
|
|||||||
@ -33,31 +33,30 @@ Page {
|
|||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors { left: parent.left; right: parent.right; verticalCenter: parent.verticalCenter }
|
anchors { left: parent.left; right: parent.right; verticalCenter: parent.verticalCenter }
|
||||||
anchors.margins: app.margins
|
anchors.margins: app.margins
|
||||||
spacing: app.margins
|
spacing: app.margins * 2
|
||||||
|
|
||||||
RowLayout {
|
Label {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
spacing: app.margins
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
color: app.accentColor
|
||||||
|
text: qsTr("Authentication required")
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
font.pixelSize: app.largeFont
|
||||||
|
}
|
||||||
|
|
||||||
ColorIcon {
|
Image {
|
||||||
height: app.iconSize * 2
|
Layout.preferredWidth: app.iconSize * 6
|
||||||
width: height
|
Layout.preferredHeight: width
|
||||||
color: app.accentColor
|
source: "images/nymea-box-setup.svg"
|
||||||
name: "../images/info.svg"
|
Layout.alignment: Qt.AlignHCenter
|
||||||
}
|
sourceSize.width: width
|
||||||
|
sourceSize.height: height
|
||||||
Label {
|
|
||||||
color: app.accentColor
|
|
||||||
text: qsTr("Authentication required")
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
Layout.fillWidth: true
|
|
||||||
font.pixelSize: app.largeFont
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
text: qsTr("Please press the button on your %1 box to authenticate this device.").arg(app.systemName)
|
text: qsTr("Please press the button on your %1 box to authenticate this device.").arg(app.systemName)
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
}
|
}
|
||||||
|
|||||||
@ -145,7 +145,17 @@ Item {
|
|||||||
})
|
})
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
var page = pageStack.push(Qt.resolvedUrl("LoginPage.qml"));
|
if (engine.jsonRpcClient.initialSetupRequired) {
|
||||||
|
var page = pageStack.push(Qt.resolvedUrl("connection/SetupWizard.qml"));
|
||||||
|
page.backPressed.connect(function() {
|
||||||
|
tabSettings.lastConnectedHost = "";
|
||||||
|
engine.connection.disconnect()
|
||||||
|
init();
|
||||||
|
})
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var page = pageStack.push(Qt.resolvedUrl("connection/LoginPage.qml"));
|
||||||
page.backPressed.connect(function() {
|
page.backPressed.connect(function() {
|
||||||
tabSettings.lastConnectedHost = "";
|
tabSettings.lastConnectedHost = "";
|
||||||
engine.connection.disconnect()
|
engine.connection.disconnect()
|
||||||
|
|||||||
158
nymea-app/ui/connection/LoginPage.qml
Normal file
158
nymea-app/ui/connection/LoginPage.qml
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
import QtQuick 2.5
|
||||||
|
import QtQuick.Controls 2.1
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
import Nymea 1.0
|
||||||
|
import "../components"
|
||||||
|
|
||||||
|
Page {
|
||||||
|
id: root
|
||||||
|
signal backPressed();
|
||||||
|
|
||||||
|
header: GuhHeader {
|
||||||
|
text: qsTr("Welcome to %1!").arg(app.systemName)
|
||||||
|
backButtonVisible: true
|
||||||
|
onBackPressed: root.backPressed()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: engine.jsonRpcClient
|
||||||
|
onAuthenticationFailed: {
|
||||||
|
var popup = errorDialog.createObject(root)
|
||||||
|
popup.text = qsTr("Sorry, that wasn't right. Try again please.")
|
||||||
|
popup.open();
|
||||||
|
}
|
||||||
|
onCreateUserSucceeded: {
|
||||||
|
engine.jsonRpcClient.authenticate(usernameTextField.text, passwordTextField.text, "nymea-app");
|
||||||
|
}
|
||||||
|
|
||||||
|
onCreateUserFailed: {
|
||||||
|
print("createUser failed")
|
||||||
|
var message;
|
||||||
|
switch (error) {
|
||||||
|
case "UserErrorInvalidUserId":
|
||||||
|
message = qsTr("The email you've entered isn't valid.")
|
||||||
|
break;
|
||||||
|
case "UserErrorDuplicateUserId":
|
||||||
|
message = qsTr("The email you've entered is already used.")
|
||||||
|
break;
|
||||||
|
case "UserErrorBadPassword":
|
||||||
|
message = qsTr("The password you've chose is too weak.")
|
||||||
|
break;
|
||||||
|
case "UserErrorBackendError":
|
||||||
|
message = qsTr("An error happened with the user storage. Please make sure your %1 box is installed correctly.")
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var popup = errorDialog.createObject(root, {text: message});
|
||||||
|
popup.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Flickable {
|
||||||
|
anchors.fill: parent
|
||||||
|
contentHeight: contentColumn.implicitHeight
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: contentColumn
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
spacing: app.margins
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.margins: app.margins
|
||||||
|
spacing: app.margins
|
||||||
|
|
||||||
|
ColorIcon {
|
||||||
|
Layout.preferredHeight: app.iconSize * 2
|
||||||
|
Layout.preferredWidth: app.iconSize * 2
|
||||||
|
name: "../images/lock-closed.svg"
|
||||||
|
color: app.accentColor
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: engine.jsonRpcClient.initialSetupRequired ?
|
||||||
|
qsTr("In order to use your %1 system, please enter your email address and set a password for your %1 box.").arg(app.systemName)
|
||||||
|
: qsTr("In order to use your %1 system, please log in.").arg(app.systemName)
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
GridLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||||
|
columns: app.width > 400 ? 2 : 1
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: qsTr("Your e-mail address:")
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: usernameTextField
|
||||||
|
Layout.fillWidth: true
|
||||||
|
inputMethodHints: Qt.ImhEmailCharactersOnly
|
||||||
|
placeholderText: "john.smith@cooldomain.com"
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: qsTr("Password:")
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: passwordTextField
|
||||||
|
Layout.fillWidth: true
|
||||||
|
echoMode: TextInput.Password
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
visible: engine.jsonRpcClient.initialSetupRequired
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: qsTr("Confirm password:")
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: confirmPasswordTextField
|
||||||
|
visible: engine.jsonRpcClient.initialSetupRequired
|
||||||
|
Layout.fillWidth: true
|
||||||
|
echoMode: TextInput.Password
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||||
|
visible: engine.jsonRpcClient.initialSetupRequired
|
||||||
|
opacity: (passwordTextField.text.length > 0 && passwordTextField.text.length < 8) || passwordTextField.text != confirmPasswordTextField.text ? 1 : 0
|
||||||
|
text: passwordTextField.text.length < 8 ? qsTr("This password isn't long enought to be secure, add some more characters please.")
|
||||||
|
: qsTr("The passwords don't match.")
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
color: app.accentColor
|
||||||
|
font.pixelSize: app.smallFont
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.bottomMargin: app.margins
|
||||||
|
text: qsTr("OK")
|
||||||
|
enabled: usernameTextField.text.length >= 5 && passwordTextField.text.length >= 8
|
||||||
|
&& (!engine.jsonRpcClient.initialSetupRequired || confirmPasswordTextField.text == passwordTextField.text)
|
||||||
|
onClicked: {
|
||||||
|
if (engine.jsonRpcClient.initialSetupRequired) {
|
||||||
|
print("create user")
|
||||||
|
engine.jsonRpcClient.createUser(usernameTextField.text, passwordTextField.text);
|
||||||
|
} else {
|
||||||
|
print("authenticate", usernameTextField.text, passwordTextField.text, "nymea-app")
|
||||||
|
engine.jsonRpcClient.authenticate(usernameTextField.text, passwordTextField.text, "nymea-app");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: errorDialog
|
||||||
|
ErrorDialog {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
28
nymea-app/ui/connection/SetupWizard.qml
Normal file
28
nymea-app/ui/connection/SetupWizard.qml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import QtQuick 2.5
|
||||||
|
import QtQuick.Controls 2.2
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
import Nymea 1.0
|
||||||
|
import "../components"
|
||||||
|
|
||||||
|
Page {
|
||||||
|
id: root
|
||||||
|
signal backPressed();
|
||||||
|
|
||||||
|
header: GuhHeader {
|
||||||
|
text: qsTr("First setup")
|
||||||
|
backButtonVisible: true
|
||||||
|
onBackPressed: root.backPressed()
|
||||||
|
}
|
||||||
|
|
||||||
|
EmptyViewPlaceholder {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: parent.width - app.margins * 2
|
||||||
|
title: qsTr("Welcome to %1!").arg(app.systemName)
|
||||||
|
text: qsTr("This %1 system has not been set up yet. This wizard will guide you through a few simple steps to set it up.").arg(app.systemName)
|
||||||
|
imageSource: "qrc:/styles/%1/logo.svg".arg(styleController.currentStyle)
|
||||||
|
buttonText: qsTr("Next")
|
||||||
|
onButtonClicked: {
|
||||||
|
var page = pageStack.push(Qt.resolvedUrl("LoginPage.qml"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user