diff --git a/client/core/defs.h b/client/core/defs.h index 3a3ff565..61c45e4e 100644 --- a/client/core/defs.h +++ b/client/core/defs.h @@ -68,7 +68,10 @@ enum ErrorCode OpenSslFailed, OpenVpnExecutableCrashed, ShadowSocksExecutableCrashed, - CloakExecutableCrashed + CloakExecutableCrashed, + + // import and install errors + ImportInvalidConfigError }; } // namespace amnezia diff --git a/client/core/errorstrings.cpp b/client/core/errorstrings.cpp index 17b40b09..dd298c76 100644 --- a/client/core/errorstrings.cpp +++ b/client/core/errorstrings.cpp @@ -57,6 +57,8 @@ QString errorString(ErrorCode code){ case (OpenVpnTapAdapterError): return QObject::tr("Can't setup OpenVPN TAP network adapter"); case (AddressPoolError): return QObject::tr("VPN pool error: no available addresses"); + case (ImportInvalidConfigError): return QObject::tr("The config does not contain any containers and credentiaks for connecting to the server"); + case(InternalError): default: return QObject::tr("Internal error"); diff --git a/client/images/controls/file-cog-2.svg b/client/images/controls/file-cog-2.svg new file mode 100644 index 00000000..20815319 --- /dev/null +++ b/client/images/controls/file-cog-2.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/client/resources.qrc b/client/resources.qrc index db53165c..185c4469 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -249,5 +249,10 @@ images/controls/server.svg ui/qml/Pages2/PageSettingsServerProtocols.qml ui/qml/Pages2/PageSettingsServerServices.qml + ui/qml/Pages2/PageSetupWizardViewConfig.qml + images/controls/file-cog-2.svg + ui/qml/Components/QuestionDrawer.qml + ui/qml/Pages2/PageDeinstalling.qml + ui/qml/Controls2/BackButtonType.qml diff --git a/client/ui/controllers/connectionController.cpp b/client/ui/controllers/connectionController.cpp index 617fd5ee..c4717012 100644 --- a/client/ui/controllers/connectionController.cpp +++ b/client/ui/controllers/connectionController.cpp @@ -9,21 +9,7 @@ ConnectionController::ConnectionController(const QSharedPointer &s } -bool ConnectionController::onConnectionButtonClicked() -{ - if (!isConnected()) { - openVpnConnection(); - } else { - closeVpnConnection(); - } -} - -bool ConnectionController::isConnected() -{ - return m_isConnected; -} - -bool ConnectionController::openVpnConnection() +void ConnectionController::openConnection() { int serverIndex = m_serversModel->getDefaultServerIndex(); QModelIndex serverModelIndex = m_serversModel->index(serverIndex); @@ -35,16 +21,6 @@ bool ConnectionController::openVpnConnection() const QJsonObject &containerConfig = qvariant_cast(m_containersModel->data(containerModelIndex, ContainersModel::Roles::ConfigRole)); - //todo error handling - qApp->processEvents(); - emit connectToVpn(serverIndex, credentials, container, containerConfig); - m_isConnected = true; - - -// int serverIndex = m_settings->defaultServerIndex(); -// ServerCredentials credentials = m_settings->serverCredentials(serverIndex); -// DockerContainer container = m_settings->defaultContainer(serverIndex); - // if (m_settings->containers(serverIndex).isEmpty()) { // set_labelErrorText(tr("VPN Protocols is not installed.\n Please install VPN container at first")); // set_pushButtonConnectChecked(false); @@ -57,20 +33,23 @@ bool ConnectionController::openVpnConnection() // return; // } - -// const QJsonObject &containerConfig = m_settings->containerConfig(serverIndex, container); - -// set_labelErrorText(""); -// set_pushButtonConnectChecked(true); -// set_pushButtonConnectEnabled(false); - -// qApp->processEvents(); - -// emit connectToVpn(serverIndex, credentials, container, containerConfig); + //todo error handling + qApp->processEvents(); + emit connectToVpn(serverIndex, credentials, container, containerConfig); } -bool ConnectionController::closeVpnConnection() +void ConnectionController::closeConnection() { emit disconnectFromVpn(); - m_isConnected = false; +} + +bool ConnectionController::isConnected() +{ + 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 8282a582..b1b0ff98 100644 --- a/client/ui/controllers/connectionController.h +++ b/client/ui/controllers/connectionController.h @@ -10,24 +10,26 @@ class ConnectionController : public QObject Q_OBJECT public: + Q_PROPERTY(bool isConnected READ isConnected WRITE setIsConnected NOTIFY isConnectedChanged) + explicit ConnectionController(const QSharedPointer &serversModel, const QSharedPointer &containersModel, QObject *parent = nullptr); -public slots: - bool onConnectionButtonClicked(); - bool isConnected(); + void setIsConnected(bool isConnected); + +public slots: + void openConnection(); + void closeConnection(); signals: void connectToVpn(int serverIndex, const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig); void disconnectFromVpn(); void connectionStateChanged(Vpn::ConnectionState state); + void isConnectedChanged(); private: - bool openVpnConnection(); - bool closeVpnConnection(); - QSharedPointer m_serversModel; QSharedPointer m_containersModel; diff --git a/client/ui/controllers/importController.cpp b/client/ui/controllers/importController.cpp index ea1e7ad6..48e811e0 100644 --- a/client/ui/controllers/importController.cpp +++ b/client/ui/controllers/importController.cpp @@ -1,6 +1,9 @@ #include "importController.h" #include +#include + +#include "core/errorstrings.h" namespace { enum class ConfigTypes { @@ -39,50 +42,68 @@ ImportController::ImportController(const QSharedPointer &serversMo } -bool ImportController::importFromFile(const QUrl &fileUrl) +void ImportController::extractConfigFromFile(const QUrl &fileUrl) { QFile file(fileUrl.toLocalFile()); if (file.open(QIODevice::ReadOnly)) { - QByteArray data = file.readAll(); + QString data = file.readAll(); auto configFormat = checkConfigFormat(data); if (configFormat == ConfigTypes::OpenVpn) { - return importOpenVpnConfig(data); + m_config = extractOpenVpnConfig(data); } else if (configFormat == ConfigTypes::WireGuard) { - return importWireGuardConfig(data); + m_config = extractWireGuardConfig(data); } else { - return importAmneziaConfig(data); + m_config = extractAmneziaConfig(data); } + + m_configFileName = QFileInfo(file.fileName()).fileName(); } - return false; } -bool ImportController::import(const QJsonObject &config) +void ImportController::extractConfigFromCode(QString code) +{ + m_config = extractAmneziaConfig(code); +} + +QString ImportController::getConfig() +{ + return QJsonDocument(m_config).toJson(QJsonDocument::Indented); +} + +QString ImportController::getConfigFileName() +{ + return m_configFileName; +} + +void ImportController::importConfig() { ServerCredentials credentials; - credentials.hostName = config.value(config_key::hostName).toString(); - credentials.port = config.value(config_key::port).toInt(); - credentials.userName = config.value(config_key::userName).toString(); - credentials.secretData = config.value(config_key::password).toString(); + credentials.hostName = m_config.value(config_key::hostName).toString(); + credentials.port = m_config.value(config_key::port).toInt(); + credentials.userName = m_config.value(config_key::userName).toString(); + credentials.secretData = m_config.value(config_key::password).toString(); - if (credentials.isValid() || config.contains(config_key::containers)) { - m_settings->addServer(config); + if (credentials.isValid() || m_config.contains(config_key::containers)) { + m_serversModel->addServer(m_config); - if (config.value(config_key::containers).toArray().isEmpty()) { - m_settings->setDefaultServer(m_settings->serversCount() - 1); + 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); } emit importFinished(); } else { qDebug() << "Failed to import profile"; - qDebug().noquote() << QJsonDocument(config).toJson(); - return false; + qDebug().noquote() << QJsonDocument(m_config).toJson(); + emit importErrorOccurred(errorString(ErrorCode::ImportInvalidConfigError)); } - return true; + m_config = {}; + m_configFileName.clear(); } -bool ImportController::importAmneziaConfig(QString data) +QJsonObject ImportController::extractAmneziaConfig(QString &data) { data.replace("vpn://", ""); QByteArray ba = QByteArray::fromBase64(data.toUtf8(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); @@ -92,13 +113,7 @@ bool ImportController::importAmneziaConfig(QString data) ba = ba_uncompressed; } - QJsonObject config; - config = QJsonDocument::fromJson(ba).object(); - if (!config.isEmpty()) { - return import(config); - } - - return false; + return QJsonDocument::fromJson(ba).object(); } //bool ImportController::importConnectionFromQr(const QByteArray &data) @@ -116,7 +131,7 @@ bool ImportController::importAmneziaConfig(QString data) // return false; //} -bool ImportController::importOpenVpnConfig(const QString &data) +QJsonObject ImportController::extractOpenVpnConfig(const QString &data) { QJsonObject openVpnConfig; openVpnConfig[config_key::config] = data; @@ -156,10 +171,10 @@ bool ImportController::importOpenVpnConfig(const QString &data) config[config_key::hostName] = hostName; - return import(config); + return config; } -bool ImportController::importWireGuardConfig(const QString &data) +QJsonObject ImportController::extractWireGuardConfig(const QString &data) { QJsonObject lastConfig; lastConfig[config_key::config] = data; @@ -200,5 +215,5 @@ bool ImportController::importWireGuardConfig(const QString &data) config[config_key::hostName] = hostName; - return import(config); + return config; } diff --git a/client/ui/controllers/importController.h b/client/ui/controllers/importController.h index 7dd548d8..114bb531 100644 --- a/client/ui/controllers/importController.h +++ b/client/ui/controllers/importController.h @@ -18,20 +18,27 @@ public: QObject *parent = nullptr); public slots: - bool importFromFile(const QUrl &fileUrl); + void importConfig(); + void extractConfigFromFile(const QUrl &fileUrl); + void extractConfigFromCode(QString code); + QString getConfig(); + QString getConfigFileName(); signals: void importFinished(); + void importErrorOccurred(QString errorMessage); private: - bool import(const QJsonObject &config); - bool importAmneziaConfig(QString data); - bool importOpenVpnConfig(const QString &data); - bool importWireGuardConfig(const QString &data); + QJsonObject extractAmneziaConfig(QString &data); + QJsonObject extractOpenVpnConfig(const QString &data); + QJsonObject extractWireGuardConfig(const QString &data); QSharedPointer m_serversModel; QSharedPointer m_containersModel; std::shared_ptr m_settings; + QJsonObject m_config; + QString m_configFileName; + }; #endif // IMPORTCONTROLLER_H diff --git a/client/ui/controllers/pageController.h b/client/ui/controllers/pageController.h index 1d041deb..22ee0bb3 100644 --- a/client/ui/controllers/pageController.h +++ b/client/ui/controllers/pageController.h @@ -9,14 +9,14 @@ namespace PageLoader { Q_NAMESPACE - enum class PageEnum { PageStart = 0, PageHome, PageShare, + enum class PageEnum { PageStart = 0, PageHome, PageShare, PageDeinstalling, PageSettingsServersList, PageSettings, PageSettingsServerData, PageSettingsServerInfo, PageSettingsServerProtocols, PageSettingsServerServices, PageSetupWizardStart, PageTest, PageSetupWizardCredentials, PageSetupWizardProtocols, PageSetupWizardEasy, PageSetupWizardProtocolSettings, PageSetupWizardInstalling, PageSetupWizardConfigSource, - PageSetupWizardTextKey + PageSetupWizardTextKey, PageSetupWizardViewConfig }; Q_ENUM_NS(PageEnum) diff --git a/client/ui/qml/Components/ConnectButton.qml b/client/ui/qml/Components/ConnectButton.qml index 73accef4..06c148f1 100644 --- a/client/ui/qml/Components/ConnectButton.qml +++ b/client/ui/qml/Components/ConnectButton.qml @@ -13,7 +13,7 @@ Button { id: border source: connectionProccess.running ? "/images/connectionProgress.svg" : - ConnectionController.isConnected() ? "/images/connectionOff.svg" : "/images/connectionOn.svg" + ConnectionController.isConnected ? "/images/connectionOff.svg" : "/images/connectionOn.svg" RotationAnimator { id: connectionProccess @@ -46,7 +46,7 @@ Button { } onClicked: { - ConnectionController.onConnectionButtonClicked() + ConnectionController.isConnected ? ConnectionController.closeConnection() : ConnectionController.openConnection() } Connections { @@ -61,6 +61,7 @@ Button { console.log("Disconnected") connectionProccess.running = false root.text = qsTr("Connect") + ConnectionController.isConnected = false break } case ConnectionState.Preparing: { @@ -78,7 +79,8 @@ Button { case ConnectionState.Connected: { console.log("Connected") connectionProccess.running = false - root.text = qsTr("Connected") + root.text = qsTr("Disconnect") + ConnectionController.isConnected = true break } case ConnectionState.Disconnecting: { diff --git a/client/ui/qml/Components/ConnectionTypeSelectionDrawer.qml b/client/ui/qml/Components/ConnectionTypeSelectionDrawer.qml index d80d5e5a..3c1ecd5b 100644 --- a/client/ui/qml/Components/ConnectionTypeSelectionDrawer.qml +++ b/client/ui/qml/Components/ConnectionTypeSelectionDrawer.qml @@ -14,20 +14,6 @@ DrawerType { width: parent.width height: parent.height * 0.4375 - background: Rectangle { - anchors.fill: parent - anchors.bottomMargin: -radius - radius: 16 - color: "#1C1D21" - - border.color: "#2C2D30" - border.width: 1 - } - - Overlay.modal: Rectangle { - color: Qt.rgba(14/255, 14/255, 17/255, 0.8) - } - ColumnLayout { anchors.top: parent.top anchors.left: parent.left diff --git a/client/ui/qml/Components/HomeContainersListView.qml b/client/ui/qml/Components/HomeContainersListView.qml index c3e98fbb..0d42b8e9 100644 --- a/client/ui/qml/Components/HomeContainersListView.qml +++ b/client/ui/qml/Components/HomeContainersListView.qml @@ -27,44 +27,54 @@ ListView { delegate: Item { implicitWidth: rootWidth - implicitHeight: containerRadioButton.implicitHeight + implicitHeight: content.implicitHeight - VerticalRadioButton { - id: containerRadioButton + ColumnLayout { + id: content anchors.fill: parent anchors.rightMargin: 16 anchors.leftMargin: 16 - text: name - descriptionText: description + VerticalRadioButton { + id: containerRadioButton - ButtonGroup.group: containersRadioButtonGroup + Layout.fillWidth: true - imageSource: "qrc:/images/controls/download.svg" - showImage: !isInstalled + text: name + descriptionText: description - checkable: isInstalled - checked: isDefault + ButtonGroup.group: containersRadioButtonGroup - onClicked: { - if (checked) { - isDefault = true - menuContent.currentIndex = index - containersDropDown.menuVisible = false - } else { - ContainersModel.setCurrentlyInstalledContainerIndex(proxyContainersModel.mapToSource(index)) - InstallController.setShouldCreateServer(false) - goToPage(PageEnum.PageSetupWizardProtocolSettings) - containersDropDown.menuVisible = false - menu.visible = false + imageSource: "qrc:/images/controls/download.svg" + showImage: !isInstalled + + checkable: isInstalled + checked: isDefault + + onClicked: { + if (checked) { + isDefault = true + menuContent.currentIndex = index + containersDropDown.menuVisible = false + } else { + ContainersModel.setCurrentlyInstalledContainerIndex(proxyContainersModel.mapToSource(index)) + InstallController.setShouldCreateServer(false) + goToPage(PageEnum.PageSetupWizardProtocolSettings) + containersDropDown.menuVisible = false + menu.visible = false + } + } + + MouseArea { + anchors.fill: containerRadioButton + cursorShape: Qt.PointingHandCursor + enabled: false } } - MouseArea { - anchors.fill: containerRadioButton - cursorShape: Qt.PointingHandCursor - enabled: false + DividerType { + Layout.fillWidth: true } } diff --git a/client/ui/qml/Components/QuestionDrawer.qml b/client/ui/qml/Components/QuestionDrawer.qml new file mode 100644 index 00000000..a79f9140 --- /dev/null +++ b/client/ui/qml/Components/QuestionDrawer.qml @@ -0,0 +1,77 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import "../Controls2" +import "../Controls2/TextTypes" + +DrawerType { + id: root + + property string headerText + property string descriptionText + property string yesButtonText + property string noButtonText + + property var yesButtonFunction + property var noButtonFunction + + width: parent.width + height: parent.height * 0.5 + + ColumnLayout { + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.topMargin: 16 + anchors.rightMargin: 16 + anchors.leftMargin: 16 + + spacing: 8 + + Header2TextType { + Layout.fillWidth: true + + text: headerText + } + + ParagraphTextType { + Layout.fillWidth: true + Layout.topMargin: 8 + + text: descriptionText + } + + BasicButtonType { + Layout.fillWidth: true + Layout.topMargin: 16 + + text: yesButtonText + + onClicked: { + if (yesButtonFunction && typeof yesButtonFunction === "function") { + yesButtonFunction() + } + } + } + + BasicButtonType { + Layout.fillWidth: true + + defaultColor: "transparent" + hoveredColor: Qt.rgba(1, 1, 1, 0.08) + pressedColor: Qt.rgba(1, 1, 1, 0.12) + disabledColor: "#878B91" + textColor: "#D7D8DB" + borderWidth: 1 + + text: noButtonText + + onClicked: { + if (noButtonFunction && typeof noButtonFunction === "function") { + noButtonFunction() + } + } + } + } +} diff --git a/client/ui/qml/Controls2/BackButtonType.qml b/client/ui/qml/Controls2/BackButtonType.qml new file mode 100644 index 00000000..bec31823 --- /dev/null +++ b/client/ui/qml/Controls2/BackButtonType.qml @@ -0,0 +1,55 @@ +import QtQuick +import QtQuick.Layouts +import Qt5Compat.GraphicalEffects + +Item { + id: root + + property string backButtonImage: "qrc:/images/controls/arrow-left.svg" + property var backButtonFunction + + implicitWidth: content.implicitWidth + implicitHeight: content.implicitHeight + + RowLayout { + id: content + + anchors.fill: parent + + ImageButtonType { + image: backButtonImage + imageColor: "#D7D8DB" + + onClicked: { + if (backButtonFunction && typeof backButtonFunction === "function") { + backButtonFunction() + } else { + closePage() + } + } + } + + Rectangle { + id: background + Layout.fillWidth: true + + color: "transparent" + + ShaderEffectSource { + id: effectSource + + sourceItem: background + anchors.fill: background + sourceRect: Qt.rect(x,y, width, height) + } + + FastBlur { + id: blur + anchors.fill: effectSource + + source: effectSource + radius: 100 + } + } + } +} diff --git a/client/ui/qml/Controls2/DrawerType.qml b/client/ui/qml/Controls2/DrawerType.qml index bede3700..e3c9d588 100644 --- a/client/ui/qml/Controls2/DrawerType.qml +++ b/client/ui/qml/Controls2/DrawerType.qml @@ -17,4 +17,18 @@ Drawer { velocity: 4 } } + + background: Rectangle { + anchors.fill: parent + anchors.bottomMargin: -radius + radius: 16 + color: "#1C1D21" + + border.color: "#2C2D30" + border.width: 1 + } + + Overlay.modal: Rectangle { + color: Qt.rgba(14/255, 14/255, 17/255, 0.8) + } } diff --git a/client/ui/qml/Controls2/DropDownType.qml b/client/ui/qml/Controls2/DropDownType.qml index 5ac50fa7..700d9981 100644 --- a/client/ui/qml/Controls2/DropDownType.qml +++ b/client/ui/qml/Controls2/DropDownType.qml @@ -117,28 +117,9 @@ Item { width: parent.width height: parent.height * 0.9 - background: Rectangle { - anchors.fill: parent - anchors.bottomMargin: -radius - radius: 16 - color: "#1C1D21" - - border.color: "#494B50" - border.width: 1 - } - - Overlay.modal: Rectangle { - color: Qt.rgba(14/255, 14/255, 17/255, 0.8) - } - - Header2Type { + ColumnLayout { id: header - headerText: root.headerText - backButtonImage: root.headerBackButtonImage - - width: parent.width - anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right @@ -146,8 +127,11 @@ Item { anchors.leftMargin: 16 anchors.rightMargin: 16 - backButtonFunction: function() { - root.menuVisible = false + BackButtonType { + backButtonImage: root.headerBackButtonImage + backButtonFunction: function() { + root.menuVisible = false + } } } @@ -164,6 +148,17 @@ Item { spacing: 16 + Header2Type { + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: 16 + anchors.rightMargin: 16 + + headerText: root.headerText + + width: parent.width + } + Loader { id: listViewLoader sourceComponent: root.listView diff --git a/client/ui/qml/Controls2/Header2Type.qml b/client/ui/qml/Controls2/Header2Type.qml index ce9d804a..9433f52a 100644 --- a/client/ui/qml/Controls2/Header2Type.qml +++ b/client/ui/qml/Controls2/Header2Type.qml @@ -6,10 +6,7 @@ import "TextTypes" Item { id: root - property string backButtonImage property string actionButtonImage - - property var backButtonFunction property var actionButtonFunction property string headerText @@ -22,25 +19,6 @@ Item { id: content anchors.fill: parent - ImageButtonType { - id: backButton - - Layout.leftMargin: -6 - - image: root.backButtonImage - imageColor: "#D7D8DB" - - visible: image ? true : false - - onClicked: { - if (backButtonFunction && typeof backButtonFunction === "function") { - backButtonFunction() - } else { - closePage() - } - } - } - RowLayout { Header2TextType { id: header diff --git a/client/ui/qml/Controls2/HeaderType.qml b/client/ui/qml/Controls2/HeaderType.qml index 2c0fbd85..19958205 100644 --- a/client/ui/qml/Controls2/HeaderType.qml +++ b/client/ui/qml/Controls2/HeaderType.qml @@ -6,10 +6,7 @@ import "TextTypes" Item { id: root - property string backButtonImage property string actionButtonImage - - property var backButtonFunction property var actionButtonFunction property string headerText @@ -22,25 +19,6 @@ Item { id: content anchors.fill: parent - ImageButtonType { - id: backButton - - Layout.leftMargin: -6 - - image: root.backButtonImage - imageColor: "#D7D8DB" - - visible: image ? true : false - - onClicked: { - if (backButtonFunction && typeof backButtonFunction === "function") { - backButtonFunction() - } else { - closePage() - } - } - } - RowLayout { Header1TextType { id: header diff --git a/client/ui/qml/Controls2/ProgressBarType.qml b/client/ui/qml/Controls2/ProgressBarType.qml index f2f2370a..183c3736 100644 --- a/client/ui/qml/Controls2/ProgressBarType.qml +++ b/client/ui/qml/Controls2/ProgressBarType.qml @@ -11,9 +11,11 @@ ProgressBar { color: "#412102" } - contentItem: Rectangle { - width: root.visualPosition * root.width - height: root.height - color: "#FBB26A" + contentItem: Item { + Rectangle { + width: root.visualPosition * parent.width + height: parent.height + color: "#FBB26A" + } } } diff --git a/client/ui/qml/Controls2/SwitcherType.qml b/client/ui/qml/Controls2/SwitcherType.qml index e4df04fa..b593ece8 100644 --- a/client/ui/qml/Controls2/SwitcherType.qml +++ b/client/ui/qml/Controls2/SwitcherType.qml @@ -59,7 +59,6 @@ Switch { } } - contentItem: ColumnLayout { contentItem: ColumnLayout { id: content diff --git a/client/ui/qml/Controls2/TextFieldWithHeaderType.qml b/client/ui/qml/Controls2/TextFieldWithHeaderType.qml index 3458f741..b41f0b40 100644 --- a/client/ui/qml/Controls2/TextFieldWithHeaderType.qml +++ b/client/ui/qml/Controls2/TextFieldWithHeaderType.qml @@ -6,7 +6,6 @@ Item { id: root property string headerText - property string textFieldText property string textFieldPlaceholderText property bool textFieldEditable: true @@ -14,6 +13,7 @@ Item { property var clickedFunc property alias textField: textField + property alias textFieldText: textField.text implicitHeight: 74 @@ -53,7 +53,6 @@ Item { id: textField enabled: root.textFieldEditable - text: root.textFieldText color: "#d7d8db" placeholderText: textFieldPlaceholderText diff --git a/client/ui/qml/Pages2/PageDeinstalling.qml b/client/ui/qml/Pages2/PageDeinstalling.qml new file mode 100644 index 00000000..1dc542e0 --- /dev/null +++ b/client/ui/qml/Pages2/PageDeinstalling.qml @@ -0,0 +1,91 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import SortFilterProxyModel 0.2 + +import PageEnum 1.0 + +import "./" +import "../Controls2" +import "../Controls2/TextTypes" +import "../Config" + +PageType { + id: root + + SortFilterProxyModel { + id: proxyServersModel + sourceModel: ServersModel + filters: [ + ValueFilter { + roleName: "isCurrentlyProcessed" + value: true + } + ] + } + + FlickableType { + id: fl + anchors.fill: parent + contentHeight: content.height + + Column { + id: content + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + spacing: 16 + + Repeater { + model: proxyServersModel + delegate: Item { + implicitWidth: parent.width + implicitHeight: delegateContent.implicitHeight + + ColumnLayout { + id: delegateContent + + anchors.fill: parent + anchors.rightMargin: 16 + anchors.leftMargin: 16 + + HeaderType { + Layout.fillWidth: true + Layout.topMargin: 20 + + headerText: qsTr("Removing services from ") + name + } + + ProgressBarType { + id: progressBar + + Layout.fillWidth: true + Layout.topMargin: 32 + + Timer { + id: timer + + interval: 300 + repeat: true + running: true + onTriggered: { + progressBar.value += 0.001 + } + } + } + + ParagraphTextType { + Layout.fillWidth: true + Layout.topMargin: 8 + + text: "Обычно это занимает не больше 5 минут" + } + } + } + } + } + } +} diff --git a/client/ui/qml/Pages2/PageHome.qml b/client/ui/qml/Pages2/PageHome.qml index 0ecbafcc..4f3c4b6a 100644 --- a/client/ui/qml/Pages2/PageHome.qml +++ b/client/ui/qml/Pages2/PageHome.qml @@ -113,20 +113,6 @@ PageType { width: parent.width height: parent.height * 0.90 - background: Rectangle { - anchors.fill: parent - anchors.bottomMargin: -radius - radius: 16 - - color: "#1C1D21" - border.color: root.borderColor - border.width: 1 - } - - Overlay.modal: Rectangle { - color: Qt.rgba(14/255, 14/255, 17/255, 0.8) - } - ColumnLayout { id: serversMenuHeader anchors.top: parent.top diff --git a/client/ui/qml/Pages2/PageSettings.qml b/client/ui/qml/Pages2/PageSettings.qml index d3b87c19..300421b7 100644 --- a/client/ui/qml/Pages2/PageSettings.qml +++ b/client/ui/qml/Pages2/PageSettings.qml @@ -71,7 +71,6 @@ PageType { iconImage: "qrc:/images/controls/app.svg" clickedFunction: function() { - goToPage(PageEnum.PageSetupWizardTextKey) } } @@ -85,7 +84,6 @@ PageType { iconImage: "qrc:/images/controls/save.svg" clickedFunction: function() { - goToPage(PageEnum.PageSetupWizardTextKey) } } @@ -99,7 +97,6 @@ PageType { iconImage: "qrc:/images/controls/amnezia.svg" clickedFunction: function() { - goToPage(PageEnum.PageSetupWizardTextKey) } } diff --git a/client/ui/qml/Pages2/PageSettingsServerData.qml b/client/ui/qml/Pages2/PageSettingsServerData.qml index 3ab76299..c1f84cb0 100644 --- a/client/ui/qml/Pages2/PageSettingsServerData.qml +++ b/client/ui/qml/Pages2/PageSettingsServerData.qml @@ -9,6 +9,7 @@ import ProtocolEnum 1.0 import "../Controls2" import "../Controls2/TextTypes" +import "../Components" PageType { id: root @@ -33,7 +34,19 @@ PageType { descriptionText: "May be needed when changing other settings" clickedFunction: function() { - ContainersModel.clearCachedProfiles() + questionDrawer.headerText = qsTr("Clear cached profiles?") + questionDrawer.descriptionText = qsTr("some description") + questionDrawer.yesButtonText = qsTr("Continue") + questionDrawer.noButtonText = qsTr("Cancel") + + questionDrawer.yesButtonFunction = function() { + questionDrawer.visible = false + ContainersModel.clearCachedProfiles() + } + questionDrawer.noButtonFunction = function() { + questionDrawer.visible = false + } + questionDrawer.visible = true } } @@ -46,15 +59,27 @@ PageType { textColor: "#EB5757" clickedFunction: function() { - if (ServersModel.isDefaultServerCurrentlyProcessed && ConnectionController.isConnected()) { - ConnectionController.closeVpnConnection() + questionDrawer.headerText = qsTr("Remove server?") + questionDrawer.descriptionText = qsTr("All installed AmneziaVPN services will still remain on the server.") + questionDrawer.yesButtonText = qsTr("Continue") + questionDrawer.noButtonText = qsTr("Cancel") + + questionDrawer.yesButtonFunction = function() { + questionDrawer.visible = false + if (ServersModel.isDefaultServerCurrentlyProcessed && ConnectionController.isConnected) { + ConnectionController.closeConnection() + } + ServersModel.removeServer() + if (!ServersModel.getServersCount()) { + PageController.replaceStartPage() + } else { + goToStartPage() + } } - ServersModel.removeServer() - if (!ServersModel.getServersCount()) { - PageController.replaceStartPage() - } else { - goToStartPage() + questionDrawer.noButtonFunction = function() { + questionDrawer.visible = false } + questionDrawer.visible = true } } @@ -67,14 +92,32 @@ PageType { textColor: "#EB5757" clickedFunction: function() { - if (ServersModel.isDefaultServerCurrentlyProcessed && ConnectionController.isConnected()) { - ConnectionController.closeVpnConnection() + questionDrawer.headerText = qsTr("Clear server from Amnezia software?") + questionDrawer.descriptionText = qsTr(" All containers will be deleted on the server. This means that configuration files, keys and certificates will be deleted.") + questionDrawer.yesButtonText = qsTr("Continue") + questionDrawer.noButtonText = qsTr("Cancel") + + questionDrawer.yesButtonFunction = function() { + questionDrawer.visible = false + goToPage(PageEnum.PageDeinstalling) + if (ServersModel.isDefaultServerCurrentlyProcessed && ConnectionController.isConnected) { + ConnectionController.closeVpnConnection() + } + ContainersModel.removeAllContainers() + closePage() } - ContainersModel.removeAllContainers() + questionDrawer.noButtonFunction = function() { + questionDrawer.visible = false + } + questionDrawer.visible = true } } DividerType {} + + QuestionDrawer { + id: questionDrawer + } } } } diff --git a/client/ui/qml/Pages2/PageSettingsServerInfo.qml b/client/ui/qml/Pages2/PageSettingsServerInfo.qml index 29a0c3c1..5e422230 100644 --- a/client/ui/qml/Pages2/PageSettingsServerInfo.qml +++ b/client/ui/qml/Pages2/PageSettingsServerInfo.qml @@ -30,8 +30,6 @@ PageType { } ColumnLayout { - id: content - anchors.fill: parent spacing: 16 @@ -40,24 +38,27 @@ PageType { id: header model: proxyServersModel - delegate: HeaderType { - Layout.fillWidth: true + delegate: ColumnLayout { + id: content + Layout.topMargin: 20 Layout.leftMargin: 16 Layout.rightMargin: 16 - actionButtonImage: "qrc:/images/controls/edit-3.svg" - backButtonImage: "qrc:/images/controls/arrow-left.svg" - - headerText: name - descriptionText: hostName - - actionButtonFunction: function() { - connectionTypeSelection.visible = true + BackButtonType { } - backButtonFunction: function() { - closePage() + HeaderType { + Layout.fillWidth: true + + actionButtonImage: "qrc:/images/controls/edit-3.svg" + + headerText: name + descriptionText: hostName + + actionButtonFunction: function() { + connectionTypeSelection.visible = true + } } } } @@ -74,23 +75,14 @@ PageType { TabButtonType { isSelected: tabBar.currentIndex === 0 text: qsTr("Protocols") -// onClicked: { -// tabBarStackView.goToTabBarPage(PageEnum.PageSettingsServerProtocols) -// } } TabButtonType { isSelected: tabBar.currentIndex === 1 text: qsTr("Services") -// onClicked: { -// tabBarStackView.goToTabBarPage(PageEnum.PageSettingsServerServices) -// } } TabButtonType { isSelected: tabBar.currentIndex === 2 text: qsTr("Data") -// onClicked: { -// tabBarStackView.goToTabBarPage(PageEnum.PageSettingsServerData) -// } } } @@ -110,25 +102,5 @@ PageType { stackView: root.stackView } } - -// StackViewType { -// id: tabBarStackView - -// Layout.preferredWidth: root.width -// Layout.preferredHeight: root.height - tabBar.implicitHeight - header.implicitHeight - -// function goToTabBarPage(page) { -// var pagePath = PageController.getPagePath(page) -// while (tabBarStackView.depth > 1) { -// tabBarStackView.pop() -// } -// tabBarStackView.replace(pagePath, { "objectName" : pagePath }) -// } - -// Component.onCompleted: { -// var pagePath = PageController.getPagePath(PageEnum.PageSettingsServerProtocols) -// tabBarStackView.push(pagePath, { "objectName" : pagePath }) -// } -// } } } diff --git a/client/ui/qml/Pages2/PageSettingsServersList.qml b/client/ui/qml/Pages2/PageSettingsServersList.qml index 63a40cc0..9c5ddc74 100644 --- a/client/ui/qml/Pages2/PageSettingsServersList.qml +++ b/client/ui/qml/Pages2/PageSettingsServersList.qml @@ -17,7 +17,7 @@ import "../Components" PageType { id: root - HeaderType { + ColumnLayout { id: header anchors.top: parent.top @@ -28,13 +28,19 @@ PageType { anchors.leftMargin: 16 anchors.rightMargin: 16 - actionButtonImage: "qrc:/images/controls/plus.svg" - backButtonImage: "qrc:/images/controls/arrow-left.svg" + BackButtonType { + } - headerText: "Серверы" + HeaderType { + Layout.fillWidth: true - actionButtonFunction: function() { - connectionTypeSelection.visible = true + actionButtonImage: "qrc:/images/controls/plus.svg" + + headerText: "Серверы" + + actionButtonFunction: function() { + connectionTypeSelection.visible = true + } } } diff --git a/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml b/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml index 25b88f76..79e596af 100644 --- a/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml +++ b/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml @@ -13,14 +13,6 @@ import "../Config" PageType { id: root - Connections { - target: ImportController - - function onImportFinished() { - - } - } - FlickableType { id: fl anchors.top: root.top @@ -34,13 +26,18 @@ PageType { anchors.left: parent.left anchors.right: parent.right + BackButtonType { + Layout.topMargin: 20 + Layout.rightMargin: 16 + Layout.leftMargin: 16 + } + HeaderType { Layout.fillWidth: true Layout.topMargin: 20 Layout.rightMargin: 16 Layout.leftMargin: 16 - backButtonImage: "qrc:/images/controls/arrow-left.svg" headerText: "Подключение к серверу" descriptionText: "Не используйте код подключения из публичных источников. Его могли создать, чтобы перехватывать ваши данные.\n @@ -71,7 +68,8 @@ PageType { FileDialog { id: fileDialog onAccepted: { - ImportController.importFromFile(selectedFile) + ImportController.extractConfigFromFile(selectedFile) + goToPage(PageEnum.PageSetupWizardViewConfig) } } } diff --git a/client/ui/qml/Pages2/PageSetupWizardCredentials.qml b/client/ui/qml/Pages2/PageSetupWizardCredentials.qml index 95463c3e..49197962 100644 --- a/client/ui/qml/Pages2/PageSetupWizardCredentials.qml +++ b/client/ui/qml/Pages2/PageSetupWizardCredentials.qml @@ -11,9 +11,20 @@ import "../Config" PageType { id: root + BackButtonType { + id: backButton + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: 16 + anchors.leftMargin: 16 + anchors.topMargin: 20 + } + FlickableType { id: fl - anchors.top: root.top + anchors.top: backButton.bottom anchors.bottom: root.bottom contentHeight: content.height @@ -30,9 +41,6 @@ PageType { HeaderType { Layout.fillWidth: true - Layout.topMargin: 20 - - backButtonImage: "qrc:/images/controls/arrow-left.svg" headerText: "Подключение к серверу" } diff --git a/client/ui/qml/Pages2/PageSetupWizardEasy.qml b/client/ui/qml/Pages2/PageSetupWizardEasy.qml index 8707c19b..99f6ccfb 100644 --- a/client/ui/qml/Pages2/PageSetupWizardEasy.qml +++ b/client/ui/qml/Pages2/PageSetupWizardEasy.qml @@ -30,11 +30,22 @@ PageType { } } + BackButtonType { + id: backButton + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: 16 + anchors.leftMargin: 16 + anchors.topMargin: 20 + } + FlickableType { id: fl - anchors.top: root.top + anchors.top: backButton.bottom anchors.bottom: root.bottom - contentHeight: content.implicitHeight + buttonContinue.anchors.bottomMargin + contentHeight: content.implicitHeight + continueButton.anchors.bottomMargin Column { id: content @@ -44,7 +55,6 @@ PageType { anchors.right: parent.right anchors.rightMargin: 16 anchors.leftMargin: 16 - anchors.topMargin: 20 spacing: 16 @@ -53,9 +63,7 @@ PageType { implicitWidth: parent.width - backButtonImage: "qrc:/images/controls/arrow-left.svg" - - headerText: qsTr("What is the level of Internet control in your region?") + headerText: qsTr("What is the level of internet control in your region?") } ListView { @@ -118,11 +126,10 @@ PageType { } BasicButtonType { - id: buttonContinue + id: continueButton implicitWidth: parent.width - anchors.topMargin: 24 - anchors.bottomMargin: 32 + anchors.bottomMargin: 24 text: qsTr("Continue") diff --git a/client/ui/qml/Pages2/PageSetupWizardInstalling.qml b/client/ui/qml/Pages2/PageSetupWizardInstalling.qml index cf7b1d28..c18c4d27 100644 --- a/client/ui/qml/Pages2/PageSetupWizardInstalling.qml +++ b/client/ui/qml/Pages2/PageSetupWizardInstalling.qml @@ -109,7 +109,7 @@ PageType { Layout.fillWidth: true Layout.topMargin: 32 - value: progressBarValue +// value: progressBarValue Timer { id: timer @@ -118,7 +118,7 @@ PageType { repeat: true running: true onTriggered: { - progressBarValue += 0.001 + progressBar.value += 0.001 } } } diff --git a/client/ui/qml/Pages2/PageSetupWizardProtocolSettings.qml b/client/ui/qml/Pages2/PageSetupWizardProtocolSettings.qml index fa3ac838..39761cee 100644 --- a/client/ui/qml/Pages2/PageSetupWizardProtocolSettings.qml +++ b/client/ui/qml/Pages2/PageSetupWizardProtocolSettings.qml @@ -62,13 +62,14 @@ PageType { anchors.rightMargin: 16 anchors.leftMargin: 16 + BackButtonType { + Layout.topMargin: 20 + } + HeaderType { id: header Layout.fillWidth: true - Layout.topMargin: 20 - - backButtonImage: "qrc:/images/controls/arrow-left.svg" headerText: "Установка " + name descriptionText: "Эти настройки можно будет изменить позже" diff --git a/client/ui/qml/Pages2/PageSetupWizardProtocols.qml b/client/ui/qml/Pages2/PageSetupWizardProtocols.qml index cdacaf04..75b16afd 100644 --- a/client/ui/qml/Pages2/PageSetupWizardProtocols.qml +++ b/client/ui/qml/Pages2/PageSetupWizardProtocols.qml @@ -48,9 +48,12 @@ PageType { spacing: 16 + BackButtonType { + width: parent.width + } + HeaderType { width: parent.width - backButtonImage: "qrc:/images/controls/arrow-left.svg" headerText: "Протокол подключения" descriptionText: "Выберите более приоритетный для вас. Позже можно будет установить остальные протоколы и доп сервисы, вроде DNS-прокси и SFTP." diff --git a/client/ui/qml/Pages2/PageSetupWizardTextKey.qml b/client/ui/qml/Pages2/PageSetupWizardTextKey.qml index 43dbda0e..f74c3733 100644 --- a/client/ui/qml/Pages2/PageSetupWizardTextKey.qml +++ b/client/ui/qml/Pages2/PageSetupWizardTextKey.qml @@ -29,23 +29,26 @@ PageType { spacing: 16 + BackButtonType { + Layout.topMargin: 20 + } + HeaderType { Layout.fillWidth: true - Layout.topMargin: 20 - backButtonImage: "qrc:/images/controls/arrow-left.svg" - - headerText: "Ключ для подключения" - descriptionText: "Строка, которая начинается с vpn://..." + headerText: qsTr("Connection key") + descriptionText: qsTr("A line that starts with vpn://...") } TextFieldWithHeaderType { + id: textKey + Layout.fillWidth: true Layout.topMargin: 32 - headerText: "Ключ" + headerText: qsTr("Key") textFieldPlaceholderText: "vpn://" - buttonText: "Вставить" + buttonText: qsTr("Insert") clickedFunc: function() { textField.text = "" @@ -63,10 +66,11 @@ PageType { anchors.leftMargin: 16 anchors.bottomMargin: 32 - text: qsTr("Подключиться") + text: qsTr("Continue") onClicked: function() { -// goToPage(PageEnum.PageSetupWizardInstalling) + ImportController.extractConfigFromCode(textKey.textFieldText) + goToPage(PageEnum.PageSetupWizardViewConfig) } } } diff --git a/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml b/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml new file mode 100644 index 00000000..ca679e79 --- /dev/null +++ b/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml @@ -0,0 +1,152 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Dialogs + +import PageEnum 1.0 + +import "./" +import "../Controls2" +import "../Controls2/TextTypes" +import "../Config" + +PageType { + id: root + + property bool showContent: false + + Connections { + target: ImportController + + function onImportErrorOccurred(errorMessage) { + closePage() + PageController.showErrorMessage(errorMessage) + } + + function onImportFinished() { + goToStartPage() + if (stackView.currentItem.objectName === PageController.getPagePath(PageEnum.PageHome)) { + PageController.restorePageHomeState() + } else if (stackView.currentItem.objectName === PageController.getPagePath(PageEnum.PageSettings)) { + goToPage(PageEnum.PageSettingsServersList, false) + } else { + var pagePath = PageController.getPagePath(PageEnum.PageStart) + stackView.replace(pagePath, { "objectName" : pagePath }) + } + } + } + + BackButtonType { + id: backButton + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: 16 + anchors.leftMargin: 16 + anchors.topMargin: 20 + } + + FlickableType { + id: fl + anchors.top: backButton.bottom + anchors.bottom: root.bottom + contentHeight: content.implicitHeight + connectButton.implicitHeight + + ColumnLayout { + id: content + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: 16 + anchors.leftMargin: 16 + + HeaderType { + headerText: qsTr("New connection") + } + + RowLayout { + Layout.topMargin: 32 + spacing: 8 + + visible: fileName.text !== "" + + Image { + source: "qrc:/images/controls/file-cog-2.svg" + } + + Header2TextType { + id: fileName + + Layout.fillWidth: true + + text: ImportController.getConfigFileName() + } + } + + CaptionTextType { + Layout.fillWidth: true + Layout.topMargin: 16 + + text: qsTr("Do not use connection code from public sources. It could be created to intercept your data.") + color: "#878B91" + } + + BasicButtonType { + defaultColor: "transparent" + hoveredColor: Qt.rgba(1, 1, 1, 0.08) + pressedColor: Qt.rgba(1, 1, 1, 0.12) + disabledColor: "#878B91" + textColor: "#D7D8DB" + + text: showContent ? qsTr("Collapse content") : qsTr("Show content") + + onClicked: { + showContent = !showContent + } + } + + Rectangle { + Layout.fillWidth: true + Layout.bottomMargin: 16 + + implicitHeight: configContent.implicitHeight + + radius: 10 + color: "#1C1D21" + + visible: showContent + + ParagraphTextType { + id: configContent + + anchors.fill: parent + anchors.margins: 16 + + text: ImportController.getConfig() + } + } + } + } + + ColumnLayout { + id: connectButton + + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: 16 + anchors.leftMargin: 16 + + BasicButtonType { + Layout.fillWidth: true + Layout.bottomMargin: 32 + + text: qsTr("Connect") + onClicked: { + ImportController.importConfig() + } + } + } +} diff --git a/client/ui/qml/Pages2/PageStart.qml b/client/ui/qml/Pages2/PageStart.qml index 44577037..436194b2 100644 --- a/client/ui/qml/Pages2/PageStart.qml +++ b/client/ui/qml/Pages2/PageStart.qml @@ -46,6 +46,8 @@ PageType { Component.onCompleted: { var pagePath = PageController.getPagePath(PageEnum.PageHome) tabBarStackView.push(pagePath, { "objectName" : pagePath }) + ServersModel.setCurrentlyProcessedServerIndex(ServersModel.getDefaultServerIndex()) + ContainersModel.setCurrentlyProcessedServerIndex(ServersModel.getDefaultServerIndex()) } } diff --git a/client/ui/qml/main2.qml b/client/ui/qml/main2.qml index bda1c709..9bb6aa28 100644 --- a/client/ui/qml/main2.qml +++ b/client/ui/qml/main2.qml @@ -36,7 +36,7 @@ Window { focus: true Component.onCompleted: { - var pagePath = PageController.getPagePath(PageEnum.PageStart) + var pagePath = PageController.getInitialPage() rootStackView.push(pagePath, { "objectName" : pagePath }) } }