Merge PR #316: Add support for changing the password and manage tokens
This commit is contained in:
commit
1b7f8d3bfd
@ -164,7 +164,7 @@ void NymeaDiscovery::cacheHost(NymeaHost *host)
|
||||
settings.beginGroup(QString::number(i++));
|
||||
settings.setValue("url", connection->url());
|
||||
settings.setValue("bearerType", connection->bearerType());
|
||||
settings.value("secure", connection->secure());
|
||||
settings.setValue("secure", connection->secure());
|
||||
settings.setValue("displayName", connection->displayName());
|
||||
settings.endGroup();
|
||||
}
|
||||
|
||||
@ -161,6 +161,7 @@ Connection *NymeaConnection::currentConnection() const
|
||||
if (!m_currentHost || !m_currentTransport) {
|
||||
return nullptr;
|
||||
}
|
||||
qDebug() << "secure:" << m_transportCandidates.value(m_currentTransport)->secure();
|
||||
return m_transportCandidates.value(m_currentTransport);
|
||||
}
|
||||
|
||||
|
||||
@ -189,6 +189,11 @@ bool JsonRpcClient::pushButtonAuthAvailable() const
|
||||
return m_pushButtonAuthAvailable;
|
||||
}
|
||||
|
||||
bool JsonRpcClient::authenticated() const
|
||||
{
|
||||
return m_authenticated;
|
||||
}
|
||||
|
||||
JsonRpcClient::CloudConnectionState JsonRpcClient::cloudConnectionState() const
|
||||
{
|
||||
return m_cloudConnectionState;
|
||||
@ -297,6 +302,9 @@ void JsonRpcClient::processAuthenticate(const QVariantMap &data)
|
||||
settings.endGroup();
|
||||
emit authenticationRequiredChanged();
|
||||
|
||||
m_authenticated = true;
|
||||
emit authenticated();
|
||||
|
||||
setNotificationsEnabled();
|
||||
} else {
|
||||
qWarning() << "Authentication failed" << data;
|
||||
@ -376,6 +384,7 @@ void JsonRpcClient::onInterfaceConnectedChanged(bool connected)
|
||||
qDebug() << "JsonRpcClient: Transport disconnected.";
|
||||
m_initialSetupRequired = false;
|
||||
m_authenticationRequired = false;
|
||||
m_authenticated = false;
|
||||
m_serverQtVersion.clear();
|
||||
m_serverQtBuildVersion.clear();
|
||||
if (m_connected) {
|
||||
@ -442,6 +451,8 @@ void JsonRpcClient::dataReceived(const QByteArray &data)
|
||||
settings.setValue(m_serverUuid, m_token);
|
||||
settings.endGroup();
|
||||
emit authenticationRequiredChanged();
|
||||
m_authenticated = false;
|
||||
emit authenticatedChanged();
|
||||
}
|
||||
|
||||
if (!reply->caller().isNull() && !reply->callback().isEmpty()) {
|
||||
@ -508,11 +519,13 @@ void JsonRpcClient::helloReply(const QVariantMap ¶ms)
|
||||
if (m_token.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_authenticated = true;
|
||||
emit authenticatedChanged();
|
||||
}
|
||||
|
||||
setNotificationsEnabled();
|
||||
getCloudConnectionStatus();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -50,6 +50,7 @@ class JsonRpcClient : public JsonHandler
|
||||
Q_PROPERTY(bool initialSetupRequired READ initialSetupRequired NOTIFY initialSetupRequiredChanged)
|
||||
Q_PROPERTY(bool authenticationRequired READ authenticationRequired NOTIFY authenticationRequiredChanged)
|
||||
Q_PROPERTY(bool pushButtonAuthAvailable READ pushButtonAuthAvailable NOTIFY pushButtonAuthAvailableChanged)
|
||||
Q_PROPERTY(bool authenticated READ authenticated NOTIFY authenticatedChanged)
|
||||
Q_PROPERTY(CloudConnectionState cloudConnectionState READ cloudConnectionState NOTIFY cloudConnectionStateChanged)
|
||||
Q_PROPERTY(QString serverVersion READ serverVersion NOTIFY handshakeReceived)
|
||||
Q_PROPERTY(QString jsonRpcVersion READ jsonRpcVersion NOTIFY handshakeReceived)
|
||||
@ -81,6 +82,7 @@ public:
|
||||
bool initialSetupRequired() const;
|
||||
bool authenticationRequired() const;
|
||||
bool pushButtonAuthAvailable() const;
|
||||
bool authenticated() const;
|
||||
CloudConnectionState cloudConnectionState() const;
|
||||
void deployCertificate(const QByteArray &rootCA, const QByteArray &certificate, const QByteArray &publicKey, const QByteArray &privateKey, const QString &endpoint);
|
||||
|
||||
@ -103,6 +105,7 @@ signals:
|
||||
void initialSetupRequiredChanged();
|
||||
void authenticationRequiredChanged();
|
||||
void pushButtonAuthAvailableChanged();
|
||||
void authenticatedChanged();
|
||||
void connectedChanged(bool connected);
|
||||
void tokenChanged();
|
||||
void invalidProtocolVersion(const QString &actualVersion, const QString &minimumVersion);
|
||||
@ -135,6 +138,7 @@ private:
|
||||
bool m_initialSetupRequired = false;
|
||||
bool m_authenticationRequired = false;
|
||||
bool m_pushButtonAuthAvailable = false;
|
||||
bool m_authenticated = false;
|
||||
CloudConnectionState m_cloudConnectionState = CloudConnectionStateDisabled;
|
||||
int m_pendingPushButtonTransaction = -1;
|
||||
QString m_serverUuid;
|
||||
|
||||
@ -105,6 +105,10 @@
|
||||
#include "types/script.h"
|
||||
#include "types/scripts.h"
|
||||
#include "types/types.h"
|
||||
#include "usermanager.h"
|
||||
#include "types/tokeninfos.h"
|
||||
#include "types/tokeninfo.h"
|
||||
#include "types/userinfo.h"
|
||||
|
||||
#include <QtQml/qqml.h>
|
||||
|
||||
@ -281,6 +285,11 @@ void registerQmlTypes() {
|
||||
qmlRegisterType<ScriptSyntaxHighlighter>(uri, 1, 0, "ScriptSyntaxHighlighter");
|
||||
qmlRegisterType<CodeCompletion>(uri, 1, 0, "CodeCompletion");
|
||||
qmlRegisterUncreatableType<CompletionProxyModel>(uri, 1, 0, "CompletionModel", "Get it from ScriptSyntaxHighlighter");
|
||||
|
||||
qmlRegisterType<UserManager>(uri, 1, 0, "UserManager");
|
||||
qmlRegisterUncreatableType<UserInfo>(uri, 1, 0, "UserInfo", "Get it from UserManager");
|
||||
qmlRegisterUncreatableType<TokenInfo>(uri, 1, 0, "TokenInfo", "Get it from TokenInfos");
|
||||
qmlRegisterUncreatableType<TokenInfos>(uri, 1, 0, "TokenInfos", "Get it from UserManager");
|
||||
}
|
||||
|
||||
#endif // LIBNYMEAAPPCORE_H
|
||||
|
||||
@ -55,6 +55,7 @@ SOURCES += \
|
||||
scripting/completionmodel.cpp \
|
||||
scriptmanager.cpp \
|
||||
scriptsyntaxhighlighter.cpp \
|
||||
usermanager.cpp \
|
||||
vendorsproxy.cpp \
|
||||
pluginsproxy.cpp \
|
||||
interfacesmodel.cpp \
|
||||
@ -91,6 +92,7 @@ SOURCES += \
|
||||
models/devicemodel.cpp \
|
||||
system/systemcontroller.cpp \
|
||||
|
||||
|
||||
HEADERS += \
|
||||
configuration/networkmanager.h \
|
||||
engine.h \
|
||||
@ -122,6 +124,7 @@ HEADERS += \
|
||||
scripting/completionmodel.h \
|
||||
scriptmanager.h \
|
||||
scriptsyntaxhighlighter.h \
|
||||
usermanager.h \
|
||||
vendorsproxy.h \
|
||||
pluginsproxy.h \
|
||||
interfacesmodel.h \
|
||||
|
||||
129
libnymea-app-core/usermanager.cpp
Normal file
129
libnymea-app-core/usermanager.cpp
Normal file
@ -0,0 +1,129 @@
|
||||
#include "usermanager.h"
|
||||
#include "types/tokeninfo.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QMetaEnum>
|
||||
|
||||
UserManager::UserManager(QObject *parent):
|
||||
JsonHandler(parent)
|
||||
{
|
||||
m_userInfo = new UserInfo(this);
|
||||
m_tokenInfos = new TokenInfos(this);
|
||||
}
|
||||
|
||||
Engine *UserManager::engine() const
|
||||
{
|
||||
return m_engine;
|
||||
}
|
||||
|
||||
void UserManager::setEngine(Engine *engine)
|
||||
{
|
||||
if (m_engine != engine) {
|
||||
m_engine = engine;
|
||||
m_engine->jsonRpcClient()->registerNotificationHandler(this, "notificationReceived");
|
||||
emit engineChanged();
|
||||
|
||||
m_loading = true;
|
||||
emit loadingChanged();
|
||||
|
||||
m_engine->jsonRpcClient()->sendCommand("Users.GetUserInfo", QVariantMap(), this, "getUserInfoReply");
|
||||
m_engine->jsonRpcClient()->sendCommand("Users.GetTokens", QVariantMap(), this, "getTokensReply");
|
||||
}
|
||||
}
|
||||
|
||||
bool UserManager::loading() const
|
||||
{
|
||||
return m_loading;
|
||||
}
|
||||
|
||||
UserInfo *UserManager::userInfo() const
|
||||
{
|
||||
return m_userInfo;
|
||||
}
|
||||
|
||||
TokenInfos *UserManager::tokenInfos() const
|
||||
{
|
||||
return m_tokenInfos;
|
||||
}
|
||||
|
||||
QString UserManager::nameSpace() const
|
||||
{
|
||||
return "Users";
|
||||
}
|
||||
|
||||
int UserManager::changePassword(const QString &newPassword)
|
||||
{
|
||||
QVariantMap params;
|
||||
params.insert("newPassword", newPassword);
|
||||
int callId = m_engine->jsonRpcClient()->sendCommand("Users.ChangePassword", params, this, "changePasswordReply");
|
||||
return callId;
|
||||
}
|
||||
|
||||
int UserManager::removeToken(const QUuid &id)
|
||||
{
|
||||
QVariantMap params;
|
||||
params.insert("tokenId", id);
|
||||
int callId = m_engine->jsonRpcClient()->sendCommand("Users.RemoveToken", params, this, "deleteTokenReply");
|
||||
m_tokensToBeRemoved.insert(callId, id);
|
||||
return callId;
|
||||
}
|
||||
|
||||
void UserManager::notificationReceived(const QVariantMap &data)
|
||||
{
|
||||
qDebug() << "Users notification" << data;
|
||||
}
|
||||
|
||||
void UserManager::getUserInfoReply(const QVariantMap &data)
|
||||
{
|
||||
qDebug() << "User info reply" << data;
|
||||
|
||||
m_userInfo->setUsername(data.value("params").toMap().value("userInfo").toMap().value("username").toString());
|
||||
}
|
||||
|
||||
void UserManager::getTokensReply(const QVariantMap &data)
|
||||
{
|
||||
|
||||
foreach (const QVariant &tokenVariant, data.value("params").toMap().value("tokenInfoList").toList()) {
|
||||
// qDebug() << "Token received" << tokenVariant.toMap();
|
||||
QVariantMap token = tokenVariant.toMap();
|
||||
QUuid id = token.value("id").toString();
|
||||
QString username = token.value("username").toString();
|
||||
QString deviceName = token.value("deviceName").toString();
|
||||
QDateTime creationTime = QDateTime::fromSecsSinceEpoch(token.value("creationTime").toInt());
|
||||
TokenInfo *tokenInfo = new TokenInfo(id, username, deviceName, creationTime);
|
||||
m_tokenInfos->addToken(tokenInfo);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void UserManager::deleteTokenReply(const QVariantMap &payload)
|
||||
{
|
||||
qDebug() << "Delete token reply" << payload;
|
||||
int callId = payload.value("id").toInt();
|
||||
QUuid tokenId = m_tokensToBeRemoved.take(callId);
|
||||
QVariantMap params = payload.value("params").toMap();
|
||||
QString errorString = params.value("error").toString();
|
||||
QMetaEnum metaEnum = QMetaEnum::fromType<UserManager::UserError>();
|
||||
UserError error = static_cast<UserError>(metaEnum.keyToValue(errorString.toUtf8()));
|
||||
|
||||
emit deleteTokenResponse(callId, error);
|
||||
|
||||
if (error == UserErrorNoError) {
|
||||
m_tokenInfos->removeToken(tokenId);
|
||||
}
|
||||
}
|
||||
|
||||
void UserManager::changePasswordReply(const QVariantMap &data)
|
||||
{
|
||||
qDebug() << "Change password reply" << data;
|
||||
|
||||
int callId = data.value("id").toInt();
|
||||
QVariantMap params = data.value("params").toMap();
|
||||
QString errorString = params.value("error").toString();
|
||||
QMetaEnum metaEnum = QMetaEnum::fromType<UserManager::UserError>();
|
||||
UserError error = static_cast<UserError>(metaEnum.keyToValue(errorString.toUtf8()));
|
||||
|
||||
emit changePasswordResponse(callId, error);
|
||||
}
|
||||
73
libnymea-app-core/usermanager.h
Normal file
73
libnymea-app-core/usermanager.h
Normal file
@ -0,0 +1,73 @@
|
||||
#ifndef USERMANAGER_H
|
||||
#define USERMANAGER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "jsonrpc/jsonrpcclient.h"
|
||||
#include "engine.h"
|
||||
|
||||
#include "types/tokeninfos.h"
|
||||
#include "types/userinfo.h"
|
||||
|
||||
class UserManager: public JsonHandler
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(Engine* engine READ engine WRITE setEngine NOTIFY engineChanged)
|
||||
Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged)
|
||||
|
||||
Q_PROPERTY(UserInfo* userInfo READ userInfo CONSTANT)
|
||||
Q_PROPERTY(TokenInfos* tokenInfos READ tokenInfos CONSTANT)
|
||||
|
||||
public:
|
||||
enum UserError {
|
||||
UserErrorNoError,
|
||||
UserErrorBackendError,
|
||||
UserErrorInvalidUserId,
|
||||
UserErrorDuplicateUserId,
|
||||
UserErrorBadPassword,
|
||||
UserErrorTokenNotFound,
|
||||
UserErrorPermissionDenied
|
||||
};
|
||||
Q_ENUM(UserError)
|
||||
|
||||
explicit UserManager(QObject *parent = nullptr);
|
||||
|
||||
Engine* engine() const;
|
||||
void setEngine(Engine* engine);
|
||||
|
||||
bool loading() const;
|
||||
|
||||
UserInfo* userInfo() const;
|
||||
TokenInfos* tokenInfos() const;
|
||||
|
||||
QString nameSpace() const override;
|
||||
|
||||
Q_INVOKABLE int changePassword(const QString &newPassword);
|
||||
Q_INVOKABLE int removeToken(const QUuid &id);
|
||||
|
||||
signals:
|
||||
void engineChanged();
|
||||
void loadingChanged();
|
||||
|
||||
void deleteTokenResponse(int id, UserError error);
|
||||
void changePasswordResponse(int id, UserError error);
|
||||
|
||||
private slots:
|
||||
void notificationReceived(const QVariantMap &data);
|
||||
|
||||
void getUserInfoReply(const QVariantMap &data);
|
||||
void getTokensReply(const QVariantMap &data);
|
||||
void deleteTokenReply(const QVariantMap &data);
|
||||
void changePasswordReply(const QVariantMap &data);
|
||||
|
||||
private:
|
||||
Engine *m_engine = nullptr;
|
||||
bool m_loading = false;
|
||||
|
||||
UserInfo *m_userInfo = nullptr;
|
||||
TokenInfos *m_tokenInfos = nullptr;
|
||||
|
||||
QHash<int, QUuid> m_tokensToBeRemoved;
|
||||
};
|
||||
|
||||
#endif // USERMANAGER_H
|
||||
@ -64,6 +64,10 @@ HEADERS += \
|
||||
types/tags.h \
|
||||
types/wirelessaccesspoint.h \
|
||||
types/wirelessaccesspoints.h \
|
||||
types/tokeninfo.h \
|
||||
types/tokeninfos.h \
|
||||
types/userinfo.h \
|
||||
|
||||
|
||||
SOURCES += \
|
||||
types/browseritem.cpp \
|
||||
@ -122,3 +126,8 @@ SOURCES += \
|
||||
types/tags.cpp \
|
||||
types/wirelessaccesspoint.cpp \
|
||||
types/wirelessaccesspoints.cpp \
|
||||
types/tokeninfo.cpp \
|
||||
types/tokeninfos.cpp \
|
||||
types/userinfo.cpp \
|
||||
|
||||
|
||||
|
||||
31
libnymea-common/types/tokeninfo.cpp
Normal file
31
libnymea-common/types/tokeninfo.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
#include "tokeninfo.h"
|
||||
|
||||
TokenInfo::TokenInfo(const QUuid &id, const QString &username, const QString &deviceName, const QDateTime &creationTime, QObject *parent):
|
||||
QObject(parent),
|
||||
m_id(id),
|
||||
m_username(username),
|
||||
m_deviceName(deviceName),
|
||||
m_creationTime(creationTime)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QUuid TokenInfo::id() const
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
|
||||
QString TokenInfo::username() const
|
||||
{
|
||||
return m_username;
|
||||
}
|
||||
|
||||
QString TokenInfo::deviceName() const
|
||||
{
|
||||
return m_deviceName;
|
||||
}
|
||||
|
||||
QDateTime TokenInfo::creationTime() const
|
||||
{
|
||||
return m_creationTime;
|
||||
}
|
||||
26
libnymea-common/types/tokeninfo.h
Normal file
26
libnymea-common/types/tokeninfo.h
Normal file
@ -0,0 +1,26 @@
|
||||
#ifndef TOKENINFO_H
|
||||
#define TOKENINFO_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QUuid>
|
||||
#include <QDateTime>
|
||||
|
||||
class TokenInfo : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TokenInfo(const QUuid &id, const QString &username, const QString &deviceName, const QDateTime &creationTime, QObject *parent = nullptr);
|
||||
|
||||
QUuid id() const;
|
||||
QString username() const;
|
||||
QString deviceName() const;
|
||||
QDateTime creationTime() const;
|
||||
|
||||
private:
|
||||
QUuid m_id;
|
||||
QString m_username;
|
||||
QString m_deviceName;
|
||||
QDateTime m_creationTime;
|
||||
};
|
||||
|
||||
#endif // TOKENINFO_H
|
||||
60
libnymea-common/types/tokeninfos.cpp
Normal file
60
libnymea-common/types/tokeninfos.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
#include "tokeninfos.h"
|
||||
#include "tokeninfo.h"
|
||||
|
||||
TokenInfos::TokenInfos(QObject *parent) : QAbstractListModel(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int TokenInfos::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return m_list.count();
|
||||
}
|
||||
|
||||
QVariant TokenInfos::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
switch (role) {
|
||||
case RoleId:
|
||||
return m_list.at(index.row())->id();
|
||||
case RoleUsername:
|
||||
return m_list.at(index.row())->username();
|
||||
case RoleDeviceName:
|
||||
return m_list.at(index.row())->deviceName();
|
||||
case RoleCreationTime:
|
||||
return m_list.at(index.row())->creationTime();
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> TokenInfos::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> roles;
|
||||
roles.insert(RoleId, "id");
|
||||
roles.insert(RoleUsername, "username");
|
||||
roles.insert(RoleDeviceName, "deviceName");
|
||||
roles.insert(RoleCreationTime, "creationTime");
|
||||
return roles;
|
||||
}
|
||||
|
||||
void TokenInfos::addToken(TokenInfo *tokenInfo)
|
||||
{
|
||||
tokenInfo->setParent(this);
|
||||
beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
|
||||
m_list.append(tokenInfo);
|
||||
endInsertRows();
|
||||
emit countChanged();
|
||||
}
|
||||
|
||||
void TokenInfos::removeToken(const QUuid &tokenId)
|
||||
{
|
||||
for (int i = 0; i < m_list.count(); i++) {
|
||||
if (m_list.at(i)->id() == tokenId) {
|
||||
beginRemoveRows(QModelIndex(), i, i);
|
||||
m_list.takeAt(i)->deleteLater();
|
||||
endRemoveRows();
|
||||
emit countChanged();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
36
libnymea-common/types/tokeninfos.h
Normal file
36
libnymea-common/types/tokeninfos.h
Normal file
@ -0,0 +1,36 @@
|
||||
#ifndef TOKENINFOS_H
|
||||
#define TOKENINFOS_H
|
||||
|
||||
#include <QAbstractListModel>
|
||||
|
||||
class TokenInfo;
|
||||
|
||||
class TokenInfos : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
|
||||
public:
|
||||
enum Roles {
|
||||
RoleId,
|
||||
RoleUsername,
|
||||
RoleDeviceName,
|
||||
RoleCreationTime
|
||||
};
|
||||
|
||||
explicit TokenInfos(QObject *parent = nullptr);
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
void addToken(TokenInfo *tokenInfo);
|
||||
void removeToken(const QUuid &tokenId);
|
||||
|
||||
signals:
|
||||
void countChanged();
|
||||
|
||||
private:
|
||||
QList<TokenInfo*> m_list;
|
||||
};
|
||||
|
||||
#endif // TOKENINFOS_H
|
||||
27
libnymea-common/types/userinfo.cpp
Normal file
27
libnymea-common/types/userinfo.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
#include "userinfo.h"
|
||||
|
||||
UserInfo::UserInfo(QObject *parent):
|
||||
QObject(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
UserInfo::UserInfo(const QString &username, QObject *parent):
|
||||
QObject(parent),
|
||||
m_username(username)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QString UserInfo::username() const
|
||||
{
|
||||
return m_username;
|
||||
}
|
||||
|
||||
void UserInfo::setUsername(const QString &username)
|
||||
{
|
||||
if (m_username != username) {
|
||||
m_username = username;
|
||||
emit usernameChanged();
|
||||
}
|
||||
}
|
||||
25
libnymea-common/types/userinfo.h
Normal file
25
libnymea-common/types/userinfo.h
Normal file
@ -0,0 +1,25 @@
|
||||
#ifndef USERINFO_H
|
||||
#define USERINFO_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class UserInfo : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString username READ username NOTIFY usernameChanged)
|
||||
public:
|
||||
explicit UserInfo(QObject *parent = nullptr);
|
||||
explicit UserInfo(const QString &username, QObject *parent = nullptr);
|
||||
|
||||
QString username() const;
|
||||
void setUsername(const QString &username);
|
||||
|
||||
signals:
|
||||
void usernameChanged();
|
||||
|
||||
private:
|
||||
QString m_username;
|
||||
|
||||
};
|
||||
|
||||
#endif // USERINFO_H
|
||||
@ -216,5 +216,6 @@
|
||||
<file>ui/images/save.svg</file>
|
||||
<file>ui/images/edit-clear.svg</file>
|
||||
<file>ui/images/smartlock.svg</file>
|
||||
<file>ui/images/key.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@ -208,5 +208,8 @@
|
||||
<file>ui/magic/scripting/LineNumbers.qml</file>
|
||||
<file>ui/magic/scripting/CompletionBox.qml</file>
|
||||
<file>ui/magic/scripting/EditorPane.qml</file>
|
||||
<file>ui/system/UsersSettingsPage.qml</file>
|
||||
<file>ui/components/SettingsPageBase.qml</file>
|
||||
<file>ui/components/SettingsPageSectionHeader.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@ -70,6 +70,23 @@ Page {
|
||||
}
|
||||
}
|
||||
|
||||
Pane {
|
||||
Layout.fillWidth: true
|
||||
Material.elevation: layout.isGrid ? 1 : 0
|
||||
padding: 0
|
||||
visible: engine.jsonRpcClient.ensureServerVersion("4.2")
|
||||
&& engine.jsonRpcClient.authenticated
|
||||
NymeaListItemDelegate {
|
||||
width: parent.width
|
||||
iconName: "../images/account.svg"
|
||||
text: qsTr("User settings")
|
||||
subText: qsTr("Configure who can log in")
|
||||
prominentSubText: false
|
||||
wrapTexts: false
|
||||
onClicked: pageStack.push(Qt.resolvedUrl("system/UsersSettingsPage.qml"))
|
||||
}
|
||||
}
|
||||
|
||||
Pane {
|
||||
Layout.fillWidth: true
|
||||
Material.elevation: layout.isGrid ? 1 : 0
|
||||
|
||||
@ -35,46 +35,37 @@ import QtQuick.Layouts 1.1
|
||||
import Nymea 1.0
|
||||
import "../components"
|
||||
|
||||
Page {
|
||||
SettingsPageBase {
|
||||
id: root
|
||||
header: NymeaHeader {
|
||||
text: qsTr("About %1").arg(app.appName)
|
||||
backButtonVisible: true
|
||||
onBackPressed: pageStack.pop()
|
||||
}
|
||||
title: qsTr("About %1").arg(app.appName)
|
||||
|
||||
Flickable {
|
||||
anchors.fill: parent
|
||||
contentHeight: imprint.implicitHeight
|
||||
Imprint {
|
||||
id: imprint
|
||||
Layout.fillWidth: true
|
||||
title: app.appName
|
||||
additionalLicenses: ListModel {
|
||||
ListElement { license: "CC-BY-SA-3.0"; component: "Suru icons"; infoText: qsTr("Suru icons by Ubuntu"); platforms: "*" }
|
||||
ListElement { license: "CC-BY-SA-3.0"; component: "Ubuntu font"; infoText: qsTr("Ubuntu font by Ubuntu"); platforms: "*" }
|
||||
ListElement { license: "LGPL3"; component: "QtZeroConf"; infoText: qsTr("QtZeroConf library by Jonathan Bagg"); platforms: "android,ios,linux,osx" }
|
||||
ListElement { license: "OpenSSL"; component: "OpenSSL"; infoText: qsTr("OpenSSL libraries by Eric Young"); platforms: "android,windows" }
|
||||
ListElement { license: "OFL"; component: "Oswald font"; infoText: qsTr("Oswald font by The Oswald Project"); platforms: "*" }
|
||||
}
|
||||
|
||||
Imprint {
|
||||
id: imprint
|
||||
width: parent.width
|
||||
title: app.appName
|
||||
additionalLicenses: ListModel {
|
||||
ListElement { license: "CC-BY-SA-3.0"; component: "Suru icons"; infoText: qsTr("Suru icons by Ubuntu"); platforms: "*" }
|
||||
ListElement { license: "CC-BY-SA-3.0"; component: "Ubuntu font"; infoText: qsTr("Ubuntu font by Ubuntu"); platforms: "*" }
|
||||
ListElement { license: "LGPL3"; component: "QtZeroConf"; infoText: qsTr("QtZeroConf library by Jonathan Bagg"); platforms: "android,ios,linux,osx" }
|
||||
ListElement { license: "OpenSSL"; component: "OpenSSL"; infoText: qsTr("OpenSSL libraries by Eric Young"); platforms: "android,windows" }
|
||||
ListElement { license: "OFL"; component: "Oswald font"; infoText: qsTr("Oswald font by The Oswald Project"); platforms: "*" }
|
||||
}
|
||||
githubLink: "https://github.com/nymea/nymea-app"
|
||||
|
||||
githubLink: "https://github.com/nymea/nymea-app"
|
||||
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("App version:")
|
||||
subText: appVersion
|
||||
progressive: false
|
||||
prominentSubText: false
|
||||
}
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Qt version:")
|
||||
subText: qtVersion + (qtBuildVersion !== qtVersion ? " (" + qsTr("Built with %1").arg(qtBuildVersion) + ")" : "")
|
||||
progressive: false
|
||||
prominentSubText: false
|
||||
}
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("App version:")
|
||||
subText: appVersion
|
||||
progressive: false
|
||||
prominentSubText: false
|
||||
}
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Qt version:")
|
||||
subText: qtVersion + (qtBuildVersion !== qtVersion ? " (" + qsTr("Built with %1").arg(qtBuildVersion) + ")" : "")
|
||||
progressive: false
|
||||
prominentSubText: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,12 +87,11 @@ Page {
|
||||
Pane {
|
||||
Layout.fillWidth: true
|
||||
Material.elevation: layout.isGrid ? 1 : 0
|
||||
visible: settings.showHiddenOptions
|
||||
padding: 0
|
||||
NymeaListItemDelegate {
|
||||
width: parent.width
|
||||
text: qsTr("Developer options")
|
||||
subText: qsTr("Yeehaaa!")
|
||||
subText: qsTr("Access tools for debugging and error reporting")
|
||||
iconName: "../images/sdk.svg"
|
||||
prominentSubText: false
|
||||
wrapTexts: false
|
||||
|
||||
@ -34,12 +34,9 @@ import QtQuick.Layouts 1.3
|
||||
import Nymea 1.0
|
||||
import "../components"
|
||||
|
||||
Page {
|
||||
SettingsPageBase {
|
||||
id: root
|
||||
header: NymeaHeader {
|
||||
text: qsTr("Cloud login")
|
||||
onBackPressed: pageStack.pop()
|
||||
}
|
||||
title: qsTr("%1 cloud login").arg(app.appName)
|
||||
|
||||
Component.onCompleted: {
|
||||
if (AWSClient.isLoggedIn) {
|
||||
@ -50,13 +47,13 @@ Page {
|
||||
Connections {
|
||||
target: AWSClient
|
||||
onLoginResult: {
|
||||
busyOverlay.shown = false;
|
||||
root.busy = false;
|
||||
if (error === AWSClient.LoginErrorNoError) {
|
||||
AWSClient.fetchDevices();
|
||||
}
|
||||
}
|
||||
onDeleteAccountResult: {
|
||||
busyOverlay.shown = false;
|
||||
root.busy = false;
|
||||
if (error !== AWSClient.LoginErrorNoError) {
|
||||
var errorDialog = Qt.createComponent(Qt.resolvedUrl("../components/ErrorDialog.qml"));
|
||||
var text = qsTr("Sorry, an error happened removing the account. Please try again later.");
|
||||
@ -68,12 +65,15 @@ Page {
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
Layout.fillWidth: true
|
||||
visible: AWSClient.isLoggedIn
|
||||
|
||||
SettingsPageSectionHeader {
|
||||
text: qsTr("Login")
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: app.margins
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
wrapMode: Text.WordWrap
|
||||
@ -89,11 +89,20 @@ Page {
|
||||
}
|
||||
}
|
||||
|
||||
ThinDivider {}
|
||||
RowLayout {
|
||||
SettingsPageSectionHeader {
|
||||
text: qsTr("Connected %1:core systems").arg(app.systemName)
|
||||
}
|
||||
BusyIndicator {
|
||||
running: AWSClient.awsDevices.busy
|
||||
height: app.iconSize
|
||||
width: height
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: app.margins
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
wrapMode: Text.WordWrap
|
||||
@ -101,13 +110,11 @@ Page {
|
||||
qsTr("There are no %1:core systems connected to your cloud yet.").arg(app.systemName) :
|
||||
qsTr("There are %n %1:core systems connected to your cloud.", "", AWSClient.awsDevices.count).arg(app.systemName)
|
||||
}
|
||||
ListView {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
clip: true
|
||||
|
||||
Repeater {
|
||||
model: AWSClient.awsDevices
|
||||
delegate: NymeaListItemDelegate {
|
||||
width: parent.width
|
||||
Layout.fillWidth: true
|
||||
text: model.name
|
||||
subText: model.id
|
||||
progressive: false
|
||||
@ -129,66 +136,173 @@ Page {
|
||||
}
|
||||
}
|
||||
|
||||
BusyIndicator {
|
||||
anchors.centerIn: parent
|
||||
visible: AWSClient.awsDevices.busy
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
id: loginColumn
|
||||
visible: !AWSClient.isLoggedIn
|
||||
Layout.fillWidth: true
|
||||
SettingsPageSectionHeader {
|
||||
text: qsTr("Login")
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.topMargin: app.margins
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("Log %1 in to %2:cloud in order to connect to %2:core systems from anywhere and receive push notifications from %2:core systems.").arg(app.appName).arg(app.systemName)
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.topMargin: app.margins
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: app.smallFont
|
||||
text: qsTr("See our <a href=\"%1\">privacy policy</a> to find out what information is processed.").arg(app.privacyPolicyUrl)
|
||||
onLinkActivated: {
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.topMargin: app.margins
|
||||
text: "Username (e-mail)"
|
||||
}
|
||||
TextField {
|
||||
id: usernameTextField
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
placeholderText: "john.smith@cooldomain.com"
|
||||
inputMethodHints: Qt.ImhEmailCharactersOnly
|
||||
validator: RegExpValidator { regExp:/\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/ }
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.topMargin: app.margins
|
||||
text: qsTr("Password")
|
||||
}
|
||||
RowLayout {
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
PasswordTextField {
|
||||
id: passwordTextField
|
||||
Layout.fillWidth: true
|
||||
signup: false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
text: qsTr("OK")
|
||||
enabled: usernameTextField.acceptableInput
|
||||
onClicked: {
|
||||
root.busy = true
|
||||
AWSClient.login(usernameTextField.text, passwordTextField.password);
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: AWSClient
|
||||
onLoginResult: {
|
||||
switch (error) {
|
||||
case AWSClient.LoginErrorInvalidUserOrPass:
|
||||
errorLabel.text = qsTr("Failed to log in. Please try again. Do you perhaps have <a href=\"#\">forgotten your password?</a>")
|
||||
break;
|
||||
case AWSClient.LoginErrorNetworkError:
|
||||
errorLabel.text = qsTr("Failed to connect to the login server. Please mase sure your network connection is working.")
|
||||
break;
|
||||
default:
|
||||
errorLabel.text = qsTr("An unexpected error happened. Please report this isse. Error code: %1").arg(error)
|
||||
break;
|
||||
}
|
||||
errorLabel.visible = (error !== AWSClient.LoginErrorNoError)
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
id: errorLabel
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.bottomMargin: app.margins
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: app.smallFont
|
||||
color: "red"
|
||||
visible: false
|
||||
onLinkActivated: {
|
||||
pageStack.push(resetPasswordComponent, {email: usernameTextField.text})
|
||||
}
|
||||
}
|
||||
|
||||
ThinDivider {}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.topMargin: app.margins
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("Don't have a user yet?")
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
text: qsTr("Sign Up")
|
||||
onClicked: {
|
||||
pageStack.push(signupPageComponent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Component {
|
||||
id : logoutDialogComponent
|
||||
MeaDialog {
|
||||
id: logoutDialog
|
||||
title: qsTr("Goodbye")
|
||||
text: qsTr("Sorry to see you go. If you log out you won't be able to connect to %1:core systems remotely any more. However, you can come back any time, we'll keep your user account. If you whish to completely delete your account and all the data associated with it, check the box below before hitting ok. If you decide to delete your account, all your personal information will be removed from %1:cloud and cannot be restored.").arg(app.systemName)
|
||||
headerIcon: "../images/dialog-warning-symbolic.svg"
|
||||
standardButtons: Dialog.Cancel | Dialog.Ok
|
||||
id: signupPageComponent
|
||||
SettingsPageBase {
|
||||
id: signupPage
|
||||
title: qsTr("Sign up")
|
||||
|
||||
RowLayout {
|
||||
CheckBox {
|
||||
id: deleteCheckbox
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("Delete my account")
|
||||
Connections {
|
||||
target: AWSClient
|
||||
onSignupResult: {
|
||||
signupPage.busy = false;
|
||||
var text;
|
||||
switch (error) {
|
||||
case AWSClient.LoginErrorNoError:
|
||||
pageStack.push(enterCodeComponent)
|
||||
return;
|
||||
case AWSClient.LoginErrorInvalidUserOrPass:
|
||||
text = qsTr("The given username or password are not valid.")
|
||||
break;
|
||||
default:
|
||||
text = qsTr("Uh oh, something went wrong. Please try again.")
|
||||
}
|
||||
var errorDialog = Qt.createComponent(Qt.resolvedUrl("../components/ErrorDialog.qml"));
|
||||
var popup = errorDialog.createObject(app, {text: text})
|
||||
popup.open()
|
||||
}
|
||||
}
|
||||
|
||||
onAccepted: {
|
||||
if (deleteCheckbox.checked) {
|
||||
busyOverlay.shown = true;
|
||||
AWSClient.deleteAccount()
|
||||
} else {
|
||||
AWSClient.logout()
|
||||
}
|
||||
|
||||
SettingsPageSectionHeader {
|
||||
text: qsTr("Welcome to %1:cloud.").arg(app.systemName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Flickable {
|
||||
anchors.fill: parent
|
||||
interactive: contentHeight > height
|
||||
contentHeight: loginColumn.height
|
||||
visible: !AWSClient.isLoggedIn
|
||||
|
||||
ColumnLayout {
|
||||
id: loginColumn
|
||||
anchors { left: parent.left; right: parent.right; top: parent.top }
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.topMargin: app.margins
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("Log in to %1:cloud in order to connect to %1:core systems from anywhere.").arg(app.systemName)
|
||||
text: qsTr("Please enter your email address and pick a password in order to create a new account.");
|
||||
onLinkActivated: {
|
||||
print("clicked", link)
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.topMargin: app.margins
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: app.smallFont
|
||||
text: qsTr("See our <a href=\"%1\">privacy policy</a> to find out what information is processed.").arg(app.privacyPolicyUrl)
|
||||
text: qsTr("See our <a href=\"%1\">privacy policy</a> to find out what information is processed. By signing up to %2:cloud you accept those terms and conditions.").arg(app.privacyPolicyUrl).arg(app.systemName)
|
||||
onLinkActivated: {
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
@ -212,198 +326,32 @@ Page {
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.topMargin: app.margins
|
||||
text: qsTr("Password")
|
||||
}
|
||||
RowLayout {
|
||||
PasswordTextField {
|
||||
id: passwordTextField
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
PasswordTextField {
|
||||
id: passwordTextField
|
||||
Layout.fillWidth: true
|
||||
signup: false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.topMargin: app.margins
|
||||
text: qsTr("OK")
|
||||
enabled: usernameTextField.acceptableInput
|
||||
onClicked: {
|
||||
busyOverlay.shown = true
|
||||
AWSClient.login(usernameTextField.text, passwordTextField.password);
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: AWSClient
|
||||
onLoginResult: {
|
||||
switch (error) {
|
||||
case AWSClient.LoginErrorInvalidUserOrPass:
|
||||
errorLabel.text = qsTr("Failed to log in. Please try again. Do you perhaps have <a href=\"#\">forgotten your password?</a>")
|
||||
break;
|
||||
case AWSClient.LoginErrorNetworkError:
|
||||
errorLabel.text = qsTr("Failed to connect to the login server. Please mase sure your network connection is working.")
|
||||
break;
|
||||
default:
|
||||
errorLabel.text = qsTr("An unexpected error happened. Please report this isse. Error code: %1").arg(error)
|
||||
break;
|
||||
}
|
||||
errorLabel.visible = (error !== AWSClient.LoginErrorNoError)
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
id: errorLabel
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.bottomMargin: app.margins
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: app.smallFont
|
||||
color: "red"
|
||||
visible: false
|
||||
onLinkActivated: {
|
||||
pageStack.push(resetPasswordComponent, {email: usernameTextField.text})
|
||||
}
|
||||
}
|
||||
|
||||
ThinDivider {}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.topMargin: app.margins
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("Don't have a user yet?")
|
||||
minPasswordLength: 8
|
||||
requireLowerCaseLetter: true
|
||||
requireUpperCaseLetter: true
|
||||
requireNumber: true
|
||||
requireSpecialChar: false
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.topMargin: app.margins
|
||||
text: qsTr("Sign Up")
|
||||
onClicked: {
|
||||
pageStack.push(signupPageComponent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BusyOverlay {
|
||||
id: busyOverlay
|
||||
}
|
||||
|
||||
Component {
|
||||
id: signupPageComponent
|
||||
Page {
|
||||
id: signupPage
|
||||
header: NymeaHeader {
|
||||
Layout.margins: app.margins
|
||||
text: qsTr("Sign up")
|
||||
onBackPressed: pageStack.pop()
|
||||
}
|
||||
|
||||
Flickable {
|
||||
anchors.fill: parent
|
||||
contentHeight: signupColumn.height
|
||||
interactive: contentHeight > height
|
||||
|
||||
ColumnLayout {
|
||||
id: signupColumn
|
||||
anchors { left: parent.left; top: parent.top; right: parent.right }
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.topMargin: app.margins
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("Welcome to %1:cloud.").arg(app.systemName)
|
||||
color: app.accentColor
|
||||
font.pixelSize: app.largeFont
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.topMargin: app.margins
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("Please enter your email address and pick a password in order to create a new account.");
|
||||
onLinkActivated: {
|
||||
print("clicked", link)
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.topMargin: app.margins
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: app.smallFont
|
||||
text: qsTr("See our <a href=\"%1\">privacy policy</a> to find out what information is processed. By signing up to %2:cloud you accept those terms and conditions.").arg(app.privacyPolicyUrl).arg(app.systemName)
|
||||
onLinkActivated: {
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.topMargin: app.margins
|
||||
text: "Username (e-mail)"
|
||||
}
|
||||
TextField {
|
||||
id: usernameTextField
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
placeholderText: "john.smith@cooldomain.com"
|
||||
inputMethodHints: Qt.ImhEmailCharactersOnly
|
||||
validator: RegExpValidator { regExp:/\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/ }
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.topMargin: app.margins
|
||||
text: qsTr("Password")
|
||||
}
|
||||
PasswordTextField {
|
||||
id: passwordTextField
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
Layout.fillWidth: true
|
||||
minPasswordLength: 8
|
||||
requireLowerCaseLetter: true
|
||||
requireUpperCaseLetter: true
|
||||
requireNumber: true
|
||||
requireSpecialChar: false
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.topMargin: app.margins
|
||||
text: qsTr("Sign up")
|
||||
enabled: usernameTextField.acceptableInput && passwordTextField.isValid
|
||||
onClicked: {
|
||||
busyOverlay.shown = true;
|
||||
AWSClient.signup(usernameTextField.text, passwordTextField.password)
|
||||
}
|
||||
}
|
||||
enabled: usernameTextField.acceptableInput && passwordTextField.isValid
|
||||
onClicked: {
|
||||
signupPage.busy = true;
|
||||
AWSClient.signup(usernameTextField.text, passwordTextField.password)
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: AWSClient
|
||||
onSignupResult: {
|
||||
busyOverlay.shown = false;
|
||||
var text;
|
||||
switch (error) {
|
||||
case AWSClient.LoginErrorNoError:
|
||||
pageStack.push(enterCodeComponent)
|
||||
return;
|
||||
case AWSClient.LoginErrorInvalidUserOrPass:
|
||||
text = qsTr("The given username or password are not valid.")
|
||||
break;
|
||||
default:
|
||||
text = qsTr("Uh oh, something went wrong. Please try again.")
|
||||
}
|
||||
var errorDialog = Qt.createComponent(Qt.resolvedUrl("../components/ErrorDialog.qml"));
|
||||
var popup = errorDialog.createObject(app, {text: text})
|
||||
popup.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BusyOverlay {
|
||||
id: busyOverlay
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
Component {
|
||||
@ -436,7 +384,7 @@ Page {
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.topMargin: app.margins
|
||||
text: qsTr("OK")
|
||||
onClicked: {
|
||||
busyOverlay.shown = true;
|
||||
root.busy = true;
|
||||
AWSClient.confirmRegistration(confirmationCodeTextField.text)
|
||||
}
|
||||
}
|
||||
@ -444,7 +392,7 @@ Page {
|
||||
Connections {
|
||||
target: AWSClient
|
||||
onConfirmationResult: {
|
||||
busyOverlay.shown = false;
|
||||
root.busy = false;
|
||||
var text
|
||||
switch (error) {
|
||||
case AWSClient.LoginErrorNoError:
|
||||
@ -473,20 +421,16 @@ Page {
|
||||
|
||||
Component {
|
||||
id: resetPasswordComponent
|
||||
Page {
|
||||
SettingsPageBase {
|
||||
id: resetPasswordPage
|
||||
title: qsTr("Reset password")
|
||||
|
||||
property alias email: emailTextField.text
|
||||
|
||||
header: NymeaHeader {
|
||||
text: qsTr("Reset password")
|
||||
onBackPressed: pageStack.pop()
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: AWSClient
|
||||
onForgotPasswordResult: {
|
||||
busyOverlay.shown = false
|
||||
resetPasswordPage.busy = false
|
||||
if (error !== AWSClient.LoginErrorNoError) {
|
||||
var errorDialog = Qt.createComponent(Qt.resolvedUrl("../components/ErrorDialog.qml"));
|
||||
var text = qsTr("Sorry, this wasn't right. Did you misspell the email address?");
|
||||
@ -501,38 +445,31 @@ Page {
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors { left: parent.left; top: parent.top; right: parent.right }
|
||||
spacing: app.margins
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true; Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.topMargin: app.margins
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("Password forgotten?")
|
||||
font.pixelSize: app.largeFont
|
||||
color: app.accentColor
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true; Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("No problem. Enter your email address here and we'll send you a confirmation code to change your password.")
|
||||
}
|
||||
TextField {
|
||||
id: emailTextField
|
||||
Layout.fillWidth: true; Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
}
|
||||
Button {
|
||||
Layout.fillWidth: true; Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
text: qsTr("Reset password")
|
||||
onClicked: {
|
||||
AWSClient.forgotPassword(emailTextField.text)
|
||||
busyOverlay.shown = true
|
||||
}
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("Password forgotten?")
|
||||
font.pixelSize: app.largeFont
|
||||
color: app.accentColor
|
||||
}
|
||||
|
||||
BusyOverlay {
|
||||
id: busyOverlay
|
||||
Label {
|
||||
Layout.fillWidth: true; Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("No problem. Enter your email address here and we'll send you a confirmation code to change your password.")
|
||||
}
|
||||
TextField {
|
||||
id: emailTextField
|
||||
Layout.fillWidth: true; Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
}
|
||||
Button {
|
||||
Layout.fillWidth: true; Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
text: qsTr("Reset password")
|
||||
onClicked: {
|
||||
AWSClient.forgotPassword(emailTextField.text)
|
||||
resetPasswordPage.busy = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -540,13 +477,13 @@ Page {
|
||||
Component {
|
||||
id: confirmResetPasswordComponent
|
||||
|
||||
Page {
|
||||
SettingsPageBase {
|
||||
id: confirmResetPasswordPage
|
||||
|
||||
Connections {
|
||||
target: AWSClient
|
||||
onConfirmForgotPasswordResult: {
|
||||
busyOverlay.shown = false
|
||||
confirmResetPasswordPage.busy = false
|
||||
if (error !== AWSClient.LoginErrorNoError) {
|
||||
var errorDialog = Qt.createComponent(Qt.resolvedUrl("../components/ErrorDialog.qml"));
|
||||
var popup = errorDialog.createObject(app, {text: qsTr("Sorry, couldn't reset your password. Did you enter the wrong confirmation code?")})
|
||||
@ -564,63 +501,85 @@ Page {
|
||||
}
|
||||
|
||||
property string email
|
||||
header: NymeaHeader {
|
||||
text: qsTr("Reset password")
|
||||
onBackPressed: pageStack.pop()
|
||||
title: qsTr("Reset password")
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("Check your email!")
|
||||
color: app.accentColor
|
||||
font.pixelSize: app.largeFont
|
||||
}
|
||||
ColumnLayout {
|
||||
anchors { left: parent.left; top: parent.top; right: parent.right }
|
||||
spacing: app.margins
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true; Layout.leftMargin: app.margins; Layout.rightMargin: app.margins;
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("Enter the confirmation code you've received and a new password for your user %1.").arg(confirmResetPasswordPage.email)
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true; Layout.leftMargin: app.margins; Layout.rightMargin: app.margins;
|
||||
text: qsTr("Confirmation code:")
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: codeTextField
|
||||
Layout.fillWidth: true; Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true; Layout.leftMargin: app.margins; Layout.rightMargin: app.margins;
|
||||
text: qsTr("Pick a new password:")
|
||||
}
|
||||
|
||||
PasswordTextField {
|
||||
id: passwordTextField
|
||||
minPasswordLength: 8
|
||||
requireLowerCaseLetter: true
|
||||
requireUpperCaseLetter: true
|
||||
requireNumber: true
|
||||
requireSpecialChar: false
|
||||
Layout.fillWidth: true; Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.fillWidth: true; Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
text: qsTr("Reset password")
|
||||
enabled: passwordTextField.isValid && codeTextField.text.length > 0
|
||||
onClicked: {
|
||||
confirmResetPasswordPage.busy = true
|
||||
AWSClient.confirmForgotPassword(confirmResetPasswordPage.email, codeTextField.text, passwordTextField.password)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id : logoutDialogComponent
|
||||
MeaDialog {
|
||||
id: logoutDialog
|
||||
title: qsTr("Goodbye")
|
||||
text: qsTr("Sorry to see you go. If you log out you won't be able to connect to %1:core systems remotely any more. However, you can come back any time, we'll keep your user account. If you whish to completely delete your account and all the data associated with it, check the box below before hitting ok. If you decide to delete your account, all your personal information will be removed from %1:cloud and cannot be restored.").arg(app.systemName)
|
||||
headerIcon: "../images/dialog-warning-symbolic.svg"
|
||||
standardButtons: Dialog.Cancel | Dialog.Ok
|
||||
|
||||
RowLayout {
|
||||
CheckBox {
|
||||
id: deleteCheckbox
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true; Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.topMargin: app.margins
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("Check your email!")
|
||||
color: app.accentColor
|
||||
font.pixelSize: app.largeFont
|
||||
text: qsTr("Delete my account")
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true; Layout.leftMargin: app.margins; Layout.rightMargin: app.margins;
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("Enter the confirmation code you've received and a new password for your user %1.").arg(confirmResetPasswordPage.email)
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true; Layout.leftMargin: app.margins; Layout.rightMargin: app.margins;
|
||||
text: qsTr("Confirmation code:")
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: codeTextField
|
||||
Layout.fillWidth: true; Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true; Layout.leftMargin: app.margins; Layout.rightMargin: app.margins;
|
||||
text: qsTr("Pick a new password:")
|
||||
}
|
||||
|
||||
PasswordTextField {
|
||||
id: passwordTextField
|
||||
minPasswordLength: 8
|
||||
requireLowerCaseLetter: true
|
||||
requireUpperCaseLetter: true
|
||||
requireNumber: true
|
||||
requireSpecialChar: false
|
||||
Layout.fillWidth: true; Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.fillWidth: true; Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
text: qsTr("Reset password")
|
||||
enabled: passwordTextField.isValid && codeTextField.text.length > 0
|
||||
onClicked: {
|
||||
busyOverlay.shown = true
|
||||
AWSClient.confirmForgotPassword(confirmResetPasswordPage.email, codeTextField.text, passwordTextField.password)
|
||||
}
|
||||
}
|
||||
BusyOverlay {
|
||||
id: busyOverlay
|
||||
onAccepted: {
|
||||
if (deleteCheckbox.checked) {
|
||||
root.busy = true;
|
||||
AWSClient.deleteAccount()
|
||||
} else {
|
||||
AWSClient.logout()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,63 +34,49 @@ import QtQuick.Layouts 1.3
|
||||
import Nymea 1.0
|
||||
import "../components"
|
||||
|
||||
Page {
|
||||
SettingsPageBase {
|
||||
id: root
|
||||
header: NymeaHeader {
|
||||
text: qsTr("Developer options")
|
||||
backButtonVisible: true
|
||||
onBackPressed: pageStack.pop()
|
||||
title: qsTr("Developer options")
|
||||
|
||||
SettingsPageSectionHeader {
|
||||
text: qsTr("Logging")
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors { left: parent.left; top: parent.top; right: parent.right }
|
||||
CheckDelegate {
|
||||
text: qsTr("Enable app logging")
|
||||
enabled: AppLogController.canWriteLogs
|
||||
checked: AppLogController.enabled
|
||||
onCheckedChanged: AppLogController.enabled = checked;
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("View log")
|
||||
onClicked: pageStack.push(Qt.resolvedUrl("../appsettings/AppLogPage.qml"))
|
||||
enabled: AppLogController.enabled
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Cloud environment")
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
currentIndex: model.indexOf(app.settings.cloudEnvironment)
|
||||
model: AWSClient.availableConfigs
|
||||
onActivated: {
|
||||
app.settings.cloudEnvironment = model[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
SettingsPageSectionHeader {
|
||||
text: qsTr("Advanced options")
|
||||
visible: settings.showHiddenOptions
|
||||
}
|
||||
|
||||
CheckDelegate {
|
||||
text: qsTr("Enable app logging")
|
||||
enabled: AppLogController.canWriteLogs
|
||||
checked: AppLogController.enabled
|
||||
onCheckedChanged: AppLogController.enabled = checked;
|
||||
RowLayout {
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
visible: settings.showHiddenOptions
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Cloud environment")
|
||||
}
|
||||
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("View log")
|
||||
onClicked: pageStack.push(Qt.resolvedUrl("../appsettings/AppLogPage.qml"))
|
||||
enabled: AppLogController.enabled
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Experience mode")
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
currentIndex: model.indexOf(styleController.currentExperience)
|
||||
model: styleController.allExperiences
|
||||
onActivated: {
|
||||
styleController.currentExperience = model[index]
|
||||
}
|
||||
ComboBox {
|
||||
currentIndex: model.indexOf(app.settings.cloudEnvironment)
|
||||
model: AWSClient.availableConfigs
|
||||
onActivated: {
|
||||
app.settings.cloudEnvironment = model[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,167 +35,195 @@ import QtQuick.Layouts 1.1
|
||||
import Nymea 1.0
|
||||
import "../components"
|
||||
|
||||
Page {
|
||||
SettingsPageBase {
|
||||
id: root
|
||||
header: NymeaHeader {
|
||||
text: qsTr("Look and feel")
|
||||
backButtonVisible: true
|
||||
onBackPressed: pageStack.pop()
|
||||
title: qsTr("Look and feel")
|
||||
|
||||
SettingsPageSectionHeader {
|
||||
text: qsTr("Appearance")
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: contentColumn
|
||||
width: parent.width
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true; Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.topMargin: app.margins
|
||||
visible: !kioskMode && Qt.platform.os !== "ios"
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("View mode")
|
||||
}
|
||||
ComboBox {
|
||||
model: [qsTr("Windowed"), qsTr("Maximized"), qsTr("Fullscreen"), qsTr("Automatic")]
|
||||
currentIndex: {
|
||||
switch (settings.viewMode) {
|
||||
case ApplicationWindow.Windowed:
|
||||
return 0;
|
||||
case ApplicationWindow.Maximized:
|
||||
return 1;
|
||||
case ApplicationWindow.FullScreen:
|
||||
return 2;
|
||||
case ApplicationWindow.AutomaticVisibility:
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
onActivated: {
|
||||
switch (currentIndex) {
|
||||
case 0:
|
||||
settings.viewMode = ApplicationWindow.Windowed;
|
||||
break;
|
||||
case 1:
|
||||
settings.viewMode = ApplicationWindow.Maximized;
|
||||
break;
|
||||
case 2:
|
||||
settings.viewMode = ApplicationWindow.FullScreen;
|
||||
break;
|
||||
case 3:
|
||||
settings.viewMode = ApplicationWindow.AutomaticVisibility;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true; Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
visible: appBranding.length === 0
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: "Style"
|
||||
}
|
||||
ComboBox {
|
||||
model: styleController.allStyles
|
||||
currentIndex: styleController.allStyles.indexOf(styleController.currentStyle)
|
||||
|
||||
onActivated: {
|
||||
styleController.currentStyle = model[index]
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: styleController
|
||||
onCurrentStyleChanged: {
|
||||
var popup = styleChangedDialog.createObject(root)
|
||||
popup.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
visible: appBranding.length === 0
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Unit system")
|
||||
}
|
||||
ComboBox {
|
||||
id: unitsComboBox
|
||||
currentIndex: settings.units === "metric" ? 0 : 1
|
||||
model: [ qsTr("Metric"), qsTr("Imperial") ]
|
||||
onActivated: {
|
||||
settings.units = index == 0 ? "metric" : "imperial";
|
||||
}
|
||||
text: "Style"
|
||||
}
|
||||
ComboBox {
|
||||
model: styleController.allStyles
|
||||
currentIndex: styleController.allStyles.indexOf(styleController.currentStyle)
|
||||
|
||||
onActivated: {
|
||||
styleController.currentStyle = model[index]
|
||||
}
|
||||
}
|
||||
|
||||
CheckDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Return to home on idle")
|
||||
checked: settings.returnToHome
|
||||
onClicked: settings.returnToHome = checked
|
||||
}
|
||||
CheckDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Show connection tabs")
|
||||
checked: settings.showConnectionTabs
|
||||
onClicked: settings.showConnectionTabs = checked
|
||||
}
|
||||
|
||||
CheckDelegate {
|
||||
id: screenOffCheck
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Turn screen off when idle")
|
||||
visible: PlatformHelper.canControlScreen
|
||||
checked: PlatformHelper.screenTimeout > 0
|
||||
onClicked: PlatformHelper.screenTimeout = (checked ? 15000 : 0)
|
||||
}
|
||||
|
||||
ItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: screenOffCheck.height
|
||||
visible: PlatformHelper.screenTimeout > 0
|
||||
topPadding: 0
|
||||
contentItem: RowLayout {
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Screen off timeout")
|
||||
}
|
||||
SpinBox {
|
||||
value: PlatformHelper.screenTimeout / 1000
|
||||
onValueModified: {
|
||||
PlatformHelper.screenTimeout = value * 1000
|
||||
}
|
||||
}
|
||||
Label {
|
||||
text: qsTr("seconds")
|
||||
}
|
||||
Connections {
|
||||
target: styleController
|
||||
onCurrentStyleChanged: {
|
||||
var popup = styleChangedDialog.createObject(root)
|
||||
popup.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ItemDelegate {
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
visible: !kioskMode && Qt.platform.os !== "ios"
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
visible: PlatformHelper.canControlScreen
|
||||
topPadding: 0
|
||||
contentItem: RowLayout {
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Screen brightness")
|
||||
text: qsTr("View mode")
|
||||
}
|
||||
ComboBox {
|
||||
model: [qsTr("Windowed"), qsTr("Maximized"), qsTr("Fullscreen"), qsTr("Automatic")]
|
||||
currentIndex: {
|
||||
switch (settings.viewMode) {
|
||||
case ApplicationWindow.Windowed:
|
||||
return 0;
|
||||
case ApplicationWindow.Maximized:
|
||||
return 1;
|
||||
case ApplicationWindow.FullScreen:
|
||||
return 2;
|
||||
case ApplicationWindow.AutomaticVisibility:
|
||||
return 3;
|
||||
}
|
||||
Slider {
|
||||
Layout.fillWidth: true
|
||||
value: PlatformHelper.screenBrightness
|
||||
onMoved: PlatformHelper.screenBrightness = value
|
||||
from: 0
|
||||
to: 100
|
||||
stepSize: 1
|
||||
}
|
||||
|
||||
onActivated: {
|
||||
switch (currentIndex) {
|
||||
case 0:
|
||||
settings.viewMode = ApplicationWindow.Windowed;
|
||||
break;
|
||||
case 1:
|
||||
settings.viewMode = ApplicationWindow.Maximized;
|
||||
break;
|
||||
case 2:
|
||||
settings.viewMode = ApplicationWindow.FullScreen;
|
||||
break;
|
||||
case 3:
|
||||
settings.viewMode = ApplicationWindow.AutomaticVisibility;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CheckDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Show connection tabs")
|
||||
checked: settings.showConnectionTabs
|
||||
onClicked: settings.showConnectionTabs = checked
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
visible: settings.showHiddenOptions
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Experience mode")
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
currentIndex: model.indexOf(styleController.currentExperience)
|
||||
model: styleController.allExperiences
|
||||
onActivated: {
|
||||
styleController.currentExperience = model[index]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsPageSectionHeader {
|
||||
text: qsTr("Regional")
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Unit system")
|
||||
}
|
||||
ComboBox {
|
||||
id: unitsComboBox
|
||||
currentIndex: settings.units === "metric" ? 0 : 1
|
||||
model: [ qsTr("Metric"), qsTr("Imperial") ]
|
||||
onActivated: {
|
||||
settings.units = index == 0 ? "metric" : "imperial";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsPageSectionHeader {
|
||||
text: qsTr("Behavior")
|
||||
}
|
||||
|
||||
CheckDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Return to home on idle")
|
||||
checked: settings.returnToHome
|
||||
onClicked: settings.returnToHome = checked
|
||||
}
|
||||
|
||||
CheckDelegate {
|
||||
id: screenOffCheck
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Turn screen off when idle")
|
||||
visible: PlatformHelper.canControlScreen
|
||||
checked: PlatformHelper.screenTimeout > 0
|
||||
onClicked: PlatformHelper.screenTimeout = (checked ? 15000 : 0)
|
||||
}
|
||||
|
||||
ItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: screenOffCheck.height
|
||||
visible: PlatformHelper.screenTimeout > 0
|
||||
topPadding: 0
|
||||
contentItem: RowLayout {
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Screen off timeout")
|
||||
}
|
||||
SpinBox {
|
||||
value: PlatformHelper.screenTimeout / 1000
|
||||
onValueModified: {
|
||||
PlatformHelper.screenTimeout = value * 1000
|
||||
}
|
||||
}
|
||||
Label {
|
||||
text: qsTr("seconds")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
visible: PlatformHelper.canControlScreen
|
||||
topPadding: 0
|
||||
contentItem: RowLayout {
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Screen brightness")
|
||||
}
|
||||
Slider {
|
||||
Layout.fillWidth: true
|
||||
value: PlatformHelper.screenBrightness
|
||||
onMoved: PlatformHelper.screenBrightness = value
|
||||
from: 0
|
||||
to: 100
|
||||
stepSize: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Component {
|
||||
id: styleChangedDialog
|
||||
Dialog {
|
||||
|
||||
@ -99,7 +99,7 @@ ColumnLayout {
|
||||
}
|
||||
var ret = []
|
||||
for (var i = 0; i < texts.length; i++) {
|
||||
var entry = "<font color=\"%1\">• ".arg(checks[i] ? app.foregroundColor : app.accentColor)
|
||||
var entry = "<font color=\"%1\">• ".arg(checks[i] ? "#ffffff" : app.accentColor)
|
||||
entry += texts[i]
|
||||
entry += "</font>"
|
||||
ret.push(entry)
|
||||
|
||||
65
nymea-app/ui/components/SettingsPageBase.qml
Normal file
65
nymea-app/ui/components/SettingsPageBase.qml
Normal file
@ -0,0 +1,65 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2020, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
* This project including source code and documentation is protected by
|
||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||
* reproduction, publication, editing and translation, are reserved. The use of
|
||||
* this project is subject to the terms of a license agreement to be concluded
|
||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||
* under https://nymea.io/license
|
||||
*
|
||||
* GNU General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, GNU version 3. This project 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
|
||||
* this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 2.1
|
||||
import QtQuick.Controls.Material 2.1
|
||||
import QtQuick.Layouts 1.1
|
||||
import Nymea 1.0
|
||||
|
||||
Page {
|
||||
id: root
|
||||
header: NymeaHeader {
|
||||
text: root.title
|
||||
backButtonVisible: true
|
||||
onBackPressed: pageStack.pop()
|
||||
}
|
||||
|
||||
property alias busy: busyOverlay.shown
|
||||
default property alias content: contentColumn.data
|
||||
|
||||
Flickable {
|
||||
anchors.fill: parent
|
||||
contentHeight: contentColumn.height
|
||||
interactive: contentHeight > height
|
||||
|
||||
ScrollBar.vertical: ScrollBar {}
|
||||
|
||||
ColumnLayout {
|
||||
id: contentColumn
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: Math.min(500, parent.width)
|
||||
}
|
||||
}
|
||||
|
||||
BusyOverlay {
|
||||
id: busyOverlay
|
||||
}
|
||||
}
|
||||
43
nymea-app/ui/components/SettingsPageSectionHeader.qml
Normal file
43
nymea-app/ui/components/SettingsPageSectionHeader.qml
Normal file
@ -0,0 +1,43 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2020, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
* This project including source code and documentation is protected by
|
||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||
* reproduction, publication, editing and translation, are reserved. The use of
|
||||
* this project is subject to the terms of a license agreement to be concluded
|
||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||
* under https://nymea.io/license
|
||||
*
|
||||
* GNU General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, GNU version 3. This project 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
|
||||
* this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.2
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: app.margins * 1.5
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
Layout.bottomMargin: app.margins
|
||||
color: app.accentColor
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
@ -53,7 +53,7 @@ Page {
|
||||
popup.open();
|
||||
}
|
||||
onCreateUserSucceeded: {
|
||||
engine.jsonRpcClient.authenticate(usernameTextField.text, passwordTextField.password, "nymea-app");
|
||||
engine.jsonRpcClient.authenticate(usernameTextField.text, passwordTextField.password, "nymea-app (" + PlatformHelper.deviceModel + ")");
|
||||
}
|
||||
|
||||
onCreateUserFailed: {
|
||||
@ -153,7 +153,7 @@ Page {
|
||||
engine.jsonRpcClient.createUser(usernameTextField.text, passwordTextField.password);
|
||||
} else {
|
||||
print("authenticate", usernameTextField.text, passwordTextField.text, "nymea-app")
|
||||
engine.jsonRpcClient.authenticate(usernameTextField.text, passwordTextField.password, "nymea-app");
|
||||
engine.jsonRpcClient.authenticate(usernameTextField.text, passwordTextField.password, "nymea-app (" + PlatformHelper.deviceModel + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
170
nymea-app/ui/images/key.svg
Normal file
170
nymea-app/ui/images/key.svg
Normal file
@ -0,0 +1,170 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="96"
|
||||
height="96"
|
||||
id="svg4874"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
viewBox="0 0 96 96.000001"
|
||||
sodipodi:docname="stock_key01.svg">
|
||||
<defs
|
||||
id="defs4876" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="8.7812489"
|
||||
inkscape:cx="35.148755"
|
||||
inkscape:cy="15.789312"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="g4780"
|
||||
showgrid="true"
|
||||
showborder="true"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-paths="true"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:snap-bbox-edge-midpoints="true"
|
||||
inkscape:snap-bbox-midpoints="true"
|
||||
inkscape:object-paths="true"
|
||||
inkscape:snap-intersection-paths="true"
|
||||
inkscape:object-nodes="true"
|
||||
inkscape:snap-smooth-nodes="true"
|
||||
inkscape:snap-midpoints="true"
|
||||
inkscape:snap-object-midpoints="true"
|
||||
inkscape:snap-center="true"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:snap-global="true">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid5451"
|
||||
empspacing="8" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="8,-8.0000001"
|
||||
id="guide4063" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="4,-8.0000001"
|
||||
id="guide4065" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="-8,88.000001"
|
||||
id="guide4067" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="45,92.000001"
|
||||
id="guide4069" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="-8,4"
|
||||
id="guide4071" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="-8,8.0000001"
|
||||
id="guide4073" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="88,-8.0000001"
|
||||
id="guide4077" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="-8,84.000001"
|
||||
id="guide4074" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="12,-8.0000001"
|
||||
id="guide4076" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="84,-8.0000001"
|
||||
id="guide4080" />
|
||||
<sodipodi:guide
|
||||
position="48,-8.0000001"
|
||||
orientation="1,0"
|
||||
id="guide4170" />
|
||||
<sodipodi:guide
|
||||
position="-8,48"
|
||||
orientation="0,1"
|
||||
id="guide4172" />
|
||||
<sodipodi:guide
|
||||
position="92,-8.0000001"
|
||||
orientation="1,0"
|
||||
id="guide4760" />
|
||||
<sodipodi:guide
|
||||
position="-8,12"
|
||||
orientation="0,1"
|
||||
id="guide3234" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata4879">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(67.857146,-78.50504)">
|
||||
<g
|
||||
transform="matrix(0,-1,-1,0,373.50506,516.50504)"
|
||||
id="g4845">
|
||||
<g
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-filename="next01.png"
|
||||
transform="matrix(-0.9996045,0,0,1,575.94296,-611.00001)"
|
||||
id="g4778"
|
||||
inkscape:label="Layer 1">
|
||||
<g
|
||||
transform="matrix(-1,0,0,1,575.99999,611)"
|
||||
id="g4780">
|
||||
<rect
|
||||
style="color:#000000;fill:none;stroke-width:4"
|
||||
id="rect4782"
|
||||
width="96.037987"
|
||||
height="96"
|
||||
x="-438.00244"
|
||||
y="345.36221"
|
||||
transform="scale(-1,1)" />
|
||||
<path
|
||||
style="color:#000000;solid-opacity:1;fill:#808080;stroke-width:4.00079155"
|
||||
d="M 35.976562 0 C 30.943803 0.058176982 27.26124 -0.11846389 24.25 1.5429688 C 22.74438 2.3736801 21.555084 3.7767542 20.896484 5.53125 C 20.237874 7.2857458 20 9.3696407 20 12 L 20 28 C 20 30.630359 20.237874 32.712301 20.896484 34.466797 C 21.555084 36.221293 22.74438 37.624367 24.25 38.455078 C 27.26124 40.116501 30.943792 39.93987 35.976562 39.998047 L 35.988281 40 L 37.998047 40 L 37.998047 72.828125 L 43.169922 78 L 37.169922 84 L 47.169922 94 L 58 94 L 58 40 L 60.011719 40 L 60.021484 39.998047 C 65.054254 39.93987 68.736807 40.116501 71.748047 38.455078 C 73.253667 37.624367 74.444916 36.221293 75.103516 34.466797 C 75.762116 32.712301 76 30.630359 76 28 L 76 12 C 76 9.3696407 75.762116 7.2857458 75.103516 5.53125 C 74.444916 3.7767542 73.253667 2.3736801 71.748047 1.5429688 C 68.736807 -0.11846389 65.054244 0.058176982 60.021484 0 L 60.011719 0 L 35.988281 0 L 35.976562 0 z M 36.023438 3.9980469 L 60 3.9980469 C 65.03826 4.0568136 68.350173 4.2350584 69.814453 5.0429688 C 70.547973 5.4476786 70.967112 5.8977214 71.357422 6.9375 C 71.747742 7.9772786 71.998047 9.6303576 71.998047 12 L 71.998047 28 C 71.998047 30.369642 71.747742 32.022711 71.357422 33.0625 C 70.967112 34.102279 70.547973 34.550368 69.814453 34.955078 C 68.350173 35.762988 65.03825 35.941223 60 36 L 59.976562 36 L 58 36 L 55.998047 36 L 53.998047 36 L 53.998047 90 L 48.828125 90 L 42.828125 84 L 48.828125 78 L 42 71.171875 L 42 36 L 40 36 L 37.998047 36 L 36.023438 36 L 36 36 C 30.96174 35.941233 27.647884 35.762988 26.183594 34.955078 C 25.450074 34.550368 25.032888 34.102279 24.642578 33.0625 C 24.252268 32.022711 24 30.369642 24 28 L 24 12 C 24 9.6303576 24.252268 7.9772786 24.642578 6.9375 C 25.032888 5.8977214 25.450074 5.4476786 26.183594 5.0429688 C 27.650624 4.233549 30.969268 4.0565237 36.023438 3.9980469 z "
|
||||
transform="matrix(0,-1,-1.0003957,0,438.00245,441.36222)"
|
||||
id="path4643" />
|
||||
<ellipse
|
||||
style="color:#000000;fill:#808080;stroke-width:4.00079155;stroke-dashoffset:0.25999999"
|
||||
id="path3281"
|
||||
transform="matrix(0,-1,-1,0,0,0)"
|
||||
cx="-393.36221"
|
||||
cy="-423.99692"
|
||||
rx="5.9999971"
|
||||
ry="6.0023713" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.5 KiB |
@ -34,60 +34,52 @@ import QtQuick.Layouts 1.3
|
||||
import Nymea 1.0
|
||||
import "../components"
|
||||
|
||||
Page {
|
||||
|
||||
SettingsPageBase {
|
||||
id: root
|
||||
header: NymeaHeader {
|
||||
text: qsTr("About %1:core").arg(app.systemName)
|
||||
onBackPressed: pageStack.pop()
|
||||
}
|
||||
title: qsTr("About %1:core").arg(app.systemName)
|
||||
|
||||
Flickable {
|
||||
anchors.fill: parent
|
||||
contentHeight: imprint.implicitHeight
|
||||
|
||||
Imprint {
|
||||
id: imprint
|
||||
width: parent.width
|
||||
title: qsTr("%1:core").arg(app.systemName)
|
||||
githubLink: "https://github.com/nymea/nymea"
|
||||
Imprint {
|
||||
id: imprint
|
||||
Layout.fillWidth: true
|
||||
title: qsTr("%1:core").arg(app.systemName)
|
||||
githubLink: "https://github.com/nymea/nymea"
|
||||
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Connection:")
|
||||
subText: engine.connection.currentConnection.url
|
||||
progressive: false
|
||||
prominentSubText: false
|
||||
}
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Server UUID:")
|
||||
subText: engine.jsonRpcClient.serverUuid
|
||||
progressive: false
|
||||
prominentSubText: false
|
||||
}
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Server version:")
|
||||
subText: engine.jsonRpcClient.serverVersion
|
||||
progressive: false
|
||||
prominentSubText: false
|
||||
}
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("JSON-RPC version:")
|
||||
subText: engine.jsonRpcClient.jsonRpcVersion
|
||||
progressive: false
|
||||
prominentSubText: false
|
||||
}
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Qt version:")
|
||||
visible: engine.jsonRpcClient.ensureServerVersion("4.1")
|
||||
subText: engine.jsonRpcClient.serverQtVersion + (engine.jsonRpcClient.serverQtVersion !== engine.jsonRpcClient.serverQtBuildVersion ? + " (" + qsTr("Built with %1").arg(engine.jsonRpcClient.serverQtBuildVersion) + ")" : "")
|
||||
progressive: false
|
||||
prominentSubText: false
|
||||
}
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Connection:")
|
||||
subText: engine.connection.currentConnection.url
|
||||
progressive: false
|
||||
prominentSubText: false
|
||||
}
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Server UUID:")
|
||||
subText: engine.jsonRpcClient.serverUuid
|
||||
progressive: false
|
||||
prominentSubText: false
|
||||
}
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Server version:")
|
||||
subText: engine.jsonRpcClient.serverVersion
|
||||
progressive: false
|
||||
prominentSubText: false
|
||||
}
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("JSON-RPC version:")
|
||||
subText: engine.jsonRpcClient.jsonRpcVersion
|
||||
progressive: false
|
||||
prominentSubText: false
|
||||
}
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Qt version:")
|
||||
visible: engine.jsonRpcClient.ensureServerVersion("4.1")
|
||||
subText: engine.jsonRpcClient.serverQtVersion + (engine.jsonRpcClient.serverQtVersion !== engine.jsonRpcClient.serverQtBuildVersion ? + " (" + qsTr("Built with %1").arg(engine.jsonRpcClient.serverQtBuildVersion) + ")" : "")
|
||||
progressive: false
|
||||
prominentSubText: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,12 +34,9 @@ import QtQuick.Layouts 1.3
|
||||
import Nymea 1.0
|
||||
import "../components"
|
||||
|
||||
Page {
|
||||
SettingsPageBase {
|
||||
id: root
|
||||
header: NymeaHeader {
|
||||
text: qsTr("Cloud settings")
|
||||
onBackPressed: pageStack.pop();
|
||||
}
|
||||
title: qsTr("%1:core cloud settings").arg(app.systemName)
|
||||
|
||||
Item {
|
||||
id: d
|
||||
@ -59,97 +56,120 @@ Page {
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors { left: parent.left; top: parent.top; right: parent.right }
|
||||
SettingsPageSectionHeader {
|
||||
text: qsTr("Cloud connection")
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
text: qsTr("Connect %1:core to %1:cloud in order to access it from anywhere and send push notifications from %1:core to %2.").arg(app.systemName).arg(app.appName)
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
// Button {
|
||||
// text: "pair"
|
||||
// onClicked: engine.jsonRpcClient.setupRemoteAccess(AWSClient.idToken, AWSClient.userId)
|
||||
// }
|
||||
|
||||
SwitchDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Cloud connection enabled")
|
||||
checked: engine.nymeaConfiguration.cloudEnabled
|
||||
onToggled: {
|
||||
engine.nymeaConfiguration.cloudEnabled = checked;
|
||||
}
|
||||
}
|
||||
|
||||
SettingsPageSectionHeader {
|
||||
text: qsTr("Status")
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
|
||||
ColorIcon {
|
||||
Layout.preferredHeight: busyIndicator.height
|
||||
Layout.preferredWidth: height
|
||||
name: engine.jsonRpcClient.cloudConnectionState === JsonRpcClient.CloudConnectionStateConnected
|
||||
? "../images/cloud.svg"
|
||||
: engine.jsonRpcClient.cloudConnectionState === JsonRpcClient.CloudConnectionStateUnconfigured
|
||||
? "../images/cloud-error.svg"
|
||||
: "../images/cloud-offline.svg"
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
Layout.topMargin: app.margins
|
||||
text: qsTr("You can connect a nymea:box to a nymea:cloud in order to access it from anywhere")
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
// Button {
|
||||
// text: "pair"
|
||||
// onClicked: engine.jsonRpcClient.setupRemoteAccess(AWSClient.idToken, AWSClient.userId)
|
||||
// }
|
||||
|
||||
SwitchDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Cloud connection enabled")
|
||||
checked: engine.nymeaConfiguration.cloudEnabled
|
||||
onToggled: {
|
||||
engine.nymeaConfiguration.cloudEnabled = checked;
|
||||
}
|
||||
}
|
||||
|
||||
ThinDivider {}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
|
||||
ColorIcon {
|
||||
Layout.preferredHeight: busyIndicator.height
|
||||
Layout.preferredWidth: height
|
||||
name: engine.jsonRpcClient.cloudConnectionState === JsonRpcClient.CloudConnectionStateConnected
|
||||
? "../images/cloud.svg"
|
||||
: engine.jsonRpcClient.cloudConnectionState === JsonRpcClient.CloudConnectionStateUnconfigured
|
||||
? "../images/cloud-error.svg"
|
||||
: "../images/cloud-offline.svg"
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
text: {
|
||||
switch (engine.jsonRpcClient.cloudConnectionState) {
|
||||
case JsonRpcClient.CloudConnectionStateDisabled:
|
||||
return qsTr("This box is not connected to %1:cloud").arg(app.systemName)
|
||||
case JsonRpcClient.CloudConnectionStateUnconfigured:
|
||||
if (d.deploymentStarted) {
|
||||
return qsTr("Registering box in %1:cloud...").arg(app.systemName)
|
||||
}
|
||||
return qsTr("This box is not configured to connect to %1:cloud.").arg(app.systemName);
|
||||
case JsonRpcClient.CloudConnectionStateConnecting:
|
||||
return qsTr("Connecting the box to %1:cloud...").arg(app.systemName);
|
||||
case JsonRpcClient.CloudConnectionStateConnected:
|
||||
return qsTr("The box is connected to %1:cloud.").arg(app.systemName);
|
||||
text: {
|
||||
switch (engine.jsonRpcClient.cloudConnectionState) {
|
||||
case JsonRpcClient.CloudConnectionStateDisabled:
|
||||
return qsTr("This box is not connected to %1:cloud").arg(app.systemName)
|
||||
case JsonRpcClient.CloudConnectionStateUnconfigured:
|
||||
if (d.deploymentStarted) {
|
||||
return qsTr("Registering box in %1:cloud...").arg(app.systemName)
|
||||
}
|
||||
return engine.jsonRpcClient.cloudConnectionState
|
||||
return qsTr("This box is not configured to connect to %1:cloud.").arg(app.systemName);
|
||||
case JsonRpcClient.CloudConnectionStateConnecting:
|
||||
return qsTr("Connecting the box to %1:cloud...").arg(app.systemName);
|
||||
case JsonRpcClient.CloudConnectionStateConnected:
|
||||
return qsTr("The box is connected to %1:cloud.").arg(app.systemName);
|
||||
}
|
||||
}
|
||||
BusyIndicator {
|
||||
id: busyIndicator
|
||||
visible: (engine.jsonRpcClient.cloudConnectionState == JsonRpcClient.CloudConnectionStateUnconfigured && d.deploymentStarted) ||
|
||||
engine.jsonRpcClient.cloudConnectionState == JsonRpcClient.CloudConnectionStateConnecting
|
||||
return engine.jsonRpcClient.cloudConnectionState
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
visible: engine.jsonRpcClient.cloudConnectionState === JsonRpcClient.CloudConnectionStateUnconfigured && !d.deploymentStarted
|
||||
text: qsTr("This box is not configured to access the %1:cloud. In order for a box to connect to %1:cloud it needs to be registered first.").arg(app.systemName)
|
||||
wrapMode: Text.WordWrap
|
||||
BusyIndicator {
|
||||
id: busyIndicator
|
||||
visible: (engine.jsonRpcClient.cloudConnectionState == JsonRpcClient.CloudConnectionStateUnconfigured && d.deploymentStarted) ||
|
||||
engine.jsonRpcClient.cloudConnectionState == JsonRpcClient.CloudConnectionStateConnecting
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
visible: engine.jsonRpcClient.cloudConnectionState === JsonRpcClient.CloudConnectionStateUnconfigured && !d.deploymentStarted
|
||||
text: AWSClient.isLoggedIn ? qsTr("Register box") : qsTr("Log in to cloud")
|
||||
onClicked: {
|
||||
if (AWSClient.isLoggedIn) {
|
||||
d.deploymentStarted = true
|
||||
engine.deployCertificate();
|
||||
} else {
|
||||
pageStack.push(Qt.resolvedUrl("qrc:/ui/appsettings/CloudLoginPage.qml"))
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
visible: engine.jsonRpcClient.cloudConnectionState === JsonRpcClient.CloudConnectionStateUnconfigured && !d.deploymentStarted
|
||||
text: qsTr("This box is not configured to access the %1:cloud. In order for a box to connect to %1:cloud it needs to be registered first.").arg(app.systemName)
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
visible: engine.jsonRpcClient.cloudConnectionState === JsonRpcClient.CloudConnectionStateUnconfigured && !d.deploymentStarted
|
||||
text: AWSClient.isLoggedIn ? qsTr("Register box") : qsTr("Log in to cloud")
|
||||
onClicked: {
|
||||
if (AWSClient.isLoggedIn) {
|
||||
d.deploymentStarted = true
|
||||
engine.deployCertificate();
|
||||
} else {
|
||||
pageStack.push(Qt.resolvedUrl("qrc:/ui/appsettings/CloudLoginPage.qml"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsPageSectionHeader {
|
||||
text: qsTr("Remote connection")
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("In order to remotely connect to this %1:core, %2 needs to be logged into %1:cloud as well.").arg(app.systemName).arg(app.appName)
|
||||
}
|
||||
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Go to app settings")
|
||||
subText: qsTr("Set up cloud connection for %1").arg(app.appName)
|
||||
prominentSubText: false
|
||||
onClicked: {
|
||||
pageStack.push(Qt.resolvedUrl("../appsettings/CloudLoginPage.qml"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,127 +34,101 @@ import QtQuick.Layouts 1.3
|
||||
import Nymea 1.0
|
||||
import "../components"
|
||||
|
||||
Page {
|
||||
SettingsPageBase {
|
||||
id: root
|
||||
header: NymeaHeader {
|
||||
text: qsTr("Connection interfaces")
|
||||
onBackPressed: pageStack.pop();
|
||||
title: qsTr("Connection interfaces")
|
||||
|
||||
|
||||
SettingsPageSectionHeader {
|
||||
text: qsTr("TCP server interfaces")
|
||||
}
|
||||
|
||||
Flickable {
|
||||
anchors.fill: parent
|
||||
contentHeight: connectionsColumn.implicitHeight
|
||||
interactive: contentHeight > height
|
||||
|
||||
ColumnLayout {
|
||||
id: connectionsColumn
|
||||
anchors { left: parent.left; top: parent.top; right: parent.right }
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
Layout.topMargin: app.margins
|
||||
text: qsTr("TCP server interfaces")
|
||||
wrapMode: Text.WordWrap
|
||||
color: app.accentColor
|
||||
Repeater {
|
||||
model: engine.nymeaConfiguration.tcpServerConfigurations
|
||||
delegate: ConnectionInterfaceDelegate {
|
||||
Layout.fillWidth: true
|
||||
canDelete: true
|
||||
onClicked: {
|
||||
var component = Qt.createComponent(Qt.resolvedUrl("ServerConfigurationDialog.qml"));
|
||||
var popup = component.createObject(root, { serverConfiguration: engine.nymeaConfiguration.tcpServerConfigurations.get(index).clone() });
|
||||
popup.accepted.connect(function() {
|
||||
engine.nymeaConfiguration.setTcpServerConfiguration(popup.serverConfiguration)
|
||||
popup.serverConfiguration.destroy();
|
||||
})
|
||||
popup.rejected.connect(function() {
|
||||
popup.serverConfiguration.destroy();
|
||||
})
|
||||
popup.open()
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: engine.nymeaConfiguration.tcpServerConfigurations
|
||||
delegate: ConnectionInterfaceDelegate {
|
||||
Layout.fillWidth: true
|
||||
canDelete: true
|
||||
onClicked: {
|
||||
var component = Qt.createComponent(Qt.resolvedUrl("ServerConfigurationDialog.qml"));
|
||||
var popup = component.createObject(root, { serverConfiguration: engine.nymeaConfiguration.tcpServerConfigurations.get(index).clone() });
|
||||
popup.accepted.connect(function() {
|
||||
engine.nymeaConfiguration.setTcpServerConfiguration(popup.serverConfiguration)
|
||||
popup.serverConfiguration.destroy();
|
||||
})
|
||||
popup.rejected.connect(function() {
|
||||
popup.serverConfiguration.destroy();
|
||||
})
|
||||
popup.open()
|
||||
}
|
||||
onDeleteClicked: {
|
||||
print("should delete")
|
||||
engine.nymeaConfiguration.deleteTcpServerConfiguration(model.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
text: qsTr("Add")
|
||||
onClicked: {
|
||||
var config = engine.nymeaConfiguration.createServerConfiguration("0.0.0.0", 2222 + engine.nymeaConfiguration.tcpServerConfigurations.count, false, false);
|
||||
var component = Qt.createComponent(Qt.resolvedUrl("ServerConfigurationDialog.qml"));
|
||||
var popup = component.createObject(root, { serverConfiguration: config });
|
||||
popup.accepted.connect(function() {
|
||||
engine.nymeaConfiguration.setTcpServerConfiguration(popup.serverConfiguration)
|
||||
popup.serverConfiguration.destroy();
|
||||
})
|
||||
popup.rejected.connect(function() {
|
||||
popup.serverConfiguration.destroy();
|
||||
})
|
||||
popup.open()
|
||||
}
|
||||
}
|
||||
|
||||
ThinDivider {}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
Layout.topMargin: app.margins
|
||||
text: qsTr("WebSocket server interfaces")
|
||||
wrapMode: Text.WordWrap
|
||||
color: app.accentColor
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: engine.nymeaConfiguration.webSocketServerConfigurations
|
||||
delegate: ConnectionInterfaceDelegate {
|
||||
Layout.fillWidth: true
|
||||
canDelete: true
|
||||
onClicked: {
|
||||
var component = Qt.createComponent(Qt.resolvedUrl("ServerConfigurationDialog.qml"));
|
||||
var popup = component.createObject(root, { serverConfiguration: engine.nymeaConfiguration.webSocketServerConfigurations.get(index).clone() });
|
||||
popup.accepted.connect(function() {
|
||||
print("configuring:", popup.serverConfiguration.port)
|
||||
engine.nymeaConfiguration.setWebSocketServerConfiguration(popup.serverConfiguration)
|
||||
popup.serverConfiguration.destroy();
|
||||
})
|
||||
popup.rejected.connect(function() {
|
||||
popup.serverConfiguration.destroy();
|
||||
})
|
||||
popup.open()
|
||||
}
|
||||
onDeleteClicked: {
|
||||
print("should delete", model.id)
|
||||
engine.nymeaConfiguration.deleteWebSocketServerConfiguration(model.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
text: qsTr("Add")
|
||||
onClicked: {
|
||||
var config = engine.nymeaConfiguration.createServerConfiguration("0.0.0.0", 4444 + engine.nymeaConfiguration.webSocketServerConfigurations.count, false, false);
|
||||
var component = Qt.createComponent(Qt.resolvedUrl("ServerConfigurationDialog.qml"));
|
||||
var popup = component.createObject(root, { serverConfiguration: config });
|
||||
popup.accepted.connect(function() {
|
||||
engine.nymeaConfiguration.setWebSocketServerConfiguration(popup.serverConfiguration)
|
||||
popup.serverConfiguration.destroy();
|
||||
})
|
||||
popup.rejected.connect(function() {
|
||||
popup.serverConfiguration.destroy();
|
||||
})
|
||||
popup.open()
|
||||
}
|
||||
onDeleteClicked: {
|
||||
print("should delete")
|
||||
engine.nymeaConfiguration.deleteTcpServerConfiguration(model.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
text: qsTr("Add")
|
||||
onClicked: {
|
||||
var config = engine.nymeaConfiguration.createServerConfiguration("0.0.0.0", 2222 + engine.nymeaConfiguration.tcpServerConfigurations.count, false, false);
|
||||
var component = Qt.createComponent(Qt.resolvedUrl("ServerConfigurationDialog.qml"));
|
||||
var popup = component.createObject(root, { serverConfiguration: config });
|
||||
popup.accepted.connect(function() {
|
||||
engine.nymeaConfiguration.setTcpServerConfiguration(popup.serverConfiguration)
|
||||
popup.serverConfiguration.destroy();
|
||||
})
|
||||
popup.rejected.connect(function() {
|
||||
popup.serverConfiguration.destroy();
|
||||
})
|
||||
popup.open()
|
||||
}
|
||||
}
|
||||
|
||||
SettingsPageSectionHeader {
|
||||
text: qsTr("WebSocket server interfaces")
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: engine.nymeaConfiguration.webSocketServerConfigurations
|
||||
delegate: ConnectionInterfaceDelegate {
|
||||
Layout.fillWidth: true
|
||||
canDelete: true
|
||||
onClicked: {
|
||||
var component = Qt.createComponent(Qt.resolvedUrl("ServerConfigurationDialog.qml"));
|
||||
var popup = component.createObject(root, { serverConfiguration: engine.nymeaConfiguration.webSocketServerConfigurations.get(index).clone() });
|
||||
popup.accepted.connect(function() {
|
||||
print("configuring:", popup.serverConfiguration.port)
|
||||
engine.nymeaConfiguration.setWebSocketServerConfiguration(popup.serverConfiguration)
|
||||
popup.serverConfiguration.destroy();
|
||||
})
|
||||
popup.rejected.connect(function() {
|
||||
popup.serverConfiguration.destroy();
|
||||
})
|
||||
popup.open()
|
||||
}
|
||||
onDeleteClicked: {
|
||||
print("should delete", model.id)
|
||||
engine.nymeaConfiguration.deleteWebSocketServerConfiguration(model.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
text: qsTr("Add")
|
||||
onClicked: {
|
||||
var config = engine.nymeaConfiguration.createServerConfiguration("0.0.0.0", 4444 + engine.nymeaConfiguration.webSocketServerConfigurations.count, false, false);
|
||||
var component = Qt.createComponent(Qt.resolvedUrl("ServerConfigurationDialog.qml"));
|
||||
var popup = component.createObject(root, { serverConfiguration: config });
|
||||
popup.accepted.connect(function() {
|
||||
engine.nymeaConfiguration.setWebSocketServerConfiguration(popup.serverConfiguration)
|
||||
popup.serverConfiguration.destroy();
|
||||
})
|
||||
popup.rejected.connect(function() {
|
||||
popup.serverConfiguration.destroy();
|
||||
})
|
||||
popup.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,12 +34,9 @@ import QtQuick.Layouts 1.3
|
||||
import Nymea 1.0
|
||||
import "../components"
|
||||
|
||||
Page {
|
||||
SettingsPageBase {
|
||||
id: root
|
||||
header: NymeaHeader {
|
||||
text: qsTr("Developer tools")
|
||||
onBackPressed: pageStack.pop();
|
||||
}
|
||||
title: qsTr("Developer tools")
|
||||
|
||||
property WebServerConfiguration usedConfig: {
|
||||
var config = null
|
||||
@ -49,7 +46,7 @@ Page {
|
||||
if (tmp.address === engine.connection.currentConnection.hostAddress || tmp.address === "0.0.0.0") {
|
||||
|
||||
// This one prefers https over http...
|
||||
// if (config === null || (!config.sslEnabled && tmp.sslEnabled)) {
|
||||
// if (config === null || (!config.sslEnabled && tmp.sslEnabled)) {
|
||||
|
||||
// ...but for now, prefer http because self signed certs cause trouble and this is meant for local debugging only anyways...
|
||||
if (config === null || (config.sslEnabled && !tmp.sslEnabled)) {
|
||||
@ -61,72 +58,66 @@ Page {
|
||||
return config;
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors { left: parent.left; top: parent.top; right: parent.right }
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
spacing: app.margins
|
||||
Label {
|
||||
text: qsTr("Debug server enabled")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
Switch {
|
||||
id: debugServerEnabledSwitch
|
||||
checked: engine.nymeaConfiguration.debugServerEnabled
|
||||
onClicked: engine.nymeaConfiguration.debugServerEnabled = checked
|
||||
}
|
||||
}
|
||||
SettingsPageSectionHeader {
|
||||
text: qsTr("Debug server")
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
text: qsTr("In order to access the debug interface, please enable the web server.")
|
||||
font.pixelSize: app.smallFont
|
||||
color: "red"
|
||||
wrapMode: Text.WordWrap
|
||||
visible: engine.nymeaConfiguration.webServerConfigurations.count === 0
|
||||
}
|
||||
SwitchDelegate {
|
||||
id: debugServerEnabledSwitch
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Debug server enabled")
|
||||
checked: engine.nymeaConfiguration.debugServerEnabled
|
||||
onToggled: engine.nymeaConfiguration.debugServerEnabled = checked
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
text: qsTr("The web server cannot be reached on %1.").arg(engine.connection.currentConnection.hostAddress)
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: app.smallFont
|
||||
color: "red"
|
||||
visible: engine.nymeaConfiguration.webServerConfigurations.count > 0 && root.usedConfig === null
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
text: qsTr("In order to access the debug interface, please enable the web server.")
|
||||
font.pixelSize: app.smallFont
|
||||
color: "red"
|
||||
wrapMode: Text.WordWrap
|
||||
visible: engine.nymeaConfiguration.webServerConfigurations.count === 0
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
text: qsTr("Please enable the web server to be accessed on this address.")
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: app.smallFont
|
||||
visible: engine.nymeaConfiguration.webServerConfigurations.count > 0 && root.usedConfig === null
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
text: qsTr("The web server cannot be reached on %1.").arg(engine.connection.currentConnection.hostAddress)
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: app.smallFont
|
||||
color: "red"
|
||||
visible: engine.nymeaConfiguration.webServerConfigurations.count > 0 && root.usedConfig === null
|
||||
}
|
||||
|
||||
Button {
|
||||
id: debugServerButton
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
visible: debugServerEnabledSwitch.checked
|
||||
enabled: root.usedConfig !== null && engine.nymeaConfiguration.webServerConfigurations.count > 0
|
||||
text: qsTr("Open debug interface")
|
||||
onClicked: {
|
||||
print("opening:", engine.connection.currentConnection.url)
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
text: qsTr("Please enable the web server to be accessed on this address.")
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: app.smallFont
|
||||
visible: engine.nymeaConfiguration.webServerConfigurations.count > 0 && root.usedConfig === null
|
||||
}
|
||||
|
||||
var proto = "http" + (root.usedConfig.sslEnabled ? "s" : "") + "://"
|
||||
var path = engine.connection.currentConnection.hostAddress + ":" + root.usedConfig.port + "/debug"
|
||||
print("opening:", proto + path)
|
||||
Qt.openUrlExternally(proto + path)
|
||||
}
|
||||
Button {
|
||||
id: debugServerButton
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
visible: debugServerEnabledSwitch.checked
|
||||
enabled: root.usedConfig !== null && engine.nymeaConfiguration.webServerConfigurations.count > 0
|
||||
text: qsTr("Open debug interface")
|
||||
onClicked: {
|
||||
print("opening:", engine.connection.currentConnection.url)
|
||||
|
||||
var proto = "http" + (root.usedConfig.sslEnabled ? "s" : "") + "://"
|
||||
var path = engine.connection.currentConnection.hostAddress + ":" + root.usedConfig.port + "/debug"
|
||||
print("opening:", proto + path)
|
||||
Qt.openUrlExternally(proto + path)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -35,192 +35,213 @@ import QtQuick.Layouts 1.1
|
||||
import Nymea 1.0
|
||||
import "../components"
|
||||
|
||||
Page {
|
||||
SettingsPageBase {
|
||||
id: root
|
||||
header: NymeaHeader {
|
||||
text: qsTr("General settings")
|
||||
backButtonVisible: true
|
||||
onBackPressed: pageStack.pop()
|
||||
title: qsTr("General settings")
|
||||
|
||||
|
||||
SettingsPageSectionHeader {
|
||||
text: qsTr("General")
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: settingsGrid
|
||||
anchors { horizontalCenter: parent.horizontalCenter; top: parent.top; margins: app.margins }
|
||||
width: Math.min(500, parent.width - app.margins * 2)
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
spacing: app.margins
|
||||
|
||||
RowLayout {
|
||||
Label {
|
||||
text: qsTr("Name")
|
||||
Layout.fillWidth: true
|
||||
spacing: app.margins
|
||||
Label {
|
||||
text: qsTr("Name")
|
||||
}
|
||||
TextField {
|
||||
id: nameTextField
|
||||
Layout.fillWidth: true
|
||||
text: engine.nymeaConfiguration.serverName
|
||||
}
|
||||
Button {
|
||||
text: qsTr("OK")
|
||||
visible: nameTextField.displayText !== engine.nymeaConfiguration.serverName
|
||||
onClicked: engine.nymeaConfiguration.serverName = nameTextField.displayText
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
TextField {
|
||||
id: nameTextField
|
||||
Layout.fillWidth: true
|
||||
visible: engine.jsonRpcClient.ensureServerVersion("4.1") && engine.systemController.automaticTimeAvailable
|
||||
Label {
|
||||
text: qsTr("Set date and time automatically")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
CheckBox {
|
||||
checked: engine.systemController.automaticTime
|
||||
onClicked: {
|
||||
engine.systemController.automaticTime = checked
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: app.margins
|
||||
Layout.preferredHeight: dateButton.implicitHeight
|
||||
visible: engine.jsonRpcClient.ensureServerVersion("4.1")
|
||||
Label {
|
||||
text: qsTr("Date")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
Label {
|
||||
text: engine.systemController.serverTime.toLocaleDateString()
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignRight
|
||||
}
|
||||
Button {
|
||||
id: dateButton
|
||||
visible: !engine.systemController.automaticTime && engine.systemController.timeManagementAvailable
|
||||
contentItem: Item {
|
||||
ColorIcon {
|
||||
name: "../images/edit.svg"
|
||||
color: app.foregroundColor
|
||||
anchors.centerIn: parent
|
||||
height: parent.height
|
||||
width: height
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
var popup = datePickerComponent.createObject(root, {dateTime: engine.systemController.serverTime})
|
||||
popup.accepted.connect(function() {
|
||||
print("setting new date", popup.dateTime)
|
||||
engine.systemController.serverTime = popup.dateTime
|
||||
})
|
||||
popup.open();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: app.margins
|
||||
Layout.preferredHeight: timeButton.implicitHeight
|
||||
visible: engine.jsonRpcClient.ensureServerVersion("4.1")
|
||||
Label {
|
||||
text: qsTr("Time")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
Label {
|
||||
text: engine.systemController.serverTime.toLocaleTimeString(/*Locale.ShortTimeString*/)
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignRight
|
||||
}
|
||||
Button {
|
||||
id: timeButton
|
||||
visible: !engine.systemController.automaticTime && engine.systemController.timeManagementAvailable
|
||||
contentItem: Item {
|
||||
ColorIcon {
|
||||
name: "../images/edit.svg"
|
||||
color: app.foregroundColor
|
||||
anchors.centerIn: parent
|
||||
height: parent.height
|
||||
width: height
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
var popup = timePickerComponent.createObject(root, {hour: engine.systemController.serverTime.getHours(), minute: engine.systemController.serverTime.getMinutes()})
|
||||
popup.accepted.connect(function() {
|
||||
var date = new Date(engine.systemController.serverTime)
|
||||
date.setHours(popup.hour);
|
||||
date.setMinutes(popup.minute)
|
||||
engine.systemController.serverTime = date;
|
||||
})
|
||||
popup.open();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: app.margins
|
||||
visible: engine.jsonRpcClient.ensureServerVersion("4.1")
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Time zone")
|
||||
}
|
||||
ComboBox {
|
||||
Layout.minimumWidth: 200
|
||||
model: engine.systemController.timeZones
|
||||
currentIndex: model.indexOf(engine.systemController.serverTimeZone)
|
||||
onActivated: {
|
||||
engine.systemController.serverTimeZone = currentText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Reboot %1:core").arg(app.systemName)
|
||||
visible: engine.systemController.powerManagementAvailable
|
||||
onClicked: {
|
||||
var dialog = Qt.createComponent(Qt.resolvedUrl("../components/MeaDialog.qml"));
|
||||
var text = qsTr("Are you sure you want to reboot your %1:core sytem now?").arg(app.systemName)
|
||||
var popup = dialog.createObject(app,
|
||||
{
|
||||
headerIcon: "../images/dialog-warning-symbolic.svg",
|
||||
title: qsTr("Reboot %1:core").arg(app.systemName),
|
||||
text: text,
|
||||
standardButtons: Dialog.Ok | Dialog.Cancel
|
||||
});
|
||||
popup.open();
|
||||
popup.accepted.connect(function() {
|
||||
engine.systemController.reboot()
|
||||
})
|
||||
}
|
||||
text: engine.nymeaConfiguration.serverName
|
||||
}
|
||||
Button {
|
||||
text: qsTr("OK")
|
||||
visible: nameTextField.displayText !== engine.nymeaConfiguration.serverName
|
||||
onClicked: engine.nymeaConfiguration.serverName = nameTextField.displayText
|
||||
}
|
||||
}
|
||||
|
||||
SettingsPageSectionHeader {
|
||||
text: qsTr("Date and time")
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
visible: engine.jsonRpcClient.ensureServerVersion("4.1") && engine.systemController.automaticTimeAvailable
|
||||
Label {
|
||||
text: qsTr("Set date and time automatically")
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Shutdown %1:core").arg(app.systemName)
|
||||
visible: engine.systemController.powerManagementAvailable
|
||||
}
|
||||
CheckBox {
|
||||
checked: engine.systemController.automaticTime
|
||||
onClicked: {
|
||||
var dialog = Qt.createComponent(Qt.resolvedUrl("../components/MeaDialog.qml"));
|
||||
var text = qsTr("Are you sure you want to shut down your %1:core sytem now?").arg(app.systemName)
|
||||
var popup = dialog.createObject(app,
|
||||
{
|
||||
headerIcon: "../images/dialog-warning-symbolic.svg",
|
||||
title: qsTr("Shut down %1:core").arg(app.systemName),
|
||||
text: text,
|
||||
standardButtons: Dialog.Ok | Dialog.Cancel
|
||||
});
|
||||
popup.open();
|
||||
popup.accepted.connect(function() {
|
||||
engine.systemController.shutdown()
|
||||
})
|
||||
engine.systemController.automaticTime = checked
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
spacing: app.margins
|
||||
Layout.preferredHeight: dateButton.implicitHeight
|
||||
visible: engine.jsonRpcClient.ensureServerVersion("4.1")
|
||||
Label {
|
||||
text: qsTr("Date")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
Label {
|
||||
text: engine.systemController.serverTime.toLocaleDateString()
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignRight
|
||||
}
|
||||
Button {
|
||||
id: dateButton
|
||||
visible: !engine.systemController.automaticTime && engine.systemController.timeManagementAvailable
|
||||
contentItem: Item {
|
||||
ColorIcon {
|
||||
name: "../images/edit.svg"
|
||||
color: app.foregroundColor
|
||||
anchors.centerIn: parent
|
||||
height: parent.height
|
||||
width: height
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
var popup = datePickerComponent.createObject(root, {dateTime: engine.systemController.serverTime})
|
||||
popup.accepted.connect(function() {
|
||||
print("setting new date", popup.dateTime)
|
||||
engine.systemController.serverTime = popup.dateTime
|
||||
})
|
||||
popup.open();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
spacing: app.margins
|
||||
Layout.preferredHeight: timeButton.implicitHeight
|
||||
visible: engine.jsonRpcClient.ensureServerVersion("4.1")
|
||||
Label {
|
||||
text: qsTr("Time")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
Label {
|
||||
text: engine.systemController.serverTime.toLocaleTimeString(/*Locale.ShortTimeString*/)
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignRight
|
||||
}
|
||||
Button {
|
||||
id: timeButton
|
||||
visible: !engine.systemController.automaticTime && engine.systemController.timeManagementAvailable
|
||||
contentItem: Item {
|
||||
ColorIcon {
|
||||
name: "../images/edit.svg"
|
||||
color: app.foregroundColor
|
||||
anchors.centerIn: parent
|
||||
height: parent.height
|
||||
width: height
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
var popup = timePickerComponent.createObject(root, {hour: engine.systemController.serverTime.getHours(), minute: engine.systemController.serverTime.getMinutes()})
|
||||
popup.accepted.connect(function() {
|
||||
var date = new Date(engine.systemController.serverTime)
|
||||
date.setHours(popup.hour);
|
||||
date.setMinutes(popup.minute)
|
||||
engine.systemController.serverTime = date;
|
||||
})
|
||||
popup.open();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
spacing: app.margins
|
||||
visible: engine.jsonRpcClient.ensureServerVersion("4.1")
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Time zone")
|
||||
}
|
||||
ComboBox {
|
||||
Layout.minimumWidth: 200
|
||||
model: engine.systemController.timeZones
|
||||
currentIndex: model.indexOf(engine.systemController.serverTimeZone)
|
||||
onActivated: {
|
||||
engine.systemController.serverTimeZone = currentText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsPageSectionHeader {
|
||||
text: qsTr("System")
|
||||
visible: engine.systemController.powerManagementAvailable
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
text: qsTr("Reboot %1:core").arg(app.systemName)
|
||||
visible: engine.systemController.powerManagementAvailable
|
||||
onClicked: {
|
||||
var dialog = Qt.createComponent(Qt.resolvedUrl("../components/MeaDialog.qml"));
|
||||
var text = qsTr("Are you sure you want to reboot your %1:core sytem now?").arg(app.systemName)
|
||||
var popup = dialog.createObject(app,
|
||||
{
|
||||
headerIcon: "../images/dialog-warning-symbolic.svg",
|
||||
title: qsTr("Reboot %1:core").arg(app.systemName),
|
||||
text: text,
|
||||
standardButtons: Dialog.Ok | Dialog.Cancel
|
||||
});
|
||||
popup.open();
|
||||
popup.accepted.connect(function() {
|
||||
engine.systemController.reboot()
|
||||
})
|
||||
}
|
||||
}
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
text: qsTr("Shutdown %1:core").arg(app.systemName)
|
||||
visible: engine.systemController.powerManagementAvailable
|
||||
onClicked: {
|
||||
var dialog = Qt.createComponent(Qt.resolvedUrl("../components/MeaDialog.qml"));
|
||||
var text = qsTr("Are you sure you want to shut down your %1:core sytem now?").arg(app.systemName)
|
||||
var popup = dialog.createObject(app,
|
||||
{
|
||||
headerIcon: "../images/dialog-warning-symbolic.svg",
|
||||
title: qsTr("Shut down %1:core").arg(app.systemName),
|
||||
text: text,
|
||||
standardButtons: Dialog.Ok | Dialog.Cancel
|
||||
});
|
||||
popup.open();
|
||||
popup.accepted.connect(function() {
|
||||
engine.systemController.shutdown()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Component {
|
||||
id: timePickerComponent
|
||||
Dialog {
|
||||
|
||||
@ -34,140 +34,102 @@ import QtQuick.Layouts 1.3
|
||||
import Nymea 1.0
|
||||
import "../components"
|
||||
|
||||
Page {
|
||||
SettingsPageBase {
|
||||
id: root
|
||||
header: NymeaHeader {
|
||||
text: qsTr("MQTT broker")
|
||||
onBackPressed: pageStack.pop();
|
||||
title: qsTr("MQTT broker")
|
||||
|
||||
SettingsPageSectionHeader {
|
||||
text: qsTr("MQTT Server Interfaces")
|
||||
}
|
||||
|
||||
// Flickable {
|
||||
// anchors.fill: parent
|
||||
// contentHeight: connectionsColumn.implicitHeight
|
||||
// interactive: contentHeight > height
|
||||
Repeater {
|
||||
model: engine.nymeaConfiguration.mqttServerConfigurations
|
||||
|
||||
ColumnLayout {
|
||||
id: connectionsColumn
|
||||
// anchors { left: parent.left; top: parent.top; right: parent.right }
|
||||
anchors.fill: parent
|
||||
// layoutDirection: Qt.
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
Layout.topMargin: app.margins
|
||||
text: qsTr("MQTT Server Interfaces")
|
||||
wrapMode: Text.WordWrap
|
||||
color: app.accentColor
|
||||
delegate: ConnectionInterfaceDelegate {
|
||||
Layout.fillWidth: true
|
||||
canDelete: true
|
||||
onClicked: {
|
||||
var component = Qt.createComponent(Qt.resolvedUrl("ServerConfigurationDialog.qml"));
|
||||
var popup = component.createObject(root, { serverConfiguration: engine.nymeaConfiguration.mqttServerConfigurations.get(index).clone() });
|
||||
popup.accepted.connect(function() {
|
||||
engine.nymeaConfiguration.setMqttServerConfiguration(popup.serverConfiguration)
|
||||
popup.serverConfiguration.destroy();
|
||||
})
|
||||
popup.rejected.connect(function() {
|
||||
popup.serverConfiguration.destroy();
|
||||
})
|
||||
popup.open()
|
||||
}
|
||||
|
||||
ListView {
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumHeight: 0
|
||||
Layout.preferredHeight: Math.min(contentHeight, 120)
|
||||
model: engine.nymeaConfiguration.mqttServerConfigurations
|
||||
clip: true
|
||||
ScrollBar.vertical: ScrollBar {}
|
||||
|
||||
delegate: ConnectionInterfaceDelegate {
|
||||
width: parent.width
|
||||
canDelete: true
|
||||
onClicked: {
|
||||
var component = Qt.createComponent(Qt.resolvedUrl("ServerConfigurationDialog.qml"));
|
||||
var popup = component.createObject(root, { serverConfiguration: engine.nymeaConfiguration.mqttServerConfigurations.get(index).clone() });
|
||||
popup.accepted.connect(function() {
|
||||
engine.nymeaConfiguration.setMqttServerConfiguration(popup.serverConfiguration)
|
||||
popup.serverConfiguration.destroy();
|
||||
})
|
||||
popup.rejected.connect(function() {
|
||||
popup.serverConfiguration.destroy();
|
||||
})
|
||||
popup.open()
|
||||
}
|
||||
|
||||
onDeleteClicked: {
|
||||
engine.nymeaConfiguration.deleteMqttServerConfiguration(model.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
text: qsTr("Add")
|
||||
onClicked: {
|
||||
var config = engine.nymeaConfiguration.createServerConfiguration("0.0.0.0", 1883 + engine.nymeaConfiguration.mqttServerConfigurations.count, false, false);
|
||||
var component = Qt.createComponent(Qt.resolvedUrl("ServerConfigurationDialog.qml"));
|
||||
var popup = component.createObject(root, { serverConfiguration: config });
|
||||
popup.accepted.connect(function() {
|
||||
engine.nymeaConfiguration.setMqttServerConfiguration(popup.serverConfiguration)
|
||||
popup.serverConfiguration.destroy();
|
||||
})
|
||||
popup.rejected.connect(function() {
|
||||
popup.serverConfiguration.destroy();
|
||||
})
|
||||
popup.open()
|
||||
}
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.topMargin: app.margins; Layout.rightMargin: app.margins
|
||||
text: qsTr("MQTT permissions")
|
||||
wrapMode: Text.WordWrap
|
||||
color: app.accentColor
|
||||
}
|
||||
|
||||
ListView {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: Math.min(contentHeight, parent.height * .4)
|
||||
model: engine.nymeaConfiguration.mqttPolicies
|
||||
clip: true
|
||||
ScrollBar.vertical: ScrollBar {}
|
||||
delegate: NymeaListItemDelegate {
|
||||
width: parent.width
|
||||
iconName: "../images/account.svg"
|
||||
text: qsTr("Client ID: %1").arg(model.clientId)
|
||||
subText: qsTr("Username: %1").arg(model.username)
|
||||
progressive: false
|
||||
canDelete: true
|
||||
onClicked: {
|
||||
var page = pageStack.push(Qt.resolvedUrl("MqttPolicyPage.qml"), { policy: engine.nymeaConfiguration.mqttPolicies.get(index).clone() });
|
||||
page.accepted.connect(function() {
|
||||
if (page.policy.clientId !== model.clientId) {
|
||||
engine.nymeaConfiguration.deleteMqttPolicy(model.clientId);
|
||||
}
|
||||
engine.nymeaConfiguration.updateMqttPolicy(page.policy)
|
||||
page.policy.destroy();
|
||||
})
|
||||
page.rejected.connect(function() {
|
||||
page.policy.destroy();
|
||||
})
|
||||
}
|
||||
onDeleteClicked: {
|
||||
engine.nymeaConfiguration.deleteMqttPolicy(model.clientId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
text: qsTr("Add")
|
||||
onClicked: {
|
||||
var page = pageStack.push(Qt.resolvedUrl("MqttPolicyPage.qml"), { policy: engine.nymeaConfiguration.createMqttPolicy() });
|
||||
page.accepted.connect(function() {
|
||||
engine.nymeaConfiguration.updateMqttPolicy(page.policy)
|
||||
page.policy.destroy();
|
||||
})
|
||||
page.rejected.connect(function() {
|
||||
page.policy.destroy();
|
||||
})
|
||||
}
|
||||
}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.minimumHeight: 0
|
||||
onDeleteClicked: {
|
||||
engine.nymeaConfiguration.deleteMqttServerConfiguration(model.id)
|
||||
}
|
||||
}
|
||||
// }
|
||||
}
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
text: qsTr("Add")
|
||||
onClicked: {
|
||||
var config = engine.nymeaConfiguration.createServerConfiguration("0.0.0.0", 1883 + engine.nymeaConfiguration.mqttServerConfigurations.count, false, false);
|
||||
var component = Qt.createComponent(Qt.resolvedUrl("ServerConfigurationDialog.qml"));
|
||||
var popup = component.createObject(root, { serverConfiguration: config });
|
||||
popup.accepted.connect(function() {
|
||||
engine.nymeaConfiguration.setMqttServerConfiguration(popup.serverConfiguration)
|
||||
popup.serverConfiguration.destroy();
|
||||
})
|
||||
popup.rejected.connect(function() {
|
||||
popup.serverConfiguration.destroy();
|
||||
})
|
||||
popup.open()
|
||||
}
|
||||
}
|
||||
|
||||
SettingsPageSectionHeader {
|
||||
text: qsTr("MQTT permissions")
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: engine.nymeaConfiguration.mqttPolicies
|
||||
delegate: NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
iconName: "../images/account.svg"
|
||||
text: qsTr("Client ID: %1").arg(model.clientId)
|
||||
subText: qsTr("Username: %1").arg(model.username)
|
||||
progressive: false
|
||||
canDelete: true
|
||||
onClicked: {
|
||||
var page = pageStack.push(Qt.resolvedUrl("MqttPolicyPage.qml"), { policy: engine.nymeaConfiguration.mqttPolicies.get(index).clone() });
|
||||
page.accepted.connect(function() {
|
||||
if (page.policy.clientId !== model.clientId) {
|
||||
engine.nymeaConfiguration.deleteMqttPolicy(model.clientId);
|
||||
}
|
||||
engine.nymeaConfiguration.updateMqttPolicy(page.policy)
|
||||
page.policy.destroy();
|
||||
})
|
||||
page.rejected.connect(function() {
|
||||
page.policy.destroy();
|
||||
})
|
||||
}
|
||||
onDeleteClicked: {
|
||||
engine.nymeaConfiguration.deleteMqttPolicy(model.clientId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
text: qsTr("Add")
|
||||
onClicked: {
|
||||
var page = pageStack.push(Qt.resolvedUrl("MqttPolicyPage.qml"), { policy: engine.nymeaConfiguration.createMqttPolicy() });
|
||||
page.accepted.connect(function() {
|
||||
engine.nymeaConfiguration.updateMqttPolicy(page.policy)
|
||||
page.policy.destroy();
|
||||
})
|
||||
page.rejected.connect(function() {
|
||||
page.policy.destroy();
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@ import QtQuick.Layouts 1.1
|
||||
import Nymea 1.0
|
||||
import "../components"
|
||||
|
||||
Page {
|
||||
SettingsPageBase {
|
||||
id: root
|
||||
header: NymeaHeader {
|
||||
text: qsTr("Mqtt permission")
|
||||
@ -57,175 +57,176 @@ Page {
|
||||
signal accepted();
|
||||
signal rejected()
|
||||
|
||||
ColumnLayout {
|
||||
anchors { left: parent.left; top: parent.top; right: parent.right; bottom: parent.bottom }
|
||||
RowLayout {
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.topMargin: app.margins
|
||||
spacing: app.margins
|
||||
Label {
|
||||
text: qsTr("Client ID:")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
TextField {
|
||||
id: clientIdTextField
|
||||
Layout.fillWidth: true
|
||||
text: root.policy ? root.policy.clientId : ""
|
||||
onEditingFinished: root.policy.clientId = text
|
||||
placeholderText: qsTr("E.g. Sensor_1")
|
||||
property bool isEmpty: displayText.length === 0
|
||||
property bool isDuplicate: clientIdTextField.displayText != root.policy.clientId && engine.nymeaConfiguration.mqttPolicies.getPolicy(clientIdTextField.displayText) !== null
|
||||
property bool isValid: !isEmpty && !isDuplicate
|
||||
}
|
||||
}
|
||||
Label {
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
text: clientIdTextField.isDuplicate ? qsTr("%1 is already used").arg(clientIdTextField.displayText) : qsTr("Can't be blank")
|
||||
font.pixelSize: app.smallFont
|
||||
Layout.alignment: Qt.AlignRight
|
||||
color: "red"
|
||||
visible: !clientIdTextField.isValid
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
text: qsTr("Client info")
|
||||
color: app.accentColor
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
spacing: app.margins
|
||||
Label {
|
||||
text: qsTr("Client ID:")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
TextField {
|
||||
id: clientIdTextField
|
||||
Layout.fillWidth: true
|
||||
text: root.policy ? root.policy.clientId : ""
|
||||
onEditingFinished: root.policy.clientId = text
|
||||
placeholderText: qsTr("E.g. Sensor_1")
|
||||
property bool isEmpty: displayText.length === 0
|
||||
property bool isDuplicate: clientIdTextField.displayText != root.policy.clientId && engine.nymeaConfiguration.mqttPolicies.getPolicy(clientIdTextField.displayText) !== null
|
||||
property bool isValid: !isEmpty && !isDuplicate
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
text: clientIdTextField.isDuplicate ? qsTr("%1 is already used").arg(clientIdTextField.displayText) : qsTr("Can't be blank")
|
||||
font.pixelSize: app.smallFont
|
||||
Layout.alignment: Qt.AlignRight
|
||||
color: "red"
|
||||
visible: !clientIdTextField.isValid
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
Label {
|
||||
text: qsTr("Username:")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
TextField {
|
||||
id: usernameTextField
|
||||
Layout.fillWidth: true
|
||||
text: root.policy ? root.policy.username : ""
|
||||
onEditingFinished: root.policy.username = text
|
||||
placeholderText: qsTr("Optional")
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
Label {
|
||||
text: qsTr("Password:")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
RowLayout {
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
Label {
|
||||
text: qsTr("Username:")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
TextField {
|
||||
id: usernameTextField
|
||||
id: passwordTextField
|
||||
Layout.fillWidth: true
|
||||
text: root.policy ? root.policy.username : ""
|
||||
onEditingFinished: root.policy.username = text
|
||||
text: root.policy ? root.policy.password : ""
|
||||
onEditingFinished: root.policy.password = text
|
||||
placeholderText: qsTr("Optional")
|
||||
echoMode: hiddenPassword ? TextInput.Password : TextInput.Normal
|
||||
property bool hiddenPassword: true
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
Label {
|
||||
text: qsTr("Password:")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
RowLayout {
|
||||
TextField {
|
||||
id: passwordTextField
|
||||
Layout.fillWidth: true
|
||||
text: root.policy ? root.policy.password : ""
|
||||
onEditingFinished: root.policy.password = text
|
||||
placeholderText: qsTr("Optional")
|
||||
echoMode: hiddenPassword ? TextInput.Password : TextInput.Normal
|
||||
property bool hiddenPassword: true
|
||||
}
|
||||
ColorIcon {
|
||||
Layout.preferredHeight: app.iconSize
|
||||
Layout.preferredWidth: height
|
||||
name: "../images/eye.svg"
|
||||
color: passwordTextField.hiddenPassword ? keyColor : app.accentColor
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: passwordTextField.hiddenPassword = !passwordTextField.hiddenPassword
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ThinDivider {}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.topMargin: app.margins
|
||||
text: qsTr("Allowed publish topics")
|
||||
}
|
||||
ListView {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
model: root.policy.allowedPublishTopicFilters
|
||||
ScrollBar.vertical: ScrollBar {}
|
||||
clip: true
|
||||
delegate: NymeaListItemDelegate {
|
||||
width: parent.width
|
||||
text: modelData
|
||||
canDelete: true
|
||||
progressive: false
|
||||
onDeleteClicked: {
|
||||
root.policy.allowedPublishTopicFilters.splice(index, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
property bool add: false
|
||||
TextField {
|
||||
id: pubField
|
||||
Layout.fillWidth: parent.add
|
||||
Layout.preferredWidth: parent.add ? undefined : 0
|
||||
Behavior on width {
|
||||
NumberAnimation {}
|
||||
}
|
||||
}
|
||||
Button {
|
||||
Layout.fillWidth: !parent.add
|
||||
text: parent.add ? qsTr("OK") : qsTr("Add")
|
||||
enabled: !parent.add || pubField.displayText.length > 0
|
||||
onClicked: {
|
||||
if (parent.add) {
|
||||
root.policy.allowedPublishTopicFilters.push(pubField.displayText)
|
||||
pubField.clear();
|
||||
}
|
||||
parent.add = !parent.add;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ThinDivider {}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.topMargin: app.margins
|
||||
text: qsTr("Allowed subscribe filters")
|
||||
}
|
||||
ListView {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
model: root.policy.allowedSubscribeTopicFilters
|
||||
ScrollBar.vertical: ScrollBar {}
|
||||
clip: true
|
||||
delegate: NymeaListItemDelegate {
|
||||
width: parent.width
|
||||
text: modelData
|
||||
canDelete: true
|
||||
progressive: false
|
||||
onDeleteClicked: {
|
||||
root.policy.allowedSubscribeTopicFilters.splice(index, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.bottomMargin: app.margins
|
||||
property bool add: false
|
||||
TextField {
|
||||
id: subField
|
||||
Layout.fillWidth: parent.add
|
||||
Layout.preferredWidth: parent.add ? undefined : 0
|
||||
}
|
||||
Button {
|
||||
Layout.fillWidth: !parent.add;
|
||||
Behavior on width { NumberAnimation {} }
|
||||
text: parent.add ? qsTr("OK") : qsTr("Add")
|
||||
enabled: !parent.add || subField.displayText.length > 0
|
||||
onClicked: {
|
||||
if (parent.add) {
|
||||
root.policy.allowedSubscribeTopicFilters.push(subField.displayText)
|
||||
subField.clear();
|
||||
}
|
||||
parent.add = !parent.add;
|
||||
ColorIcon {
|
||||
Layout.preferredHeight: app.iconSize
|
||||
Layout.preferredWidth: height
|
||||
name: "../images/eye.svg"
|
||||
color: passwordTextField.hiddenPassword ? keyColor : app.accentColor
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: passwordTextField.hiddenPassword = !passwordTextField.hiddenPassword
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
text: qsTr("Allowed publish topics")
|
||||
color: app.accentColor
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: root.policy.allowedPublishTopicFilters
|
||||
delegate: NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: modelData
|
||||
canDelete: true
|
||||
progressive: false
|
||||
onDeleteClicked: {
|
||||
root.policy.allowedPublishTopicFilters.splice(index, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
property bool add: false
|
||||
TextField {
|
||||
id: pubField
|
||||
Layout.fillWidth: parent.add
|
||||
Layout.preferredWidth: parent.add ? undefined : 0
|
||||
Behavior on width {
|
||||
NumberAnimation {}
|
||||
}
|
||||
}
|
||||
Button {
|
||||
Layout.fillWidth: !parent.add
|
||||
text: parent.add ? qsTr("OK") : qsTr("Add")
|
||||
enabled: !parent.add || pubField.displayText.length > 0
|
||||
onClicked: {
|
||||
if (parent.add) {
|
||||
root.policy.allowedPublishTopicFilters.push(pubField.displayText)
|
||||
pubField.clear();
|
||||
}
|
||||
parent.add = !parent.add;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
text: qsTr("Allowed subscribe filters")
|
||||
color: app.accentColor
|
||||
}
|
||||
Repeater {
|
||||
model: root.policy.allowedSubscribeTopicFilters
|
||||
delegate: NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: modelData
|
||||
canDelete: true
|
||||
progressive: false
|
||||
onDeleteClicked: {
|
||||
root.policy.allowedSubscribeTopicFilters.splice(index, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
property bool add: false
|
||||
TextField {
|
||||
id: subField
|
||||
Layout.fillWidth: parent.add
|
||||
Layout.preferredWidth: parent.add ? undefined : 0
|
||||
}
|
||||
Button {
|
||||
Layout.fillWidth: !parent.add;
|
||||
Behavior on width { NumberAnimation {} }
|
||||
text: parent.add ? qsTr("OK") : qsTr("Add")
|
||||
enabled: !parent.add || subField.displayText.length > 0
|
||||
onClicked: {
|
||||
if (parent.add) {
|
||||
root.policy.allowedSubscribeTopicFilters.push(subField.displayText)
|
||||
subField.clear();
|
||||
}
|
||||
parent.add = !parent.add;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,14 +35,9 @@ import QtQuick.Layouts 1.1
|
||||
import Nymea 1.0
|
||||
import "../components"
|
||||
|
||||
Page {
|
||||
SettingsPageBase {
|
||||
id: root
|
||||
header: NymeaHeader {
|
||||
text: qsTr("Network settings")
|
||||
onBackPressed: {
|
||||
pageStack.pop();
|
||||
}
|
||||
}
|
||||
title: qsTr("Network settings")
|
||||
|
||||
NetworkManager {
|
||||
id: networkManager
|
||||
@ -80,212 +75,222 @@ Page {
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
SettingsPageSectionHeader {
|
||||
text: qsTr("General")
|
||||
}
|
||||
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Current connection state")
|
||||
prominentSubText: false
|
||||
subText: {
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Current connection state")
|
||||
prominentSubText: false
|
||||
subText: {
|
||||
switch (networkManager.state) {
|
||||
case NetworkManager.NetworkManagerStateUnknown:
|
||||
return qsTr("Unknown");
|
||||
case NetworkManager.NetworkManagerStateAsleep:
|
||||
return qsTr("Asleep");
|
||||
case NetworkManager.NetworkManagerStateDisconnected:
|
||||
return qsTr("Disconnected")
|
||||
case NetworkManager.NetworkManagerStateDisconnecting:
|
||||
return qsTr("Disconnecting")
|
||||
case NetworkManager.NetworkManagerStateConnecting:
|
||||
return qsTr("Connecting")
|
||||
case NetworkManager.NetworkManagerStateConnectedLocal:
|
||||
return qsTr("Locally connected")
|
||||
case NetworkManager.NetworkManagerStateConnectedSite:
|
||||
return qsTr("Site connected")
|
||||
case NetworkManager.NetworkManagerStateConnectedGlobal:
|
||||
return qsTr("Globally connected")
|
||||
|
||||
}
|
||||
}
|
||||
progressive: false
|
||||
additionalItem: Led {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
state: {
|
||||
switch (networkManager.state) {
|
||||
case NetworkManager.NetworkManagerStateUnknown:
|
||||
return qsTr("Unknown");
|
||||
case NetworkManager.NetworkManagerStateAsleep:
|
||||
return qsTr("Asleep");
|
||||
return "off";
|
||||
case NetworkManager.NetworkManagerStateDisconnected:
|
||||
return qsTr("Disconnected")
|
||||
case NetworkManager.NetworkManagerStateDisconnecting:
|
||||
return qsTr("Disconnecting")
|
||||
return "red"
|
||||
case NetworkManager.NetworkManagerStateConnecting:
|
||||
return qsTr("Connecting")
|
||||
case NetworkManager.NetworkManagerStateConnectedLocal:
|
||||
return qsTr("Locally connected")
|
||||
case NetworkManager.NetworkManagerStateConnectedSite:
|
||||
return qsTr("Site connected")
|
||||
return "orange"
|
||||
case NetworkManager.NetworkManagerStateConnectedGlobal:
|
||||
return qsTr("Globally connected")
|
||||
return "green";
|
||||
|
||||
}
|
||||
}
|
||||
progressive: false
|
||||
additionalItem: Led {
|
||||
state: {
|
||||
switch (networkManager.state) {
|
||||
case NetworkManager.NetworkManagerStateUnknown:
|
||||
case NetworkManager.NetworkManagerStateAsleep:
|
||||
return "off";
|
||||
case NetworkManager.NetworkManagerStateDisconnected:
|
||||
case NetworkManager.NetworkManagerStateDisconnecting:
|
||||
return "red"
|
||||
case NetworkManager.NetworkManagerStateConnecting:
|
||||
case NetworkManager.NetworkManagerStateConnectedLocal:
|
||||
case NetworkManager.NetworkManagerStateConnectedSite:
|
||||
return "orange"
|
||||
case NetworkManager.NetworkManagerStateConnectedGlobal:
|
||||
return "green";
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Networking enabled")
|
||||
subText: qsTr("Enable or disable networking altogether")
|
||||
prominentSubText: false
|
||||
progressive: false
|
||||
additionalItem: Switch {
|
||||
checked: networkManager.networkingEnabled
|
||||
onClicked: {
|
||||
if (!checked) {
|
||||
var dialog = Qt.createComponent(Qt.resolvedUrl("../components/MeaDialog.qml"));
|
||||
var text = qsTr("Disabling networking will disconnect all connected clients. Be aware that you will not be able to interact remotely with this %1 system any more. Do not proceed unless you know what your are doing.").arg(app.systemName)
|
||||
+ "\n\n"
|
||||
+ qsTr("Do you want to proceed?")
|
||||
var popup = dialog.createObject(app,
|
||||
{
|
||||
headerIcon: "../images/dialog-warning-symbolic.svg",
|
||||
title: qsTr("Disable networking?"),
|
||||
text: text,
|
||||
standardButtons: Dialog.Ok | Dialog.Cancel
|
||||
});
|
||||
popup.open();
|
||||
popup.accepted.connect(function() {
|
||||
networkManager.enableNetworking(false);
|
||||
})
|
||||
popup.rejected.connect(function() {
|
||||
checked = true;
|
||||
})
|
||||
} else {
|
||||
networkManager.enableNetworking(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ThinDivider {}
|
||||
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Wired network")
|
||||
subText: qsTr("Shows the current ethernet status")
|
||||
progressive: false
|
||||
prominentSubText: false
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: networkManager.wiredNetworkDevices
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
iconName: model.pluggedIn ? "../images/network-wired.svg" : "../images/network-wired-offline.svg"
|
||||
text: model.interface + " (" + model.macAddress + ")"
|
||||
subText: {
|
||||
var ret = model.pluggedIn ? qsTr("Plugged in") : qsTr("Unplugged")
|
||||
ret += " - "
|
||||
ret += networkStateToString(model.state)
|
||||
return ret;
|
||||
}
|
||||
progressive: false
|
||||
}
|
||||
}
|
||||
|
||||
ThinDivider {}
|
||||
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Wireless network")
|
||||
subText: qsTr("Enable or disable WiFi")
|
||||
progressive: false
|
||||
prominentSubText: false
|
||||
additionalItem: Switch {
|
||||
checked: networkManager.wirelessNetworkingEnabled
|
||||
onClicked: {
|
||||
if (!checked) {
|
||||
var dialog = Qt.createComponent(Qt.resolvedUrl("../components/MeaDialog.qml"));
|
||||
var text = qsTr("Disabling WiFi will disconnect all clients connected via WiFi. Be aware that you will not be able to interact remotely with this %1 system any more unless a LAN cable is connected.").arg(app.systemName)
|
||||
+ "\n\n"
|
||||
+ qsTr("Do you want to proceed?")
|
||||
var popup = dialog.createObject(app,
|
||||
{
|
||||
headerIcon: "../images/dialog-warning-symbolic.svg",
|
||||
title: qsTr("Disable WiFi?"),
|
||||
text: text,
|
||||
standardButtons: Dialog.Ok | Dialog.Cancel
|
||||
});
|
||||
popup.open();
|
||||
popup.accepted.connect(function() {
|
||||
networkManager.enableWirelessNetworking(false);
|
||||
})
|
||||
popup.rejected.connect(function() {
|
||||
checked = true;
|
||||
})
|
||||
} else {
|
||||
networkManager.enableWirelessNetworking(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: networkManager.wirelessNetworkDevices
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
iconName: {
|
||||
switch (model.state) {
|
||||
case NetworkDevice.NetworkDeviceStateUnknown:
|
||||
case NetworkDevice.NetworkDeviceStateUnmanaged:
|
||||
case NetworkDevice.NetworkDeviceStateUnavailable:
|
||||
case NetworkDevice.NetworkDeviceStateDisconnected:
|
||||
case NetworkDevice.NetworkDeviceStateDeactivating:
|
||||
case NetworkDevice.NetworkDeviceStateFailed:
|
||||
return "../images/network-wifi-offline.svg"
|
||||
case NetworkDevice.NetworkDeviceStatePrepare:
|
||||
return "../images/network-wifi.svg";
|
||||
case NetworkDevice.NetworkDeviceStateConfig:
|
||||
return "../images/network-wifi-offline.svg"
|
||||
case NetworkDevice.NetworkDeviceStateNeedAuth:
|
||||
return "../images/network-wifi.svg";
|
||||
case NetworkDevice.NetworkDeviceStateIpConfig:
|
||||
return "../images/network-wifi-offline.svg"
|
||||
case NetworkDevice.NetworkDeviceStateIpCheck:
|
||||
return "../images/network-wifi.svg";
|
||||
case NetworkDevice.NetworkDeviceStateSecondaries:
|
||||
return "../images/network-wifi-offline.svg"
|
||||
case NetworkDevice.NetworkDeviceStateActivated:
|
||||
return "../images/network-wifi.svg";
|
||||
|
||||
}
|
||||
console.warn("Unhandled enum", model.state)
|
||||
}
|
||||
text: model.interface + " (" + model.macAddress + ")"
|
||||
subText: networkStateToString(model.state)
|
||||
onClicked: {
|
||||
var wirelessNetworkDevice = networkManager.wirelessNetworkDevices.getWirelessNetworkDevice(model.interface);
|
||||
if (wirelessNetworkDevice.state === NetworkDevice.NetworkDeviceStateDisconnected) {
|
||||
networkManager.refreshWifis(model.interface)
|
||||
pageStack.push(wirelessAccessPointsPageComponent, {wirelessNetworkDevice: wirelessNetworkDevice})
|
||||
} else {
|
||||
pageStack.push(currentApPageComponent, {wirelessNetworkDevice: wirelessNetworkDevice})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
}
|
||||
Component {
|
||||
id: wirelessAccessPointsPageComponent
|
||||
Page {
|
||||
id: wirelessAccessPointsPage
|
||||
header: NymeaHeader {
|
||||
text: qsTr("WiFi networks")
|
||||
onBackPressed: {
|
||||
pageStack.pop();
|
||||
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Networking enabled")
|
||||
subText: qsTr("Enable or disable networking altogether")
|
||||
prominentSubText: false
|
||||
progressive: false
|
||||
additionalItem: Switch {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
checked: networkManager.networkingEnabled
|
||||
onClicked: {
|
||||
if (!checked) {
|
||||
var dialog = Qt.createComponent(Qt.resolvedUrl("../components/MeaDialog.qml"));
|
||||
var text = qsTr("Disabling networking will disconnect all connected clients. Be aware that you will not be able to interact remotely with this %1 system any more. Do not proceed unless you know what your are doing.").arg(app.systemName)
|
||||
+ "\n\n"
|
||||
+ qsTr("Do you want to proceed?")
|
||||
var popup = dialog.createObject(app,
|
||||
{
|
||||
headerIcon: "../images/dialog-warning-symbolic.svg",
|
||||
title: qsTr("Disable networking?"),
|
||||
text: text,
|
||||
standardButtons: Dialog.Ok | Dialog.Cancel
|
||||
});
|
||||
popup.open();
|
||||
popup.accepted.connect(function() {
|
||||
networkManager.enableNetworking(false);
|
||||
})
|
||||
popup.rejected.connect(function() {
|
||||
checked = true;
|
||||
})
|
||||
} else {
|
||||
networkManager.enableNetworking(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsPageSectionHeader {
|
||||
text: qsTr("Wired network")
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
text: qsTr("No wired network interfaces available")
|
||||
wrapMode: Text.WordWrap
|
||||
visible: networkManager.wiredNetworkDevices.count == 0
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: networkManager.wiredNetworkDevices
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
iconName: model.pluggedIn ? "../images/network-wired.svg" : "../images/network-wired-offline.svg"
|
||||
text: model.interface + " (" + model.macAddress + ")"
|
||||
subText: {
|
||||
var ret = model.pluggedIn ? qsTr("Plugged in") : qsTr("Unplugged")
|
||||
ret += " - "
|
||||
ret += networkStateToString(model.state)
|
||||
return ret;
|
||||
}
|
||||
progressive: false
|
||||
}
|
||||
}
|
||||
|
||||
SettingsPageSectionHeader {
|
||||
text: qsTr("Wireless network")
|
||||
}
|
||||
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Enabled")
|
||||
subText: qsTr("Enable or disable WiFi")
|
||||
progressive: false
|
||||
prominentSubText: false
|
||||
additionalItem: Switch {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
checked: networkManager.wirelessNetworkingEnabled
|
||||
onClicked: {
|
||||
if (!checked) {
|
||||
var dialog = Qt.createComponent(Qt.resolvedUrl("../components/MeaDialog.qml"));
|
||||
var text = qsTr("Disabling WiFi will disconnect all clients connected via WiFi. Be aware that you will not be able to interact remotely with this %1 system any more unless a LAN cable is connected.").arg(app.systemName)
|
||||
+ "\n\n"
|
||||
+ qsTr("Do you want to proceed?")
|
||||
var popup = dialog.createObject(app,
|
||||
{
|
||||
headerIcon: "../images/dialog-warning-symbolic.svg",
|
||||
title: qsTr("Disable WiFi?"),
|
||||
text: text,
|
||||
standardButtons: Dialog.Ok | Dialog.Cancel
|
||||
});
|
||||
popup.open();
|
||||
popup.accepted.connect(function() {
|
||||
networkManager.enableWirelessNetworking(false);
|
||||
})
|
||||
popup.rejected.connect(function() {
|
||||
checked = true;
|
||||
})
|
||||
} else {
|
||||
networkManager.enableWirelessNetworking(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
text: qsTr("No wired network interfaces available")
|
||||
wrapMode: Text.WordWrap
|
||||
visible: networkManager.wirelessNetworkDevices.count == 0
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: networkManager.wirelessNetworkDevices
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
iconName: {
|
||||
switch (model.state) {
|
||||
case NetworkDevice.NetworkDeviceStateUnknown:
|
||||
case NetworkDevice.NetworkDeviceStateUnmanaged:
|
||||
case NetworkDevice.NetworkDeviceStateUnavailable:
|
||||
case NetworkDevice.NetworkDeviceStateDisconnected:
|
||||
case NetworkDevice.NetworkDeviceStateDeactivating:
|
||||
case NetworkDevice.NetworkDeviceStateFailed:
|
||||
return "../images/network-wifi-offline.svg"
|
||||
case NetworkDevice.NetworkDeviceStatePrepare:
|
||||
return "../images/network-wifi.svg";
|
||||
case NetworkDevice.NetworkDeviceStateConfig:
|
||||
return "../images/network-wifi-offline.svg"
|
||||
case NetworkDevice.NetworkDeviceStateNeedAuth:
|
||||
return "../images/network-wifi.svg";
|
||||
case NetworkDevice.NetworkDeviceStateIpConfig:
|
||||
return "../images/network-wifi-offline.svg"
|
||||
case NetworkDevice.NetworkDeviceStateIpCheck:
|
||||
return "../images/network-wifi.svg";
|
||||
case NetworkDevice.NetworkDeviceStateSecondaries:
|
||||
return "../images/network-wifi-offline.svg"
|
||||
case NetworkDevice.NetworkDeviceStateActivated:
|
||||
return "../images/network-wifi.svg";
|
||||
|
||||
}
|
||||
console.warn("Unhandled enum", model.state)
|
||||
}
|
||||
text: model.interface + " (" + model.macAddress + ")"
|
||||
subText: networkStateToString(model.state)
|
||||
onClicked: {
|
||||
var wirelessNetworkDevice = networkManager.wirelessNetworkDevices.getWirelessNetworkDevice(model.interface);
|
||||
if (wirelessNetworkDevice.state === NetworkDevice.NetworkDeviceStateDisconnected) {
|
||||
networkManager.refreshWifis(model.interface)
|
||||
pageStack.push(wirelessAccessPointsPageComponent, {wirelessNetworkDevice: wirelessNetworkDevice})
|
||||
} else {
|
||||
pageStack.push(currentApPageComponent, {wirelessNetworkDevice: wirelessNetworkDevice})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: wirelessAccessPointsPageComponent
|
||||
SettingsPageBase {
|
||||
id: wirelessAccessPointsPage
|
||||
title: qsTr("WiFi networks")
|
||||
|
||||
property var wirelessNetworkDevice: null
|
||||
|
||||
@ -294,13 +299,11 @@ Page {
|
||||
accessPoints: wirelessAccessPointsPage.wirelessNetworkDevice.accessPoints
|
||||
}
|
||||
|
||||
ListView {
|
||||
Repeater {
|
||||
id: listView
|
||||
anchors.fill: parent
|
||||
model: apProxy
|
||||
ScrollBar.vertical: ScrollBar {}
|
||||
delegate: NymeaListItemDelegate {
|
||||
width: parent.width
|
||||
Layout.fillWidth: true
|
||||
text: model.ssid
|
||||
subText: model.macAddress
|
||||
iconName: {
|
||||
@ -334,106 +337,95 @@ Page {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Component {
|
||||
id: authPageComponent
|
||||
Page {
|
||||
SettingsPageBase {
|
||||
id: authPage
|
||||
|
||||
header: NymeaHeader {
|
||||
text: qsTr("Authenticate")
|
||||
onBackPressed: pageStack.pop()
|
||||
}
|
||||
title: qsTr("Authenticate")
|
||||
|
||||
property var wirelessNetworkDevice: null
|
||||
property var wirelessAccessPoint: null
|
||||
|
||||
ColumnLayout {
|
||||
anchors { left: parent.left; top: parent.top; right: parent.right }
|
||||
|
||||
Label {
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
text: qsTr("Enter the password for %1").arg(authPage.wirelessAccessPoint.ssid)
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
RowLayout {
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
TextField {
|
||||
id: passwordTextField
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
text: qsTr("Enter the password for %1").arg(authPage.wirelessAccessPoint.ssid)
|
||||
wrapMode: Text.WordWrap
|
||||
property bool showPassword: false
|
||||
echoMode: showPassword ? TextInput.Normal : TextInput.Password
|
||||
}
|
||||
RowLayout {
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
TextField {
|
||||
id: passwordTextField
|
||||
Layout.fillWidth: true
|
||||
property bool showPassword: false
|
||||
echoMode: showPassword ? TextInput.Normal : TextInput.Password
|
||||
}
|
||||
|
||||
ColorIcon {
|
||||
Layout.preferredHeight: app.iconSize
|
||||
Layout.preferredWidth: app.iconSize
|
||||
name: "../images/eye.svg"
|
||||
color: passwordTextField.showPassword ? app.accentColor : keyColor
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: passwordTextField.showPassword = !passwordTextField.showPassword
|
||||
}
|
||||
}
|
||||
}
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
text: qsTr("OK")
|
||||
enabled: passwordTextField.displayText.length >= 8
|
||||
onClicked: {
|
||||
networkManager.connectToWiFi(authPage.wirelessNetworkDevice.interface, authPage.wirelessAccessPoint.ssid, passwordTextField.text)
|
||||
pageStack.pop(root);
|
||||
ColorIcon {
|
||||
Layout.preferredHeight: app.iconSize
|
||||
Layout.preferredWidth: app.iconSize
|
||||
name: "../images/eye.svg"
|
||||
color: passwordTextField.showPassword ? app.accentColor : keyColor
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: passwordTextField.showPassword = !passwordTextField.showPassword
|
||||
}
|
||||
}
|
||||
}
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
text: qsTr("OK")
|
||||
enabled: passwordTextField.displayText.length >= 8
|
||||
onClicked: {
|
||||
networkManager.connectToWiFi(authPage.wirelessNetworkDevice.interface, authPage.wirelessAccessPoint.ssid, passwordTextField.text)
|
||||
pageStack.pop(root);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: currentApPageComponent
|
||||
Page {
|
||||
SettingsPageBase {
|
||||
id: currentApPage
|
||||
header: NymeaHeader {
|
||||
text: qsTr("Current connection")
|
||||
onBackPressed: pageStack.pop();
|
||||
}
|
||||
title: qsTr("Current connection")
|
||||
|
||||
property WirelessNetworkDevice wirelessNetworkDevice: null
|
||||
|
||||
GridLayout {
|
||||
anchors { left: parent.left; top: parent.top; right: parent.right }
|
||||
columns: 1
|
||||
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("SSID")
|
||||
subText: currentApPage.wirelessNetworkDevice.currentAccessPoint.ssid
|
||||
progressive: false
|
||||
}
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("MAC Address")
|
||||
subText: currentApPage.wirelessNetworkDevice.currentAccessPoint.macAddress
|
||||
progressive: false
|
||||
}
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Signal strength")
|
||||
subText: currentApPage.wirelessNetworkDevice.currentAccessPoint.signalStrength
|
||||
progressive: false
|
||||
}
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("SSID")
|
||||
subText: currentApPage.wirelessNetworkDevice.currentAccessPoint.ssid
|
||||
progressive: false
|
||||
}
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("MAC Address")
|
||||
subText: currentApPage.wirelessNetworkDevice.currentAccessPoint.macAddress
|
||||
progressive: false
|
||||
}
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Signal strength")
|
||||
subText: currentApPage.wirelessNetworkDevice.currentAccessPoint.signalStrength
|
||||
progressive: false
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
text: qsTr("Disconnect")
|
||||
onClicked: {
|
||||
networkManager.disconnectInterface(currentApPage.wirelessNetworkDevice.interface)
|
||||
pageStack.pop(root);
|
||||
}
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
text: qsTr("Disconnect")
|
||||
onClicked: {
|
||||
networkManager.disconnectInterface(currentApPage.wirelessNetworkDevice.interface)
|
||||
pageStack.pop(root);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@ import QtQuick.Layouts 1.3
|
||||
import "../components"
|
||||
import Nymea 1.0
|
||||
|
||||
Page {
|
||||
SettingsPageBase {
|
||||
id: root
|
||||
header: NymeaHeader {
|
||||
text: qsTr("Plugins")
|
||||
@ -51,36 +51,26 @@ Page {
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("This list shows the list of installed plugins on this %1 system.").arg(app.systemName)
|
||||
}
|
||||
|
||||
ThinDivider {}
|
||||
|
||||
ListView {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
model: PluginsProxy {
|
||||
id: pluginsProxy
|
||||
plugins: engine.deviceManager.plugins
|
||||
}
|
||||
clip: true
|
||||
|
||||
delegate: NymeaListItemDelegate {
|
||||
property var plugin: pluginsProxy.get(index)
|
||||
width: parent.width
|
||||
iconName: "../images/plugin.svg"
|
||||
text: model.name
|
||||
progressive: plugin.paramTypes.count > 0
|
||||
onClicked: pageStack.push(Qt.resolvedUrl("PluginParamsPage.qml"), {plugin: plugin})
|
||||
}
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("This list shows the list of installed plugins on this %1 system.").arg(app.systemName)
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: PluginsProxy {
|
||||
id: pluginsProxy
|
||||
plugins: engine.deviceManager.plugins
|
||||
}
|
||||
|
||||
delegate: NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
property var plugin: pluginsProxy.get(index)
|
||||
iconName: "../images/plugin.svg"
|
||||
text: model.name
|
||||
progressive: plugin.paramTypes.count > 0
|
||||
onClicked: pageStack.push(Qt.resolvedUrl("PluginParamsPage.qml"), {plugin: plugin})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
133
nymea-app/ui/system/UsersSettingsPage.qml
Normal file
133
nymea-app/ui/system/UsersSettingsPage.qml
Normal file
@ -0,0 +1,133 @@
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 2.1
|
||||
import QtQuick.Controls.Material 2.1
|
||||
import QtQuick.Layouts 1.1
|
||||
import Nymea 1.0
|
||||
import "../components"
|
||||
|
||||
SettingsPageBase {
|
||||
id: root
|
||||
title: qsTr("User settings")
|
||||
|
||||
UserManager {
|
||||
id: userManager
|
||||
engine: _engine
|
||||
|
||||
onChangePasswordResponse: {
|
||||
if (error != UserManager.UserErrorNoError) {
|
||||
var component = Qt.createComponent("../components/ErrorDialog.qml")
|
||||
var text;
|
||||
switch (error) {
|
||||
case UserManager.UserErrorBadPassword:
|
||||
text = qsTr("The given password is not valid.");
|
||||
break;
|
||||
case UserManager.UserErrorPermissionDenied:
|
||||
text = qsTr("Permission denied.");
|
||||
break;
|
||||
case UserManager.UserErrorBackendError:
|
||||
text = qsTr("The new password could not be stored.")
|
||||
break;
|
||||
default:
|
||||
text = qsTr("Un unexpected error happened when changing the password. We're sorry for this. (Error code: %1)").arg(error);
|
||||
break;
|
||||
}
|
||||
|
||||
var popup = component.createObject(app, {text: text});
|
||||
popup.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsPageSectionHeader {
|
||||
text: qsTr("User info")
|
||||
}
|
||||
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: userManager.userInfo.username
|
||||
subText: qsTr("Username")
|
||||
progressive: false
|
||||
prominentSubText: false
|
||||
iconName: "../images/account.svg"
|
||||
}
|
||||
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Change password")
|
||||
iconName: "../images/key.svg"
|
||||
onClicked: {
|
||||
var page = pageStack.push(changePasswordComponent)
|
||||
page.confirmed.connect(function(newPassword) {
|
||||
userManager.changePassword(newPassword)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
SettingsPageSectionHeader {
|
||||
text: qsTr("Devices / Apps accessing nymea:core")
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: userManager.tokenInfos
|
||||
|
||||
delegate: NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: model.deviceName
|
||||
subText: qsTr("Created on %1").arg(Qt.formatDateTime(model.creationTime, Qt.DefaultLocaleShortDate))
|
||||
prominentSubText: false
|
||||
progressive: false
|
||||
canDelete: true
|
||||
|
||||
onDeleteClicked: {
|
||||
userManager.removeToken(model.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Component {
|
||||
id: changePasswordComponent
|
||||
SettingsPageBase {
|
||||
id: changePasswordPage
|
||||
title: qsTr("Change password")
|
||||
|
||||
signal confirmed(string newPassword)
|
||||
|
||||
SettingsPageSectionHeader {
|
||||
text: qsTr("Change password")
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
text: qsTr("Please enter the new password for %1").arg(userManager.userInfo.username)
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
PasswordTextField {
|
||||
id: passwordTextField
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
minPasswordLength: 8
|
||||
requireLowerCaseLetter: true
|
||||
requireUpperCaseLetter: true
|
||||
requireNumber: true
|
||||
requireSpecialChar: false
|
||||
signup: true
|
||||
}
|
||||
|
||||
Button {
|
||||
text: qsTr("OK")
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
enabled: passwordTextField.isValid
|
||||
onClicked: {
|
||||
changePasswordPage.confirmed(passwordTextField.password)
|
||||
pageStack.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -34,73 +34,53 @@ import QtQuick.Layouts 1.3
|
||||
import Nymea 1.0
|
||||
import "../components"
|
||||
|
||||
Page {
|
||||
SettingsPageBase {
|
||||
id: root
|
||||
header: NymeaHeader {
|
||||
text: qsTr("Web server")
|
||||
onBackPressed: pageStack.pop();
|
||||
title: qsTr("Web server")
|
||||
|
||||
SettingsPageSectionHeader {
|
||||
text: qsTr("Web server interfaces")
|
||||
}
|
||||
|
||||
Flickable {
|
||||
anchors.fill: parent
|
||||
contentHeight: connectionsColumn.implicitHeight
|
||||
interactive: contentHeight > height
|
||||
|
||||
ColumnLayout {
|
||||
id: connectionsColumn
|
||||
anchors { left: parent.left; top: parent.top; right: parent.right }
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
Layout.topMargin: app.margins
|
||||
text: qsTr("Web server interfaces")
|
||||
wrapMode: Text.WordWrap
|
||||
color: app.accentColor
|
||||
Repeater {
|
||||
model: engine.nymeaConfiguration.webServerConfigurations
|
||||
delegate: ConnectionInterfaceDelegate {
|
||||
Layout.fillWidth: true
|
||||
canDelete: true
|
||||
onClicked: {
|
||||
var component = Qt.createComponent(Qt.resolvedUrl("WebServerConfigurationDialog.qml"));
|
||||
var popup = component.createObject(root, { serverConfiguration: engine.nymeaConfiguration.webServerConfigurations.get(index).clone() });
|
||||
popup.accepted.connect(function() {
|
||||
engine.nymeaConfiguration.setWebServerConfiguration(popup.serverConfiguration)
|
||||
popup.serverConfiguration.destroy();
|
||||
})
|
||||
popup.rejected.connect(function() {
|
||||
popup.serverConfiguration.destroy();
|
||||
})
|
||||
popup.open()
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: engine.nymeaConfiguration.webServerConfigurations
|
||||
delegate: ConnectionInterfaceDelegate {
|
||||
Layout.fillWidth: true
|
||||
canDelete: true
|
||||
onClicked: {
|
||||
var component = Qt.createComponent(Qt.resolvedUrl("WebServerConfigurationDialog.qml"));
|
||||
var popup = component.createObject(root, { serverConfiguration: engine.nymeaConfiguration.webServerConfigurations.get(index).clone() });
|
||||
popup.accepted.connect(function() {
|
||||
engine.nymeaConfiguration.setWebServerConfiguration(popup.serverConfiguration)
|
||||
popup.serverConfiguration.destroy();
|
||||
})
|
||||
popup.rejected.connect(function() {
|
||||
popup.serverConfiguration.destroy();
|
||||
})
|
||||
popup.open()
|
||||
}
|
||||
onDeleteClicked: {
|
||||
print("should delete")
|
||||
engine.nymeaConfiguration.deleteWebServerConfiguration(model.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
text: qsTr("Add")
|
||||
onClicked: {
|
||||
var config = engine.nymeaConfiguration.createWebServerConfiguration("0.0.0.0", 80 + engine.nymeaConfiguration.webServerConfigurations.count, false, false, "/var/www/");
|
||||
var component = Qt.createComponent(Qt.resolvedUrl("WebServerConfigurationDialog.qml"));
|
||||
var popup = component.createObject(root, { serverConfiguration: config });
|
||||
popup.accepted.connect(function() {
|
||||
engine.nymeaConfiguration.setWebServerConfiguration(popup.serverConfiguration)
|
||||
popup.serverConfiguration.destroy();
|
||||
})
|
||||
popup.rejected.connect(function() {
|
||||
popup.serverConfiguration.destroy();
|
||||
})
|
||||
popup.open()
|
||||
}
|
||||
onDeleteClicked: {
|
||||
print("should delete")
|
||||
engine.nymeaConfiguration.deleteWebServerConfiguration(model.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
text: qsTr("Add")
|
||||
onClicked: {
|
||||
var config = engine.nymeaConfiguration.createWebServerConfiguration("0.0.0.0", 80 + engine.nymeaConfiguration.webServerConfigurations.count, false, false, "/var/www/");
|
||||
var component = Qt.createComponent(Qt.resolvedUrl("WebServerConfigurationDialog.qml"));
|
||||
var popup = component.createObject(root, { serverConfiguration: config });
|
||||
popup.accepted.connect(function() {
|
||||
engine.nymeaConfiguration.setWebServerConfiguration(popup.serverConfiguration)
|
||||
popup.serverConfiguration.destroy();
|
||||
})
|
||||
popup.rejected.connect(function() {
|
||||
popup.serverConfiguration.destroy();
|
||||
})
|
||||
popup.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user