diff --git a/client/resources.qrc b/client/resources.qrc index a8f8718e..5b5fd593 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -268,5 +268,6 @@ images/controls/github.svg images/controls/mail.svg images/controls/telegram.svg + ui/qml/Controls2/TextTypes/SmallTextType.qml diff --git a/client/ui/controllers/connectionController.cpp b/client/ui/controllers/connectionController.cpp index dcc15958..aace2857 100644 --- a/client/ui/controllers/connectionController.cpp +++ b/client/ui/controllers/connectionController.cpp @@ -6,39 +6,27 @@ ConnectionController::ConnectionController(const QSharedPointer &serversModel, const QSharedPointer &containersModel, - const QSharedPointer &vpnConnection, - QObject *parent) - : QObject(parent) - , m_serversModel(serversModel) - , m_containersModel(containersModel) - , m_vpnConnection(vpnConnection) + const QSharedPointer &vpnConnection, QObject *parent) + : QObject(parent), m_serversModel(serversModel), m_containersModel(containersModel), m_vpnConnection(vpnConnection) { - connect(m_vpnConnection.get(), - &VpnConnection::connectionStateChanged, - this, - &ConnectionController::connectionStateChanged); - connect(this, - &ConnectionController::connectToVpn, - m_vpnConnection.get(), - &VpnConnection::connectToVpn, + connect(m_vpnConnection.get(), &VpnConnection::connectionStateChanged, this, + &ConnectionController::onConnectionStateChanged); + connect(this, &ConnectionController::connectToVpn, m_vpnConnection.get(), &VpnConnection::connectToVpn, Qt::QueuedConnection); - connect(this, - &ConnectionController::disconnectFromVpn, - m_vpnConnection.get(), - &VpnConnection::disconnectFromVpn, + connect(this, &ConnectionController::disconnectFromVpn, m_vpnConnection.get(), &VpnConnection::disconnectFromVpn, Qt::QueuedConnection); } void ConnectionController::openConnection() { int serverIndex = m_serversModel->getDefaultServerIndex(); - ServerCredentials credentials = qvariant_cast( - m_serversModel->data(serverIndex, ServersModel::ServersModelRoles::CredentialsRole)); + ServerCredentials credentials = + qvariant_cast(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole)); DockerContainer container = m_containersModel->getDefaultContainer(); QModelIndex containerModelIndex = m_containersModel->index(container); - const QJsonObject &containerConfig = qvariant_cast(m_containersModel->data(containerModelIndex, - ContainersModel::Roles::ConfigRole)); + const QJsonObject &containerConfig = + qvariant_cast(m_containersModel->data(containerModelIndex, ContainersModel::Roles::ConfigRole)); if (container == DockerContainer::None) { emit connectionErrorOccurred(tr("VPN Protocols is not installed.\n Please install VPN container at first")); @@ -59,13 +47,65 @@ QString ConnectionController::getLastConnectionError() return errorString(m_vpnConnection->lastError()); } -bool ConnectionController::isConnected() +void ConnectionController::onConnectionStateChanged(Vpn::ConnectionState state) +{ + m_isConnected = false; + m_connectionStateText = tr("Connection..."); + switch (state) { + case Vpn::ConnectionState::Connected: { + m_isConnectionInProgress = false; + m_isConnected = true; + m_connectionStateText = tr("Disconnect"); + break; + } + case Vpn::ConnectionState::Connecting: { + m_isConnectionInProgress = true; + break; + } + case Vpn::ConnectionState::Reconnecting: { + m_isConnectionInProgress = true; + m_connectionStateText = tr("Reconnection..."); + break; + } + case Vpn::ConnectionState::Disconnected: { + m_isConnectionInProgress = false; + m_connectionStateText = tr("Connect"); + break; + } + case Vpn::ConnectionState::Disconnecting: { + m_isConnectionInProgress = true; + m_connectionStateText = tr("Disconnection..."); + break; + } + case Vpn::ConnectionState::Preparing: { + m_isConnectionInProgress = true; + break; + } + case Vpn::ConnectionState::Error: { + m_isConnectionInProgress = false; + emit connectionErrorOccurred(getLastConnectionError()); + break; + } + case Vpn::ConnectionState::Unknown: { + m_isConnectionInProgress = false; + emit connectionErrorOccurred(getLastConnectionError()); + break; + } + } + emit connectionStateChanged(); +} + +QString ConnectionController::connectionStateText() const +{ + return m_connectionStateText; +} + +bool ConnectionController::isConnectionInProgress() const +{ + return m_isConnectionInProgress; +} + +bool ConnectionController::isConnected() const { return m_isConnected; } - -void ConnectionController::setIsConnected(bool isConnected) -{ - m_isConnected = isConnected; - emit isConnectedChanged(); -} diff --git a/client/ui/controllers/connectionController.h b/client/ui/controllers/connectionController.h index c1e81ea3..421ae84f 100644 --- a/client/ui/controllers/connectionController.h +++ b/client/ui/controllers/connectionController.h @@ -11,28 +11,30 @@ class ConnectionController : public QObject Q_OBJECT public: - Q_PROPERTY(bool isConnected READ isConnected WRITE setIsConnected NOTIFY isConnectedChanged) + Q_PROPERTY(bool isConnected READ isConnected NOTIFY connectionStateChanged) + Q_PROPERTY(bool isConnectionInProgress READ isConnectionInProgress NOTIFY connectionStateChanged) + Q_PROPERTY(QString connectionStateText READ connectionStateText NOTIFY connectionStateChanged) explicit ConnectionController(const QSharedPointer &serversModel, const QSharedPointer &containersModel, const QSharedPointer &vpnConnection, QObject *parent = nullptr); - bool isConnected(); - void setIsConnected(bool isConnected); //todo take state from vpnconnection? + bool isConnected() const; + bool isConnectionInProgress() const; + QString connectionStateText() const; public slots: void openConnection(); void closeConnection(); QString getLastConnectionError(); - Vpn::ConnectionState connectionState(){return {};}; //todo update ConnectButton text on page change + void onConnectionStateChanged(Vpn::ConnectionState state); signals: void connectToVpn(int serverIndex, const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig); void disconnectFromVpn(); - void connectionStateChanged(Vpn::ConnectionState state); - void isConnectedChanged(); + void connectionStateChanged(); void connectionErrorOccurred(QString errorMessage); @@ -43,6 +45,8 @@ private: QSharedPointer m_vpnConnection; bool m_isConnected = false; + bool m_isConnectionInProgress = false; + QString m_connectionStateText = tr("Connect"); }; #endif // CONNECTIONCONTROLLER_H diff --git a/client/ui/controllers/exportController.cpp b/client/ui/controllers/exportController.cpp index cbb5a1bb..b6cd7abb 100644 --- a/client/ui/controllers/exportController.cpp +++ b/client/ui/controllers/exportController.cpp @@ -44,7 +44,7 @@ void ExportController::generateConnectionConfig() { int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex(); ServerCredentials credentials = qvariant_cast( - m_serversModel->data(serverIndex, ServersModel::ServersModelRoles::CredentialsRole)); + m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole)); DockerContainer container = static_cast( m_containersModel->getCurrentlyProcessedContainerIndex()); diff --git a/client/ui/controllers/importController.cpp b/client/ui/controllers/importController.cpp index 2724f9cd..99de3131 100644 --- a/client/ui/controllers/importController.cpp +++ b/client/ui/controllers/importController.cpp @@ -89,7 +89,7 @@ void ImportController::importConfig() if (!m_config.value(config_key::containers).toArray().isEmpty()) { auto newServerIndex = m_serversModel->index(m_serversModel->getServersCount() - 1); - m_serversModel->setData(newServerIndex, true, ServersModel::ServersModelRoles::IsDefaultRole); + m_serversModel->setData(newServerIndex, true, ServersModel::Roles::IsDefaultRole); } emit importFinished(); diff --git a/client/ui/controllers/installController.cpp b/client/ui/controllers/installController.cpp index 08ef69d4..5d3bfc2f 100644 --- a/client/ui/controllers/installController.cpp +++ b/client/ui/controllers/installController.cpp @@ -2,40 +2,54 @@ #include -#include "core/servercontroller.h" #include "core/errorstrings.h" +#include "core/servercontroller.h" +#include "utilities.h" InstallController::InstallController(const QSharedPointer &serversModel, - const QSharedPointer &containersModel, - const std::shared_ptr &settings, - QObject *parent) : QObject(parent), m_serversModel(serversModel), m_containersModel(containersModel), m_settings(settings) -{} + const QSharedPointer &containersModel, + const std::shared_ptr &settings, QObject *parent) + : QObject(parent), m_serversModel(serversModel), m_containersModel(containersModel), m_settings(settings) +{ +} void InstallController::install(DockerContainer container, int port, TransportProto transportProto) { Proto mainProto = ContainerProps::defaultProtocol(container); - QJsonObject containerConfig { - { config_key::port, QString::number(port) }, - { config_key::transport_proto, ProtocolProps::transportProtoToString(transportProto, mainProto) } - }; - QJsonObject config { - { config_key::container, ContainerProps::containerToString(container) }, - { ProtocolProps::protoToString(mainProto), containerConfig } - }; + QJsonObject containerConfig { { config_key::port, QString::number(port) }, + { config_key::transport_proto, + ProtocolProps::transportProtoToString(transportProto, mainProto) } }; + QJsonObject config { { config_key::container, ContainerProps::containerToString(container) }, + { ProtocolProps::protoToString(mainProto), containerConfig } }; if (m_shouldCreateServer) { + if (isServerAlreadyExists()) { + return; + } installServer(container, config); } else { installContainer(container, config); } } -void InstallController::installServer(DockerContainer container, QJsonObject& config) +void InstallController::installServer(DockerContainer container, QJsonObject &config) { - //todo check if container already installed ServerController serverController(m_settings); - ErrorCode errorCode = serverController.setupContainer(m_currentlyInstalledServerCredentials, container, config); + + QMap installedContainers; + ErrorCode errorCode = + serverController.getAlreadyInstalledContainers(m_currentlyInstalledServerCredentials, installedContainers); + if (!installedContainers.contains(container)) { + errorCode = serverController.setupContainer(m_currentlyInstalledServerCredentials, container, config); + installedContainers.insert(container, config); + } + + bool isInstalledContainerFound = false; + if (!installedContainers.isEmpty()) { + isInstalledContainerFound = true; + } + if (errorCode == ErrorCode::NoError) { QJsonObject server; server.insert(config_key::hostName, m_currentlyInstalledServerCredentials.hostName); @@ -44,43 +58,123 @@ void InstallController::installServer(DockerContainer container, QJsonObject& co server.insert(config_key::port, m_currentlyInstalledServerCredentials.port); server.insert(config_key::description, m_settings->nextAvailableServerName()); - server.insert(config_key::containers, QJsonArray{ config }); + QJsonArray containerConfigs; + for (const QJsonObject &containerConfig : qAsConst(installedContainers)) { + containerConfigs.append(containerConfig); + } + + server.insert(config_key::containers, containerConfigs); server.insert(config_key::defaultContainer, ContainerProps::containerToString(container)); m_serversModel->addServer(server); auto newServerIndex = m_serversModel->index(m_serversModel->getServersCount() - 1); - m_serversModel->setData(newServerIndex, true, ServersModel::ServersModelRoles::IsDefaultRole); + m_serversModel->setData(newServerIndex, true, ServersModel::Roles::IsDefaultRole); - emit installServerFinished(); + emit installServerFinished(isInstalledContainerFound); return; } emit installationErrorOccurred(errorString(errorCode)); } -void InstallController::installContainer(DockerContainer container, QJsonObject& config) +void InstallController::installContainer(DockerContainer container, QJsonObject &config) { - //todo check if container already installed int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex(); - ServerCredentials serverCredentials = qvariant_cast( - m_serversModel->data(serverIndex, ServersModel::ServersModelRoles::CredentialsRole)); + ServerCredentials serverCredentials = + qvariant_cast(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole)); ServerController serverController(m_settings); - ErrorCode errorCode = serverController.setupContainer(serverCredentials, container, config); + + QMap installedContainers; + ErrorCode errorCode = serverController.getAlreadyInstalledContainers(serverCredentials, installedContainers); + + bool isInstalledContainerFound = false; + if (!installedContainers.isEmpty()) { + isInstalledContainerFound = true; + } + + if (!installedContainers.contains(container)) { + errorCode = serverController.setupContainer(serverCredentials, container, config); + installedContainers.insert(container, config); + } + if (errorCode == ErrorCode::NoError) { - m_containersModel->setData(m_containersModel->index(container), config, ContainersModel::Roles::ConfigRole); - emit installContainerFinished(); + for (auto iterator = installedContainers.begin(); iterator != installedContainers.end(); iterator++) { + auto modelIndex = m_containersModel->index(iterator.key()); + QJsonObject containerConfig = + qvariant_cast(m_containersModel->data(modelIndex, ContainersModel::Roles::ConfigRole)); + if (containerConfig.isEmpty()) { + m_containersModel->setData(m_containersModel->index(iterator.key()), iterator.value(), + ContainersModel::Roles::ConfigRole); + } + } + + emit installContainerFinished(isInstalledContainerFound); return; } emit installationErrorOccurred(errorString(errorCode)); } -void InstallController::setCurrentlyInstalledServerCredentials(const QString &hostName, const QString &userName, const QString &secretData) +bool InstallController::isServerAlreadyExists() +{ + for (int i = 0; i < m_serversModel->getServersCount(); i++) { + auto modelIndex = m_serversModel->index(i); + const ServerCredentials credentials = + qvariant_cast(m_serversModel->data(modelIndex, ServersModel::Roles::CredentialsRole)); + if (m_currentlyInstalledServerCredentials.hostName == credentials.hostName + && m_currentlyInstalledServerCredentials.port == credentials.port) { + emit serverAlreadyExists(i); + return true; + } + } + return false; +} + +void InstallController::scanServerForInstalledContainers() +{ + int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex(); + ServerCredentials serverCredentials = + qvariant_cast(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole)); + + ServerController serverController(m_settings); + + QMap installedContainers; + ErrorCode errorCode = serverController.getAlreadyInstalledContainers(serverCredentials, installedContainers); + + if (errorCode == ErrorCode::NoError) { + bool isInstalledContainerAddedToGui = false; + + for (auto iterator = installedContainers.begin(); iterator != installedContainers.end(); iterator++) { + auto modelIndex = m_containersModel->index(iterator.key()); + QJsonObject containerConfig = + qvariant_cast(m_containersModel->data(modelIndex, ContainersModel::Roles::ConfigRole)); + if (containerConfig.isEmpty()) { + m_containersModel->setData(m_containersModel->index(iterator.key()), iterator.value(), + ContainersModel::Roles::ConfigRole); + isInstalledContainerAddedToGui = true; + } + } + + emit scanServerFinished(isInstalledContainerAddedToGui); + return; + } + + emit installationErrorOccurred(errorString(errorCode)); +} + +QRegularExpression InstallController::ipAddressPortRegExp() +{ + return Utils::ipAddressPortRegExp(); +} + +void InstallController::setCurrentlyInstalledServerCredentials(const QString &hostName, const QString &userName, + const QString &secretData) { m_currentlyInstalledServerCredentials.hostName = hostName; if (m_currentlyInstalledServerCredentials.hostName.contains(":")) { - m_currentlyInstalledServerCredentials.port = m_currentlyInstalledServerCredentials.hostName.split(":").at(1).toInt(); + m_currentlyInstalledServerCredentials.port = + m_currentlyInstalledServerCredentials.hostName.split(":").at(1).toInt(); m_currentlyInstalledServerCredentials.hostName = m_currentlyInstalledServerCredentials.hostName.split(":").at(0); } m_currentlyInstalledServerCredentials.userName = userName; diff --git a/client/ui/controllers/installController.h b/client/ui/controllers/installController.h index d9e1ad95..6b01e102 100644 --- a/client/ui/controllers/installController.h +++ b/client/ui/controllers/installController.h @@ -3,10 +3,10 @@ #include -#include "core/defs.h" #include "containers/containers_defs.h" -#include "ui/models/servers_model.h" +#include "core/defs.h" #include "ui/models/containers_model.h" +#include "ui/models/servers_model.h" class InstallController : public QObject { @@ -14,22 +14,32 @@ class InstallController : public QObject public: explicit InstallController(const QSharedPointer &serversModel, const QSharedPointer &containersModel, - const std::shared_ptr &settings, - QObject *parent = nullptr); + const std::shared_ptr &settings, QObject *parent = nullptr); public slots: void install(DockerContainer container, int port, TransportProto transportProto); - void setCurrentlyInstalledServerCredentials(const QString &hostName, const QString &userName, const QString &secretData); + void setCurrentlyInstalledServerCredentials(const QString &hostName, const QString &userName, + const QString &secretData); void setShouldCreateServer(bool shouldCreateServer); + void scanServerForInstalledContainers(); + + QRegularExpression ipAddressPortRegExp(); + signals: - void installContainerFinished(); - void installServerFinished(); + void installContainerFinished(bool isInstalledContainerFound); + void installServerFinished(bool isInstalledContainerFound); + + void scanServerFinished(bool isInstalledContainerFound); void installationErrorOccurred(QString errorMessage); + + void serverAlreadyExists(int serverIndex); + private: - void installServer(DockerContainer container, QJsonObject& config); - void installContainer(DockerContainer container, QJsonObject& config); + void installServer(DockerContainer container, QJsonObject &config); + void installContainer(DockerContainer container, QJsonObject &config); + bool isServerAlreadyExists(); QSharedPointer m_serversModel; QSharedPointer m_containersModel; diff --git a/client/ui/controllers/pageController.cpp b/client/ui/controllers/pageController.cpp index e49177a5..4ee9b3bf 100644 --- a/client/ui/controllers/pageController.cpp +++ b/client/ui/controllers/pageController.cpp @@ -10,7 +10,7 @@ QString PageController::getInitialPage() if (m_serversModel->getServersCount()) { if (m_serversModel->getDefaultServerIndex() < 0) { auto defaultServerIndex = m_serversModel->index(0); - m_serversModel->setData(defaultServerIndex, true, ServersModel::ServersModelRoles::IsDefaultRole); + m_serversModel->setData(defaultServerIndex, true, ServersModel::Roles::IsDefaultRole); } return getPagePath(PageLoader::PageEnum::PageStart); } else { diff --git a/client/ui/controllers/pageController.h b/client/ui/controllers/pageController.h index 587e0e38..384d3c8d 100644 --- a/client/ui/controllers/pageController.h +++ b/client/ui/controllers/pageController.h @@ -40,14 +40,9 @@ namespace PageLoader }; Q_ENUM_NS(PageEnum) - static void declareQmlPageEnum() { - qmlRegisterUncreatableMetaObject( - PageLoader::staticMetaObject, - "PageEnum", - 1, 0, - "PageEnum", - "Error: only enums" - ); + static void declareQmlPageEnum() + { + qmlRegisterUncreatableMetaObject(PageLoader::staticMetaObject, "PageEnum", 1, 0, "PageEnum", "Error: only enums"); } } @@ -55,8 +50,7 @@ class PageController : public QObject { Q_OBJECT public: - explicit PageController(const QSharedPointer &serversModel, - QObject *parent = nullptr); + explicit PageController(const QSharedPointer &serversModel, QObject *parent = nullptr); public slots: QString getInitialPage(); @@ -64,9 +58,11 @@ public slots: signals: void goToPageHome(); + void goToPageSettings(); void restorePageHomeState(bool isContainerInstalled = false); void replaceStartPage(); void showErrorMessage(QString errorMessage); + void showInfoMessage(QString message); private: QSharedPointer m_serversModel; diff --git a/client/ui/models/containers_model.cpp b/client/ui/models/containers_model.cpp index 1caf6944..f095dd02 100644 --- a/client/ui/models/containers_model.cpp +++ b/client/ui/models/containers_model.cpp @@ -2,7 +2,8 @@ #include "core/servercontroller.h" -ContainersModel::ContainersModel(std::shared_ptr settings, QObject *parent) : m_settings(settings), QAbstractListModel(parent) +ContainersModel::ContainersModel(std::shared_ptr settings, QObject *parent) + : m_settings(settings), QAbstractListModel(parent) { } @@ -21,25 +22,25 @@ bool ContainersModel::setData(const QModelIndex &index, const QVariant &value, i DockerContainer container = ContainerProps::allContainers().at(index.row()); switch (role) { - case NameRole: - // return ContainerProps::containerHumanNames().value(container); - case DescRole: - // return ContainerProps::containerDescriptions().value(container); - case ConfigRole: //todo save to model also - m_settings->setContainerConfig(m_currentlyProcessedServerIndex, - container, - value.toJsonObject()); - case ServiceTypeRole: - // return ContainerProps::containerService(container); - case DockerContainerRole: - // return container; - case IsInstalledRole: - // return m_settings->containers(m_currentlyProcessedServerIndex).contains(container); - case IsDefaultRole: { - m_settings->setDefaultContainer(m_currentlyProcessedServerIndex, container); - m_defaultContainerIndex = container; - emit defaultContainerChanged(); - } + case NameRole: + // return ContainerProps::containerHumanNames().value(container); + case DescRole: + // return ContainerProps::containerDescriptions().value(container); + case ConfigRole: { + m_settings->setContainerConfig(m_currentlyProcessedServerIndex, container, value.toJsonObject()); + m_containers = m_settings->containers(m_currentlyProcessedServerIndex); + } + case ServiceTypeRole: + // return ContainerProps::containerService(container); + case DockerContainerRole: + // return container; + case IsInstalledRole: + // return m_settings->containers(m_currentlyProcessedServerIndex).contains(container); + case IsDefaultRole: { + m_settings->setDefaultContainer(m_currentlyProcessedServerIndex, container); + m_defaultContainerIndex = container; + emit defaultContainerChanged(); + } } emit dataChanged(index, index); @@ -48,40 +49,30 @@ bool ContainersModel::setData(const QModelIndex &index, const QVariant &value, i QVariant ContainersModel::data(const QModelIndex &index, int role) const { - if (!index.isValid() || index.row() < 0 - || index.row() >= ContainerProps::allContainers().size()) { + if (!index.isValid() || index.row() < 0 || index.row() >= ContainerProps::allContainers().size()) { return QVariant(); } DockerContainer container = ContainerProps::allContainers().at(index.row()); switch (role) { - case NameRole: - return ContainerProps::containerHumanNames().value(container); - case DescRole: - return ContainerProps::containerDescriptions().value(container); - case ConfigRole: { - if (container == DockerContainer::None) return QJsonObject(); - return m_containers.value(container); + case NameRole: return ContainerProps::containerHumanNames().value(container); + case DescRole: return ContainerProps::containerDescriptions().value(container); + case ConfigRole: { + if (container == DockerContainer::None) { + return QJsonObject(); } - case ServiceTypeRole: - return ContainerProps::containerService(container); - case DockerContainerRole: - return container; - case IsEasySetupContainerRole: - return ContainerProps::isEasySetupContainer(container); - case EasySetupHeaderRole: - return ContainerProps::easySetupHeader(container); - case EasySetupDescriptionRole: - return ContainerProps::easySetupDescription(container); - case IsInstalledRole: - return m_containers.contains(container); - case IsCurrentlyProcessedRole: - return container == static_cast(m_currentlyProcessedContainerIndex); - case IsDefaultRole: - return container == m_defaultContainerIndex; - case IsSupportedRole: - return ContainerProps::isSupportedByCurrentPlatform(container); + return m_containers.value(container); + } + case ServiceTypeRole: return ContainerProps::containerService(container); + case DockerContainerRole: return container; + case IsEasySetupContainerRole: return ContainerProps::isEasySetupContainer(container); + case EasySetupHeaderRole: return ContainerProps::easySetupHeader(container); + case EasySetupDescriptionRole: return ContainerProps::easySetupDescription(container); + case IsInstalledRole: return m_containers.contains(container); + case IsCurrentlyProcessedRole: return container == static_cast(m_currentlyProcessedContainerIndex); + case IsDefaultRole: return container == m_defaultContainerIndex; + case IsSupportedRole: return ContainerProps::isSupportedByCurrentPlatform(container); } return QVariant(); @@ -130,7 +121,7 @@ void ContainersModel::removeAllContainers() endResetModel(); } - //todo process errors + // todo process errors } void ContainersModel::clearCachedProfiles() @@ -141,7 +132,8 @@ void ContainersModel::clearCachedProfiles() } } -QHash ContainersModel::roleNames() const { +QHash ContainersModel::roleNames() const +{ QHash roles; roles[NameRole] = "name"; roles[DescRole] = "description"; diff --git a/client/ui/models/servers_model.cpp b/client/ui/models/servers_model.cpp index f027a90d..94267ed1 100644 --- a/client/ui/models/servers_model.cpp +++ b/client/ui/models/servers_model.cpp @@ -1,6 +1,7 @@ #include "servers_model.h" -ServersModel::ServersModel(std::shared_ptr settings, QObject *parent) : m_settings(settings), QAbstractListModel(parent) +ServersModel::ServersModel(std::shared_ptr settings, QObject *parent) + : m_settings(settings), QAbstractListModel(parent) { m_servers = m_settings->serversArray(); m_defaultServerIndex = m_settings->defaultServerIndex(); @@ -14,8 +15,7 @@ int ServersModel::rowCount(const QModelIndex &parent) const bool ServersModel::setData(const QModelIndex &index, const QVariant &value, int role) { - if (!index.isValid() || index.row() < 0 - || index.row() >= static_cast(m_servers.size())) { + if (!index.isValid() || index.row() < 0 || index.row() >= static_cast(m_servers.size())) { return false; } @@ -29,8 +29,7 @@ bool ServersModel::setData(const QModelIndex &index, const QVariant &value, int break; } case IsDefaultRole: { - m_settings->setDefaultServer(index.row()); - m_defaultServerIndex = m_settings->defaultServerIndex(); + setDefaultServerIndex(index.row()); break; } default: { @@ -58,16 +57,11 @@ QVariant ServersModel::data(const QModelIndex &index, int role) const } return description; } - case HostNameRole: - return server.value(config_key::hostName).toString(); - case CredentialsRole: - return QVariant::fromValue(m_settings->serverCredentials(index.row())); - case CredentialsLoginRole: - return m_settings->serverCredentials(index.row()).userName; - case IsDefaultRole: - return index.row() == m_defaultServerIndex; - case IsCurrentlyProcessedRole: - return index.row() == m_currenlyProcessedServerIndex; + case HostNameRole: return server.value(config_key::hostName).toString(); + case CredentialsRole: return QVariant::fromValue(m_settings->serverCredentials(index.row())); + case CredentialsLoginRole: return m_settings->serverCredentials(index.row()).userName; + case IsDefaultRole: return index.row() == m_defaultServerIndex; + case IsCurrentlyProcessedRole: return index.row() == m_currenlyProcessedServerIndex; } return QVariant(); @@ -92,6 +86,7 @@ const int ServersModel::getServersCount() void ServersModel::setCurrentlyProcessedServerIndex(int index) { m_currenlyProcessedServerIndex = index; + emit currentlyProcessedServerIndexChanged(); } int ServersModel::getCurrentlyProcessedServerIndex() @@ -104,6 +99,12 @@ bool ServersModel::isDefaultServerCurrentlyProcessed() return m_defaultServerIndex == m_currenlyProcessedServerIndex; } +bool ServersModel::isCurrentlyProcessedServerHasWriteAccess() +{ + auto credentials = m_settings->serverCredentials(m_currenlyProcessedServerIndex); + return (!credentials.userName.isEmpty() && !credentials.secretData.isEmpty()); +} + void ServersModel::addServer(const QJsonObject &server) { beginResetModel(); @@ -119,18 +120,19 @@ void ServersModel::removeServer() m_servers = m_settings->serversArray(); if (m_settings->defaultServerIndex() == m_currenlyProcessedServerIndex) { - m_settings->setDefaultServer(0); + setDefaultServerIndex(0); } else if (m_settings->defaultServerIndex() > m_currenlyProcessedServerIndex) { - m_settings->setDefaultServer(m_settings->defaultServerIndex() - 1); + setDefaultServerIndex(m_settings->defaultServerIndex() - 1); } if (m_settings->serversCount() == 0) { - m_settings->setDefaultServer(-1); + setDefaultServerIndex(-1); } endResetModel(); } -QHash ServersModel::roleNames() const { +QHash ServersModel::roleNames() const +{ QHash roles; roles[NameRole] = "name"; roles[HostNameRole] = "hostName"; @@ -140,3 +142,9 @@ QHash ServersModel::roleNames() const { roles[IsCurrentlyProcessedRole] = "isCurrentlyProcessed"; return roles; } + +void ServersModel::setDefaultServerIndex(const int index) +{ + m_settings->setDefaultServer(index); + m_defaultServerIndex = m_settings->defaultServerIndex(); +} diff --git a/client/ui/models/servers_model.h b/client/ui/models/servers_model.h index be13d61b..891d6ad1 100644 --- a/client/ui/models/servers_model.h +++ b/client/ui/models/servers_model.h @@ -5,7 +5,8 @@ #include "settings.h" -struct ServerModelContent { +struct ServerModelContent +{ QString desc; QString address; bool isDefault; @@ -15,7 +16,7 @@ class ServersModel : public QAbstractListModel { Q_OBJECT public: - enum ServersModelRoles { + enum Roles { NameRole = Qt::UserRole + 1, HostNameRole, CredentialsRole, @@ -35,6 +36,7 @@ public: public slots: const int getDefaultServerIndex(); bool isDefaultServerCurrentlyProcessed(); + bool isCurrentlyProcessedServerHasWriteAccess(); const int getServersCount(); @@ -47,7 +49,12 @@ public slots: protected: QHash roleNames() const override; +signals: + void currentlyProcessedServerIndexChanged(); + private: + void setDefaultServerIndex(const int index); + QJsonArray m_servers; std::shared_ptr m_settings; diff --git a/client/ui/qml/Components/ConnectButton.qml b/client/ui/qml/Components/ConnectButton.qml index 3c34a6b0..626c4ffb 100644 --- a/client/ui/qml/Components/ConnectButton.qml +++ b/client/ui/qml/Components/ConnectButton.qml @@ -15,7 +15,7 @@ Button { } } - text: qsTr("Connect") + text: ConnectionController.connectionStateText background: Item { clip: true @@ -26,13 +26,21 @@ Button { Image { id: border - source: connectionProccess.running ? "/images/connectionProgress.svg" : - ConnectionController.isConnected ? "/images/connectionOff.svg" : "/images/connectionOn.svg" + source: { + if (ConnectionController.isConnectionInProgress) { + return "/images/connectionProgress.svg" + } else if (ConnectionController.isConnected) { + return "/images/connectionOff.svg" + } else { + return "/images/connectionOn.svg" + } + } + RotationAnimator { id: connectionProccess target: border - running: false + running: ConnectionController.isConnectionInProgress from: 0 to: 360 loops: Animation.Infinite @@ -67,63 +75,12 @@ Button { } onClicked: { - connectionProccess.running ? ConnectionController.closeConnection() : ConnectionController.openConnection() - } - - Connections { - target: ConnectionController - function onConnectionStateChanged(state) { - switch(state) { - case ConnectionState.Unknown: { - console.log("Unknown") - break - } - case ConnectionState.Disconnected: { - console.log("Disconnected") - connectionProccess.running = false - root.text = qsTr("Connect") - ConnectionController.isConnected = false - break - } - case ConnectionState.Preparing: { - console.log("Preparing") - connectionProccess.running = true - root.text = qsTr("Connection...") - break - } - case ConnectionState.Connecting: { - console.log("Connecting") - connectionProccess.running = true - root.text = qsTr("Connection...") - break - } - case ConnectionState.Connected: { - console.log("Connected") - connectionProccess.running = false - root.text = qsTr("Disconnect") - ConnectionController.isConnected = true - break - } - case ConnectionState.Disconnecting: { - console.log("Disconnecting") - connectionProccess.running = true - root.text = qsTr("Disconnection...") - break - } - case ConnectionState.Reconnecting: { - console.log("Reconnecting") - connectionProccess.running = true - root.text = qsTr("Reconnection...") - break - } - case ConnectionState.Error: { - console.log("Error") - connectionProccess.running = false - root.text = qsTr("Connect") - PageController.showErrorMessage(ConnectionController.getLastConnectionError()) - break - } - } + if (ConnectionController.isConnectionInProgress) { + ConnectionController.closeConnection() + } else if (ConnectionController.isConnected) { + ConnectionController.closeConnection() + } else { + ConnectionController.openConnection() } } } diff --git a/client/ui/qml/Components/ShareConnectionDrawer.qml b/client/ui/qml/Components/ShareConnectionDrawer.qml index 627eba81..e03738c8 100644 --- a/client/ui/qml/Components/ShareConnectionDrawer.qml +++ b/client/ui/qml/Components/ShareConnectionDrawer.qml @@ -141,6 +141,7 @@ DrawerType { Layout.bottomMargin: 16 padding: 0 + leftPadding: 0 height: 24 color: "#D7D8DB" diff --git a/client/ui/qml/Controls2/TextFieldWithHeaderType.qml b/client/ui/qml/Controls2/TextFieldWithHeaderType.qml index b41f0b40..8ad92d54 100644 --- a/client/ui/qml/Controls2/TextFieldWithHeaderType.qml +++ b/client/ui/qml/Controls2/TextFieldWithHeaderType.qml @@ -2,6 +2,8 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts +import "TextTypes" + Item { id: root @@ -34,15 +36,10 @@ Item { anchors.fill: backgroud ColumnLayout { - Text { + LabelTextType { text: root.headerText color: "#878b91" - font.pixelSize: 13 - font.weight: 400 - font.family: "PT Root UI VF" - font.letterSpacing: 0.02 - height: 16 Layout.fillWidth: true Layout.rightMargin: 16 Layout.leftMargin: 16 diff --git a/client/ui/qml/Controls2/TextTypes/SmallTextType.qml b/client/ui/qml/Controls2/TextTypes/SmallTextType.qml new file mode 100644 index 00000000..96f3342d --- /dev/null +++ b/client/ui/qml/Controls2/TextTypes/SmallTextType.qml @@ -0,0 +1,12 @@ +import QtQuick + +Text { + height: 20 + + color: "#D7D8DB" + font.pixelSize: 14 + font.weight: Font.Normal + font.family: "PT Root UI VF" + + wrapMode: Text.WordWrap +} diff --git a/client/ui/qml/Pages2/PageHome.qml b/client/ui/qml/Pages2/PageHome.qml index 282c1408..a4e90117 100644 --- a/client/ui/qml/Pages2/PageHome.qml +++ b/client/ui/qml/Pages2/PageHome.qml @@ -21,8 +21,8 @@ PageType { property string borderColor: "#2C2D30" - property string currentServerName: serversMenuContent.currentItem.delegateData.name - property string currentServerHostName: serversMenuContent.currentItem.delegateData.hostName + property string currentServerName + property string currentServerHostName property string currentContainerName ConnectButton { @@ -93,7 +93,15 @@ PageType { Layout.bottomMargin: 44 Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter - text: root.currentContainerName + " | " + root.currentServerHostName + text: { + var string = "" + if (SettingsController.isAmneziaDnsEnabled()) { + string += "Amnezia DNS | " + } + + string += root.currentContainerName + " | " + root.currentServerHostName + return string + } } } @@ -153,6 +161,8 @@ PageType { headerBackButtonImage: "qrc:/images/controls/arrow-left.svg" rootButtonClickedFunction: function() { + // todo check if server index changed before set Currently processed + // todo make signal slot for change server index in containersModel ServersModel.setCurrentlyProcessedServerIndex(serversMenuContent.currentIndex) ContainersModel.setCurrentlyProcessedServerIndex(serversMenuContent.currentIndex) containersDropDown.menuVisible = true @@ -161,20 +171,45 @@ PageType { listView: HomeContainersListView { rootWidth: root.width + Connections { + target: ServersModel + + function onCurrentlyProcessedServerIndexChanged() { + updateContainersModelFilters() + } + + function updateContainersModelFilters() { + if (ServersModel.isCurrentlyProcessedServerHasWriteAccess()) { + proxyContainersModel.filters = [serviceTypeFilter, supportedFilter] + } else { + proxyContainersModel.filters = installedFilter + } + } + } + + ValueFilter { + id: serviceTypeFilter + roleName: "serviceType" + value: ProtocolEnum.Vpn + } + ValueFilter { + id: supportedFilter + roleName: "isSupported" + value: true + } + ValueFilter { + id: installedFilter + roleName: "isInstalled" + value: true + } + model: SortFilterProxyModel { id: proxyContainersModel sourceModel: ContainersModel - filters: [ - ValueFilter { - roleName: "serviceType" - value: ProtocolEnum.Vpn - }, - ValueFilter { - roleName: "isSupported" - value: true - } - ] + + Component.onCompleted: updateContainersModelFilters() } + currentIndex: ContainersModel.getDefaultContainer() } } @@ -272,6 +307,9 @@ PageType { isDefault = true ContainersModel.setCurrentlyProcessedServerIndex(index) + + root.currentServerName = name + root.currentServerHostName = hostName } MouseArea { @@ -302,6 +340,13 @@ PageType { Layout.fillWidth: true } } + + Component.onCompleted: { + if (serversMenuContent.currentIndex === index) { + root.currentServerName = name + root.currentServerHostName = hostName + } + } } } } diff --git a/client/ui/qml/Pages2/PageSettingsServerData.qml b/client/ui/qml/Pages2/PageSettingsServerData.qml index 6179983b..b98d2b8c 100644 --- a/client/ui/qml/Pages2/PageSettingsServerData.qml +++ b/client/ui/qml/Pages2/PageSettingsServerData.qml @@ -14,6 +14,21 @@ import "../Components" PageType { id: root + Connections { + target: InstallController + + function onScanServerFinished(isInstalledContainerFound) { + var message = "" + if (isInstalledContainerFound) { + message = qsTr("All installed containers have been added to the application") + } else { + message = qsTr("Не найдено установленных контейнеров") + } + + PageController.showErrorMessage(message) + } + } + FlickableType { id: fl anchors.top: parent.top @@ -30,8 +45,8 @@ PageType { LabelWithButtonType { Layout.fillWidth: true - text: "Clear Amnezia cache" - descriptionText: "May be needed when changing other settings" + text: qsTr("Clear Amnezia cache") + descriptionText: qsTr("May be needed when changing other settings") clickedFunction: function() { questionDrawer.headerText = qsTr("Clear cached profiles?") @@ -52,6 +67,19 @@ PageType { DividerType {} + LabelWithButtonType { + Layout.fillWidth: true + + text: qsTr("Проверить сервер на наличие ранее установленных сервисов Amnezia") + descriptionText: qsTr("Добавим их в приложение, если они не отображались") + + clickedFunction: function() { + InstallController.scanServerForInstalledContainers() + } + } + + DividerType {} + LabelWithButtonType { Layout.fillWidth: true diff --git a/client/ui/qml/Pages2/PageSetupWizardCredentials.qml b/client/ui/qml/Pages2/PageSetupWizardCredentials.qml index 3f037035..fa6b1f92 100644 --- a/client/ui/qml/Pages2/PageSetupWizardCredentials.qml +++ b/client/ui/qml/Pages2/PageSetupWizardCredentials.qml @@ -7,6 +7,7 @@ import PageEnum 1.0 import "./" import "../Controls2" import "../Config" +import "../Controls2/TextTypes" PageType { id: root @@ -42,28 +43,32 @@ PageType { HeaderType { Layout.fillWidth: true - headerText: "Подключение к серверу" + headerText: qsTr("Server connection") } TextFieldWithHeaderType { id: hostname Layout.fillWidth: true - headerText: "Server IP address [:port]" + headerText: qsTr("Server IP address [:port]") + textFieldPlaceholderText: qsTr("Enter the address in the format 255.255.255.255:88") + textField.validator: RegularExpressionValidator { + regularExpression: InstallController.ipAddressPortRegExp() + } } TextFieldWithHeaderType { id: username Layout.fillWidth: true - headerText: "Login to connect via SSH" + headerText: qsTr("Login to connect via SSH") } TextFieldWithHeaderType { id: secretData Layout.fillWidth: true - headerText: "Password / Private key" + headerText: qsTr("Password / Private key") textField.echoMode: TextInput.Password } @@ -71,7 +76,7 @@ PageType { Layout.fillWidth: true Layout.topMargin: 24 - text: qsTr("Настроить сервер простым образом") + text: qsTr("Set up a server the easy way") onClicked: function() { InstallController.setShouldCreateServer(true) @@ -92,7 +97,7 @@ PageType { textColor: "#D7D8DB" borderWidth: 1 - text: qsTr("Выбрать протокол для установки") + text: qsTr("Select protocol to install") onClicked: function() { InstallController.setShouldCreateServer(true) diff --git a/client/ui/qml/Pages2/PageSetupWizardInstalling.qml b/client/ui/qml/Pages2/PageSetupWizardInstalling.qml index 9631e258..d0fdeabd 100644 --- a/client/ui/qml/Pages2/PageSetupWizardInstalling.qml +++ b/client/ui/qml/Pages2/PageSetupWizardInstalling.qml @@ -24,7 +24,7 @@ PageType { PageController.showErrorMessage(errorMessage) } - function onInstallContainerFinished() { + function onInstallContainerFinished(isInstalledContainerFound) { goToStartPage() if (stackView.currentItem.objectName === PageController.getPagePath(PageEnum.PageHome)) { PageController.restorePageHomeState(true) @@ -34,9 +34,15 @@ PageType { } else { goToPage(PageEnum.PageHome) } + + if (isInstalledContainerFound) { + //todo change to info message + PageController.showErrorMessage(qsTr("The container you are trying to install is already installed on the server. " + + "All installed containers have been added to the application")) + } } - function onInstallServerFinished() { + function onInstallServerFinished(isInstalledContainerFound) { goToStartPage() if (stackView.currentItem.objectName === PageController.getPagePath(PageEnum.PageHome)) { PageController.restorePageHomeState() @@ -46,6 +52,19 @@ PageType { var pagePath = PageController.getPagePath(PageEnum.PageStart) stackView.replace(pagePath, { "objectName" : pagePath }) } + + if (isInstalledContainerFound) { + PageController.showErrorMessage(qsTr("The container you are trying to install is already installed on the server. " + + "All installed containers have been added to the application")) + } + } + + function onServerAlreadyExists(serverIndex) { + goToStartPage() + ServersModel.setCurrentlyProcessedServerIndex(serverIndex) + goToPage(PageEnum.PageSettingsServerInfo, false) + + PageController.showErrorMessage(qsTr("The server has already been added to the application")) } } diff --git a/client/ui/qml/Pages2/PageStart.qml b/client/ui/qml/Pages2/PageStart.qml index a91cab6a..1307ee05 100644 --- a/client/ui/qml/Pages2/PageStart.qml +++ b/client/ui/qml/Pages2/PageStart.qml @@ -20,6 +20,11 @@ PageType { tabBarStackView.goToTabBarPage(PageController.getPagePath(PageEnum.PageHome)) } + function onGoToPageSettings() { + tabBar.currentIndex = 2 + tabBarStackView.goToTabBarPage(PageController.getPagePath(PageEnum.PageSettings)) + } + function onShowErrorMessage(errorMessage) { popupErrorMessage.popupErrorMessageText = errorMessage popupErrorMessage.open()